The most important function of a computer is to process data. A useful computer language needs to have good IO functionality to allow unprocessed data to flow into the program and processed data to flow out.

Java’s IO capabilities are complex compared to those of other languages. In other languages, many IO functions, such as reading files, are wrapped and can be implemented in a line or two. In Java, programmers often need multiple levels of decoration to read files.

The benefit of relative complexity is the flexibility of IO. In Java, programmers can control the entire flow of IO to design the best IO approach. We’ll see more below.

Below is the file file.txt that I used for the demonstration

Hello World! Hello Nerd!

Let’s look at an example of a file read:

import java.io.*; public class Test { public static void main(String[] args) { try { BufferedReader br = new BufferedReader(new FileReader("file.txt")); String line = br.readLine(); while (line ! = null) { System.out.println(line); line = br.readLine(); } br.close(); } catch(IOException e) { System.out.println("IO Problem"); }}}Copy the code

This program contains a try… catch… Finally exception handler.

Br: BufferedReader br = new BufferedReader(new FileReader(“file.txt”));

To create this, we create a FileReader object that reads a stream of bytes from the file “file.txt” and converts it to a stream of text. In Java, the standard text encoding is Unicode. BufferedReader() receives the FileReader object and extends the FileReader functionality to create a new BufferedReader object. In addition to the file reads and transforms described above, this object also provides buffered reading. Finally, we can read the file line by line by calling the readLine() method on the BR object.

Cache read is the creation of an area of memory that serves as a buffer for the text stream read by the FileReader. When the contents of the cache are read away (such as the readLine() command), the cache loads subsequent text streams.

BufferedReader() is a decorator that takes an original object and returns a decorated, more complex object. The advantage of a decorator is that it can be used to modify different objects. What we’re embellishing here is a stream of text read from a file. Other text streams, such as standard input, network streams, and so on, can be BufferedReader() embellished for cache reading.

The figure below shows how BR works, with data flowing from the bottom up:

The above decorating process is similar to the idea of text flow in Linux. In Linux, we use a function-like approach to processing and passing text streams. In Java, we use decorators. But they all have the same goal of modularity and free combination of functionality.

In fact, Java provides a wealth of decorators. FileReader combines the read and transform steps and uses common defaults, such as encoding Unicode. Instead of FileReader, we can use a combination of FileInputStream + InputStreamReader to separate the byte reading and conversion steps and have better control over both processes.

(Of course, FileReader is much easier to use. InputStreamReader converts a FileInputStream to a Reader for processing Unicode text.

Arrows indicate the direction of data flow

Streams read and write from four base classes: InputStream, OutputStream, Reader, and Writer. InputStream and Reader handle reads, while OutputStream and Writer handle writes. They are all in the java.io package. The inheritance relationship is as follows:

java.io

In addition, IOException has the following derived classes:

IOException

Reader and Writer and their derived classes deal with Unicode text. As we saw with Buffered Reader, InputStreamReader or FileReader.

InputStream and OutputStream and their derived classes handle byte streams. All data in a computer can be thought of as bytes, so InputStream and OutputStream can be used to process a wider range of data. For example, we can use the following combination to read the data contained in the compressed file (such as integers):

Arrows indicate the direction of data flow

We read the byte stream from the compressed file, then decompress it, and finally read the data.

The write operation is similar to the read operation. We can implement complex write functions by using decorations. Here is a simple example of writing text:

import java.io.*; public class Test { public static void main(String[] args) { try { String content = "Thank you for your fish."; File file = new File("new.txt"); // create the file if doesn't exists if (! file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file.getAbsoluteFile()); BufferedWriter bw = new BufferedWriter(fw); bw.write(content); bw.close(); } catch(IOException e) { System.out.println("IO Problem"); }}}Copy the code

Above, the File object is created to handle file paths.

The summary here is just a basic introduction to Java IO. Java IO is relatively complex. Java programmers need to spend some time familiarizing themselves with classes and their capabilities in java.io.