Common processing flows in JavaIO

Earlier, we looked at the use of JavaIO stream basics, where the four most basic base classes for IO streams are InputStream, OutputStream, Reader, and Writer. The most common subclasses of file manipulation are FileInputStream, FileOutputStream, FileReader, and FileWriter. Their usage is basically the same, except that the first two are byte manipulation and the last two are character manipulation.

The difference between a byte stream and a character stream

1. First of all, the operation unit is different. Byte stream operations operate on 8-bit bytes and character stream operations operate on 16-bit bytes.

2. Secondly, actually byte stream does not use buffer (memory) during operation, it is directly operated by the file itself, while character stream uses buffer during operation, and then operates file through buffer.

The buffer

A buffer can simply be thought of as a special piece of memory. Its main functions are:

1, in some cases, if a program frequently operate a resource (e.g., file or database), the performance will be very low, at this time in order to improve performance, it can be part of the data into the temporary memory of an area, after can be directly read data from the area, because read memory speed will be faster, so that we can improve program performance. 2. In the operation of character stream, all characters are formed in memory, and all contents are temporarily saved in memory before output, so buffer is used to temporarily store data. If you want to print the entire character stream without closing it, you can use the Flush () method of the Writer class.

Use of byte streams and character streams

Is it better to use byte stream or character stream in development?

On all hard drives, files are saved or transferred in bytes, including images in bytes, and characters are formed only in memory, so byte operations are the most common. Which one is better depends on the circumstances. For example, if we want to read the contents of the character class file and operate on the opposite content, then the character stream is more appropriate. If you don’t need to do anything with the content but are simply transferring files such as copying files then byte stream is more suitable.

Description of the FileReader and FileWriter classes

public class FileReader

public class FileReader 
    extends InputStreamReader 
        extends Reader 
            extends java.lang.Object
Copy the code

FileReader is not a subclass of Reader, but a subclass of the conversion stream.

public class FileWriter 
    extends OutputStreamWriter 
        extends Writer 
            extends java.lang.Object
Copy the code

FilWriter is not a subclass of Writer, but a subclass of the transform stream. That is, in any case, operating on the byte stream output stream as an output stream of characters is actually output as bytes. The character input stream operates as a character but uses a byte stream, which means that when data is transferred or read from a file, the actual data stored in the file is always bytes.

Buffered processing stream

BufferedInputStream/BufferedOutputStream and BufferedReader/BufferedWriter each have a built-in buffer (8KB). The former handles byte streams and the latter character streams.

Commonly used method

Read text from the stream, using caching to improve efficiency, including characters, arrays, and lines. The size of the cache can be specified or the default size can be used. In most cases, the default size is sufficient. Note that the mark and reset functions are provided here, which can remember the location of the read and can be rolled back to read again in the future. It is necessary to avoid overwriting this part of the buffer when reading data and to save it for a length that can be specified at mark time. The reset call can be used to roll back data, but it must be marked, and the data read after mark cannot exceed the size set at the time of mark, otherwise it will be invalid, and the reset call will throw an exception.

Public class Buffer_Test {public static void main(String[] args) {// BufferedOutputStream and FileIOutputStream are the same // These two processing streams operate directly on the buffer BufferedOutputStream out; BufferedInputStreamin= null; // Input stream is the same as fileInputStream. Try {in = new BufferedInputStream(new FileInputStream("D: \ \ Java file IO \ \ Test \ \ a.t xt." ")); The second argument can specify the buffer size in bytesfor(int i = 0; i < 1024 * 9; i++) { in.read(); // Only the first time 8K is read from the file into the buffer, } out = new BufferedOutputStream(new FileOutputStream( // Node streams manipulate data directly"D: \ \ Java file IO \ \ Test \ \ a.t xt." ".true)); out.write(23); // Write data to the buffer and then write to the file. // Write data to the buffer and then write to the file. // Write data to the buffer Out.flush (); out.flush(); out.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}}Copy the code

The read() method executes as follows: We read 8192 bytes (or characters) directly from the source file. Whenever we read, we read and write directly from the BUF cache. If the size of the read is larger than the size of the BUF, the BUF returns the required data in memory (memory size 8192, If the 4096 is already gone and 8192 is to be read this time, the remaining 4096 bytes are read directly and the disk is interacted with), then the source file size of 8192 is read again and returned to the BUF cache and then returned to read() until the requirements are met. The write() method, again, writes data to the cache. When the write() method is called, it writes directly from the cache. If the write size is larger than the buF size, flush() directly, and then update the memory. The read and write methods are the same. Essentially improved efficiency and reduced disk interaction.

sense

Why do I need a Buffered processing stream?

To improve the efficiency of character reading. The BufferedInputStream and BufferedOutputStream classes are subclasses of FilterInputStream and FilterOutputStream, respectively, as decorator subclasses. And the constructor need FilterInputStream/FilterOutputStream subclass into arguments.

The BufferedReader and BufferedWriter classes are subclasses of Reader and Writer, respectively, as decorator subclasses. And the constructor requires subclasses of Reader/Writer.

Using them prevents actual writes every time data is read/sent, representing the use of buffers. It is important to remember that unbuffered operations write one byte for every byte read, and since disk IO operations are much slower than memory operations, unbuffered streams are inefficient. A buffered stream that reads many bytes at a time but does not write to disk, but is stored in memory first. When the buffer size is enough, write to disk at once. This method can reduce the number of disk operations, and speed up a lot!

If there is no data in the current buffer, the underlying reader is called to read data into the buffer. If there is data, read directly. The default buffer size is 8K, which means that each read is 8K units.

BufferedReader also provides a line-by-line function called readLine, which is not a Reader method. This method removes newlines (\r, \n, \r\n).

The role of flush

BufferedOutputStream automatically flushes when close() is called. BufferedOutputStream or Bufferedwriter will flush if close() is not called. Flush is called when the contents of the buffer need to be written to a file or sent over the network to another machine.

Source code analysis

Take BufferedInputStream as an example: source code analysis

To understand the source code for BufferedInputStream, you need to understand its thinking.

BufferedInputStream provides buffering for other input streams. When we create BufferedInputStream, we specify an input stream as an argument through its constructor. BufferedInputStream reads the input stream in batches, one piece at a time into the buffer; After this part of the buffer is processed, the next part of the data is read from the input stream. Why do you need a buffer?

The reason is simple, efficiency! The data in the buffer is actually stored in memory, and the original data may be stored in the hard disk or NandFlash storage media; And we know that reading data from memory is at least 10 times faster than reading data from a hard disk. Why not just read all the data into the buffer at once?

First, it may take a long time to read all the data. Second, memory is very expensive and the capacity is not as big as hard disk.

Additional knowledge

1. What is the difference between the built-in buffer of the class and the buffer we create ourselves?

FileWriter and Filereader already have buffers, but also BufferReader and BufferWriteer?

Java always has a buffer for IO operations. They differ in size. BufferWriter uses the buffer more rationally. When processing a large amount of data, FileWrite is less efficient than BufferWriter.

Processing stream with Object

Processing streams with objects are called Object processing streams. There are two common types: ObjectInputStream and ObjectOutputStream

An object stream can write an object out of, or read an object into, a program (object persistence), where serialization and deserialization are performed.

Serialization concept

Putting an object into some type of permanent storage is called holding. An object is said to be keepable if it can be stored on disk or tape, or if it can be sent to another machine and stored on storage or disk.

Serialization, persistence, serialization is a concept in Java. Reference links: serialization and deserialization

Basic usage

class Dog implements Serializable {
    private static final long serialVersionUID = -5156631308412187014L;
    private String name;
    private int age;

    public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } // omit get/setPublic static void main(String[] args) {Dog Dog = new Dog()"nickel"And 8); ObjectOutputStream out = null; ObjectInputStreamin = null;
        try {
            out = new ObjectOutputStream(
                    new FileOutputStream("D:\\Java\\IoTest\\dog.txt"));
            out.writeObject(dog);

            System.out.println("--------------- deserialization results as follows -------------------");
            in = new ObjectInputStream(
                    new FileInputStream("D:\\Java\\IoTest\\dog.txt")); Dog dog1 = (Dog) in.readObject(); System.out.println(dog1); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); }}}Copy the code

Note: Our object class must implement the Serializable interface. (The java.io.Serializable interface does not have any methods. Class objects that do not implement Serializable cannot save or restore their state.

A processing class with Data

The objects created by the DataInputStream and DataOutputStream classes are called data input streams and data output streams. They run programs that read Java raw data in a machine-independent style. That is, when reading a value, you no longer care how many bytes the value should be.

Commonly used method

Basic usage

public class Data_StreamTest {
    public static void main(String[] args) {
        DataOutputStream out = null;
        DataInputStream in = null;
        People p = new People("Zhang", 13, new A(1, "dsad"));
        try {
            out = new DataOutputStream(new FileOutputStream("D:\\Java\\IoTest\\p.txt"));
            out.writeUTF(p.getName());
            out.writeInt(p.getAge());
            out.writeInt(p.getA().getSex());
            out.writeUTF(p.getA().getName());
            in = new DataInputStream(new FileInputStream("D:\\Java\\IoTest\\p.txt"));
            People p1 = new People();
            p1.setName(in.readUTF());
            p1.setAge(in.readInt());
            A a = new A();
            a.setSex(in.readInt());
            a.setName(in.readUTF());
            p1.setA(a);
            System.out.println(p1);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class People {
    private String name;
    private int age;
    private A a;

    public People() { } public People(String name, int age, A a) { this.name = name; this.age = age; this.a = a; } // omit get/setClass A{private int sex; private String name; publicA() { } public A(int sex, String name) { this.sex = sex; this.name = name; } // omit get/setMethods}Copy the code

The difference and relation between object flow and data flow

1. Object is equivalent to a box containing IO stream. We can compare objects to blocks that are pieced together, and IO stream is the building blocks of pieced together. This is why objects should be Serializable.

WriteObject (this) ObjectInputStream (ObjectInputStream) WriteUTF (number), output.writeutf (name),output.writeInt(age)… The second box (DataInputStream), so in the first box we pour out the mixed blocks ((Member)intput.readObject()), and in the second box we take them out separately ({input.readutf (),inpu) T.r eadUTF (), input. ReadInt ()… }).

3. There is no significant difference in handling basic types, except that Object can write a serialized class instance to the output stream, whereas ObjectInput can read an ObjectOutput class instance from the input stream into an instance. DataOutputStream can only handle primitive types. The class that Object handles must be a class that implements serialization.