Skip to content
On this page

Reader

Reader is another input stream interface provided by Java's IO library. The difference from InputStream is that InputStream is a byte stream, reading in bytes, while Reader is a character stream, reading in characters:

InputStreamReader
Byte stream, reads by byteCharacter stream, reads by char
Reads byte (-1, 0~255): int read()Reads character (-1, 0~65535): int read()
Reads byte array: int read(byte[] b)Reads character array: int read(char[] c)

java.io.Reader is the superclass of all character input streams, with its main method defined as:

java
public int read() throws IOException;

This method reads the next character from the character stream and returns it as an int, ranging from 0 to 65535. If the end of the stream is reached, it returns -1.

FileReader

FileReader is a subclass of Reader that can open a file and provide a Reader. The following code demonstrates how to read all characters from a FileReader:

java
public void readFile() throws IOException {
    // Create a FileReader object:
    Reader reader = new FileReader("src/readme.txt"); // What is the character encoding?
    for (;;) {
        int n = reader.read(); // Repeatedly call read() until -1 is returned
        if (n == -1) {
            break;
        }
        System.out.println((char)n); // Print char
    }
    reader.close(); // Close the stream
}

This code works fine for a plain ASCII-encoded text file. However, if the file contains Chinese characters, it may result in garbled output, as FileReader uses the system's default encoding, which might be GBK on Windows, leading to issues with UTF-8 encoded files.

To avoid encoding issues, we need to specify the encoding when creating the FileReader:

java
Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8);

Similar to InputStream, Reader is a resource that needs to be properly closed even when errors occur. Therefore, we use try (resource) to ensure the Reader is correctly closed whether or not an IO error occurs:

java
try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
    // TODO
}

Reader also provides a method to read multiple characters at once into a char[] array:

java
public int read(char[] c) throws IOException;

This method returns the actual number of characters read, up to the length of the char[] array. A return value of -1 indicates the end of the stream.

Using this method, we can set up a buffer and fill it as much as possible each time:

java
public void readFile() throws IOException {
    try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
        char[] buffer = new char[1000];
        int n;
        while ((n = reader.read(buffer)) != -1) {
            System.out.println("read " + n + " chars.");
        }
    }
}

CharArrayReader

CharArrayReader simulates a Reader in memory by turning a char[] array into a Reader, similar to ByteArrayInputStream:

java
try (Reader reader = new CharArrayReader("Hello".toCharArray())) {
}

StringReader

StringReader allows a String to be used as a data source, functioning nearly the same as CharArrayReader:

java
try (Reader reader = new StringReader("Hello")) {
}

InputStreamReader

How is Reader related to InputStream?

Aside from the special CharArrayReader and StringReader, a standard Reader is constructed based on InputStream, as it needs to read byte streams and convert them to characters based on the specified encoding. If we examine the source code of FileReader, we see that it internally holds a FileInputStream.

Since Reader is essentially a byte-to-char converter based on InputStream, it is entirely feasible to convert an existing InputStream into a Reader. InputStreamReader serves as such a converter, allowing any InputStream to be converted into a Reader. Here’s an example:

java
// Hold an InputStream:
InputStream input = new FileInputStream("src/readme.txt");
// Convert to Reader:
Reader reader = new InputStreamReader(input, "UTF-8");

When constructing InputStreamReader, we need to pass an InputStream and specify the encoding, resulting in a Reader object. The above code can be simplified using try (resource) as follows:

java
try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt"), "UTF-8")) {
    // TODO:
}

This code essentially represents one implementation of FileReader.

When using the try (resource) structure, closing the Reader automatically invokes the close() method of the underlying InputStream, so only the outer Reader object needs to be closed.

Tip

Using InputStreamReader, you can convert an InputStream into a Reader.

Summary

  • Reader defines the superclass for all character input streams.
  • FileReader implements file character stream input and requires encoding to be specified.
  • CharArrayReader and StringReader can simulate character stream input in memory.
  • Reader is constructed based on InputStream: you can convert any InputStream to Reader using InputStreamReader with specified encoding.
  • Always use try (resource) to ensure Reader is closed correctly.
Reader has loaded