Appearance
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:
| InputStream | Reader |
|---|---|
| Byte stream, reads by byte | Character 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
Readerdefines the superclass for all character input streams.FileReaderimplements file character stream input and requires encoding to be specified.CharArrayReaderandStringReadercan simulate character stream input in memory.Readeris constructed based onInputStream: you can convert anyInputStreamtoReaderusingInputStreamReaderwith specified encoding.- Always use
try (resource)to ensureReaderis closed correctly.