Readers and Writers
Streams are primarily intended for data that can be read as pure bytesbasically, byte data and numeric data encoded as binary numbers of one sort or another. Streams are specifically not intended for reading and writing text, including both ASCII text, such as "Hello World," and numbers formatted as text, such as "3.1415929". For these purposes, you should use readers and writers.
Input and output streams are fundamentally byte-based. Readers and writers are based on characters, which can have varying widths depending on the character set. For example, ASCII and Latin-1 use 1-byte characters. UTF-32 uses 4-byte characters. UTF-8 uses characters of varying width (between one and four bytes). Since characters are ultimately composed of bytes, readers take their input from streams. However, they convert those bytes into chars according to a specified encoding format before passing them along. Similarly, writers convert chars to bytes according to a specified encoding before writing them onto some underlying stream.
The java.io.Reader and java.io.Writer classes are abstract superclasses for classes that read and write character-based data. The subclasses are notable for handling the conversion between different character sets. The core Java API includes nine reader and eight writer classes, all in the java.io package:
BufferedReader
BufferedWriter
CharArrayReader
CharArrayWriter
FileReader
FileWriter
FilterReader
FilterWriter
InputStreamReader
LineNumberReader
OutputStreamWriter
PipedReader
PipedWriter
PrintWriter
PushbackReader
StringReader
StringWriter
For the most part, these classes have methods that are extremely similar to the equivalent stream classes. Often the only difference is that a byte in the signature of a stream method becomes a char in the signature of the matching reader or writer method. For example, the java.io.OutputStream class declares these three write( ) methods:
public abstract void write(int i) throws IOException public void write(byte[] data) throws IOException public void write(byte[] data, int offset, int length) throws IOException
The java.io.Writer class, therefore, declares these three write( ) methods:
public void write(int i) throws IOException public void write(char[] data) throws IOException public abstract void write(char[] data, int offset, int length) throws IOException
As you can see, the signatures match except that in the latter two methods the byte array data has changed to a char array. There's also a less obvious difference not reflected in the signature. While the int passed to the OutputStream write( ) method is reduced modulo 256 before being output, the int passed to the Writer write( ) method is reduced modulo 65,536. This reflects the different ranges of chars and bytes.
java.io.Writer also has two more write( ) methods that take their data from a string:
public void write(String s) throws IOException public void write(String s, int offset, int length) throws IOException
Because streams don't know how to deal with character-based data, there are no corresponding methods in the java.io.OutputStream class.