Come on guys, let’s meet.

Secular wanderer: a programmer who focuses on technical research

preface

In the previous chapter, we learned about files and IO streams, respectively:

  • FileInputStream
  • FileOutputStream
  • FileReader
  • FileWriter

These are the key points that we must grasp

Emphasis is also placed on the problem of IO flow direction: the program as a reference

  • From file to program is the input stream
  • From the program to the file is the output stream

If you don’t know, go to the previous section

I’m going to show you a diagram of some of the flows that might be used

Let’s go through the other streams one by one

Processing flow

In an IO stream, there are streams that wrap around the underlying input/output streams, through which we can improve the efficiency of reading/writing files through the underlying stream. These streams are called processing streams

Streams come in pairs, like before:

  • InputStream – OutputStream
  • Reader – Writer

So I’m going to introduce you in pairs

Byte flow character stream

Earlier, we said that input stream processing requires data sources, which can come from files, networks, and so on.

We can use byte streams to process any data source, but we can also pursue efficiency while pursuing functionality, right

And in one scenario, only the byte stream, not the character stream, can be retrieved

Sockets, for example, can only get byte streams

If we can determine that the data source is a character set, then we can use the processing class to wrap the processing, improve processing efficiency, which is what we will introduce the following wrapper class:

  • InputStreamReader
  • OutputStreamWriter

Let’s start with a picture:

These two classes are specifically used to convert byte streams. Let’s see how they are implemented

File file = new File(System.getProperty("user.dir") + "/study-java/src/main/java/zopx/top/study/jav/_file/InputStreamReaderDemo.java");

/** ** *
FileInputStream inputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream("b.txt");

/** * Since reading is reading text files, it is character set data, so we can use processing streams to convert */

InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);

System.out.println(inputStreamReader.read());

int len = 0;
// byte[] buffer = new byte[1024];
char[] buffer = new char[1024];
while ((len = inputStreamReader.read(buffer)) > 0) {
    outputStreamWriter.write(new String(buffer, 0, len));
}

outputStreamWriter.flush();
outputStreamWriter.close();
inputStreamReader.close();
// Close the base stream at the end of the process
fileOutputStream.close();
inputStream.close();
Copy the code

System.getproperty (“user.dir”) : Gets the current working directory. If we want to output Java System parameters, we can use the current method:

System.getProperties().list(System.out);

When converting streams via InputStreamReader, you can specify a character encoding, otherwise it looks like this:

  • The default character set of the Java vm. If the default character set of the Java VM is null, utF-8 is used
public static Charset defaultCharset(a) {
    if (defaultCharset == null) {
        synchronized (Charset.class) {
            String csn = AccessController.doPrivileged(
                new GetPropertyAction("file.encoding"));
            Charset cs = lookup(csn);
            if(cs ! =null)
                defaultCharset = cs;
            else
                defaultCharset = forName("UTF-8"); }}return defaultCharset;
}
Copy the code

The above is the process of converting a byte stream into a character stream. In fact, once the byte stream is converted, a series of processing operations will be the same as the process of character stream

Buffer byte stream

Buffer flow is we in practice in order to improve the performance of another kind of processing flow, the basic flow, read and write files will directly call the underlying accessors, frequently called the underlying methods can also cause performance of consumption, so Java based on the buffer flow is achieved, through the extension of the underlying, speaking, reading and writing method, improve the performance of ascension

Let’s look at the read and write methods of the basic IO stream

// FileInputStream

private native int readBytes(byte b[], int off, int len) throws IOException;

public int read(byte b[]) throws IOException {
    return readBytes(b, 0, b.length);
}

// FileOutputStream
public void write(byte b[], int off, int len) throws IOException {
    writeBytes(b, off, len, append);
}
private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;
Copy the code

The accessor to the buffered stream

// BufferedInputStream
public synchronized int read(byte b[], int off, int len)
        throws IOException
{
    getBufIfOpen(); // Check for closed stream
    if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return 0;
    }

    int n = 0;
    for (;;) {
        int nread = read1(b, off + n, len - n);
        if (nread <= 0)
            return (n == 0)? nread : n; n += nread;if (n >= len)
            return n;
        // if not closed but no bytes available, return
        InputStream input = in;
        if(input ! =null && input.available() <= 0)
            returnn; }}// BufferedOutputStream
public synchronized void write(byte b[], int off, int len) throws IOException {
    if (len >= buf.length) {
        /* If the request length exceeds the size of the output buffer, flush the output buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */
        flushBuffer();
        out.write(b, off, len);
        return;
    }
    if (len > buf.length - count) {
        flushBuffer();
    }
    System.arraycopy(b, off, buf, count, len);
    count += len;
}
Copy the code

Why does buffering streams improve performance?

  • By looking atBufferedOutputStream::writeMethod view, inwrite()The method internally determines if the condition is not met, and stores the data written by our outer layer inprotected byte buf[];Compared with this kind of data, we can passflush()Method refreshes and then invokes the underlying layerwrite()Method writes data to the specified file
  • This improves performance by reducing the number of calls to the underlying methods

Let’s take a look at the implementation

private static void bufferIO(a) throws Exception {
    BufferedInputStream bufis = new BufferedInputStream(new FileInputStream(System.getProperty("user.dir") + "/study-java/src/main/java/zopx/top/study/jav/_file/InputStreamReaderDemo.java"));
    BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c.txt"));

    int len = 0;
    byte[] buffer = new byte[1024];

    while((len = bufis.read(buffer)) ! = -1) {
        bufos.write(buffer, 0, len);
    }
    bufis.close();
    // This is why flush() is called here
    bufos.flush();
    bufos.close();
}
Copy the code

If we look at the source code, we can get information about them from the comments

  • When BufferedInputStream is created, an array of internal buffers is created, and when bytes in the stream are read or skipped, the internal buffers are repopulated as needed from the contained input stream, as many bytes at a time
  • With BufferedOutputStream, an application can write bytes to the underlying output stream without causing a call to the underlying system for each byte written.

Buffering read/write streams

Buffering read/write streams and buffering byte streams have the same effect. Let’s look at the implementation:

private static void bufferRead(a) throws Exception {
    BufferedReader br = new BufferedReader(new FileReader(FILE_NAME));
    BufferedWriter bw = new BufferedWriter(new FileWriter("d.txt"));

    // Read the entire line
    String line = "";
    while((line = br.readLine()) ! =null) {
        bw.write(line);
        / / a newline
        bw.newLine();
    }

    br.close();
    bw.flush();
    bw.close();
}
Copy the code

The difference from the previous comparison is

  • BufferedReaderIn addition to reading content via char[], it is also possible to read entire lines:readLine()
  • If we callreadLine()When reading content, passBufferedWriterWhen writing, it needs to be callednewLine()If this step is missing, the entire data is written on one line

That’s a lot more convenient

Printing flow

There is another class of flows in Java that we use a lot, but don’t pay much attention to, and that is the print stream. The corresponding class is:

  • System.out
  • System.in

It might be a little bit more intuitive if you look at it like this:

PrintStream out = System.out;
InputStream in = System.in;
Copy the code

This approach is obvious: all content is output on the console

PrintStream provides us with the ability to easily print representations of various data values

We believe this scenario exists, and we will also use this class when interacting with the console. Let’s take a look at a small example:

// Enter from the console
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
    // Get the input
    String next = scanner.next();
    System.out.println(next);
}
Copy the code

Other types of streams

Combined with the above initial figure, there are still three groups of flows that have not been introduced. Let’s introduce them respectively as follows:

ByteArrayInputStream/ByteArrayOutputStream

ByteArrayInputStream is a stream that contains an internal buffer containing bytes that can be read from the stream.

ByteArrayOutputStream’s data is written to a byte array. Buffers grow automatically as data is written

Let’s look at the main structures

/ / emphasis
protected byte buf[];
protected int pos;
Copy the code

Here’s how to use it:

It is used in much the same way as the previous stream

ByteArrayInputStream inputStream = new ByteArrayInputStream("Mr. Sanq's learning record is complete.".getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

int len = 0;
byte[] buffer = new byte[1024];
while((len = inputStream.read(buffer)) ! = -1) {
    outputStream.write(buffer, 0, len);
}
inputStream.close();
outputStream.close();

System.out.println(outputStream.toString());
outputStream.writeTo(System.out);
Copy the code

The difference from the previous stream is:

  • ByteArrayInputStream data source can only be byte[]
  • ByteArrayOutputStreamBy calling thewrite()Method to store data to its internal propertiesprotected byte buf[], and will automatically increase the length of the data, and can passwriteTo()Method to specify the output stream
  • andByteArrayOutputStreaminclose()And then it will passtoString()ortoByteArray()To output the data content

In plain English, you’re manipulating arrays of bytes

ObjectInputStream/ObjectOutputStream

Deserialize/serialize base data types and objects

In the process of file storage or network transmission, you can transfer text, pictures, video, audio and other files, these files are transmitted through binary array, so if we want to transfer object data, can it be achieved?

The answer, of course, is yes, and we’ll talk about serialization and deserialization first

First, what serialization and deserialization?

  • Serialization: The process of converting the state information of an object into a form that can be stored or transmitted
  • Deserialization: The process by which a program retrieves serialized data from a file or network and reorganizes the content to obtain the state of the object

Java objects, as we normally define them, can only be stored in JVM memory, and when the JVM shuts down, memory is emptied, and the current object does not exist. If you can convert an object to a stream and store it in a specified file by serializing it with ObjectOutputStream, you can make the object permanent by deserializing the stored stream object into a Java object with ObjectInputStream

Based on this feature, we can implement it in this way:

  • Permanence object
  • Transfer objects over the network

If you want to implement serialization in Java, you only need to implement one interface:

  • Serializable

Here’s an example:

public class Student implements Serializable {
    public Long id;
    public String name;

    public Student(a) {}public Student(Long id, String name) {
        this.id = id;
        this.name = name; }}Copy the code

This implements the object serialization.

Let’s see what we can do

The above implementation is simple, but the key apis are ObjectInputStream and ObjectOutputStream

Let’s look at specific uses:

private static void io(a) throws Exception {
    // Write to the specified file
    // Write successfully do not open to read, this is not human can understand
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("a.tmp"));
    outputStream.writeObject(new Student(1L."Zhang"));
    outputStream.writeUTF("里斯");
    outputStream.writeBoolean(true);
    outputStream.writeInt(1);
    outputStream.close();

    // Read the corresponding data from the file
    ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.tmp"));
    Object o = inputStream.readObject();
    String name = inputStream.readUTF();
    boolean b = inputStream.readBoolean();
    int i = inputStream.readInt();
    inputStream.close();
    System.out.println(o);
    System.out.println(name);
    System.out.println(b);
    System.out.println(i);
}
Copy the code

ObjectInputStream and ObjectOutputStream provide a number of corresponding methods:

We can use these methods to store as much type data as we want. But one thing to note here:

  • The order of writeXX() and readXX() must be the same; otherwise, an error will be reported

Let’s summarize the stream object:

  • If you need to transfer objects through IO streams, you must implement the serialization interface
  • If there are individual fields that do not need to be instantiated during serialization, we can modify them with transient, such as password

When we talk about networks later, we can try passing objects over the network via the current stream

DataInputStream/DataOutputStream

Data input streams allow applications to read raw Java data types from the underlying input stream in a machine-independent manner. The application uses the data output stream to write data that can later be read by the data input stream

This set of streams is used in much the same way as the ObjectInputStream described above, and the API methods are similar. Let’s look at it in action:

static String str = "ddsad";
private static void io(a) throws Exception {

    DataOutputStream outputStream = new DataOutputStream(new FileOutputStream("b.tmp"));
    outputStream.writeUTF(kakaca);
    outputStream.writeBoolean(true);
    outputStream.writeBytes(str);
    outputStream.writeLong(2);

    outputStream.flush();
    outputStream.close();

    DataInputStream inputStream = new DataInputStream(new FileInputStream("b.tmp"));
    System.out.println(inputStream.readUTF());
    System.out.println(inputStream.readBoolean());
    byte[] buff = new byte[str.length()];
    inputStream.read(buff);
    System.out.println(new String(buff));
    System.out.println(inputStream.readLong());

    inputStream.close();
}
Copy the code

It almost covers the methods supported by ObjectInputStream and ObjectOutputStream, but I won’t cover them here

The document

There are only a few methods listed above, but it is recommended to check the official documentation for more methods:

I’m not going to do a list, but you can go through the following summary section and search for that

IO stream document set