Character Streams
Text is characters, but files contain bytes
For text, it is more convenient to work with characters rather than individual bytes.
Reader and Writer provide that model:
the first reads characters, and the second writes them.
But the boundary between a file and text still passes through an encoding — the rule for converting bytes to characters and back.
That is why reading text requires knowing the encoding in which it was stored.
Modern code usually specifies it explicitly, such as UTF_8,
rather than relying on environment settings.
try (Reader reader = new InputStreamReader(
new FileInputStream("message.txt"), StandardCharsets.UTF_8)) {
int ch = reader.read();
System.out.println((char) ch);
}
InputStreamReader is a bridge between the two worlds:
it receives bytes from an InputStream and decodes them into characters using the selected encoding.
OutputStreamWriter works in the opposite direction.
Reader and Writer work with char values
Character streams provide methods for reading and writing individual characters and char arrays.
As with byte streams, reading returns the special value -1 when the data has ended.
char[] buffer = new char[1024];
try (Reader reader = new FileReader("message.txt", StandardCharsets.UTF_8)) {
int read = reader.read(buffer);
System.out.println(read);
}
Here, the read value again means the actual number of elements received,
except now those elements are characters rather than raw bytes.
Writing text
try (Writer writer = new FileWriter("message.txt", StandardCharsets.UTF_8)) {
writer.write("Hello, Java!");
}
A Writer accepts text as characters, while the concrete implementation is responsible for turning them into bytes.
This makes the code's intention clearer: if the task is to work with text, the character API usually expresses that meaning better.