A language that supports international text
must separate the reading and writing of raw
bytes from the reading and writing of characters, since in an
international system they are no longer the same thing. Classes that
read characters must be able to parse a variety of character
encodings, not just ASCII, and translate them into the
language’s native character set. Classes that write characters
must be able to translate the language’s native character set
into a variety of formats and write those. In Java this task is
performed by the Reader
and
Writer
classes.
You’re probably going to experience a little
déjà vu. The
java.io.Writer
class is modeled on the
java.io.OutputStream
class. The
java.io.Reader
class is modeled on the
java.io.InputStream
class. The names and
signatures of the members of the Reader
and
Writer
classes are similar (sometimes identical)
to the names and signatures of the members of the
InputStream
and OutputStream
classes. The patterns these classes follow are similar as well.
Filtered input and output streams are chained to other streams in
their constructors. Similarly, filtered readers and writers are
chained to other readers and writers in their constructors.
InputStream
and OutputStream
are abstract superclasses that identify common functionality in the
concrete subclasses. Likewise, Reader
and
Writer
are abstract superclasses that identify
common functionality in the concrete subclasses. The difference
between readers and writers and input and output streams is that
streams are fundamentally byte based, while readers and writers are
fundamentally character based. Where an input stream reads a byte, a
reader reads a character; where an output stream writes a byte, a
writer writes a character.
While bytes are a more or less universal concept, characters are not.
As you learned in the last chapter, the same character can be encoded
differently in different character sets. Different character sets
encode different characters. Characters can even have different
widths in different character sets. For example, ASCII and ISO
Latin-1 use one-byte characters. Unicode uses two-byte characters.
UTF-8 uses characters of varying width between one and three bytes.
Concrete subclasses of the Reader
and
Writer
classes convert between different character
sets and Java’s internal Unicode character set.
The Writer
class is abstract, just like
OutputStream
is abstract. You won’t have any
pure instances of Writer
that are not also
instances of some concrete subclass of Writer
.
However, many of the subclasses of Writer
differ
primarily in the targets of the text they write, just as many
concrete subclasses of OutputStream
differ only in
the targets of the data they write. Most of the time you don’t
care about the difference between FileOutputStream
and ByteArrayOutputStream
. Similarly, most of the
time you won’t care about the differences between
FileWriter
and StringWriter
.
You’ll just use the methods of the common superclass,
java.io.Writer
.
You use a writer almost exactly as you use an output stream. Rather
than writing byte
s, you write
char
s. The write()
method
writes a subarray from the char
array text
starting at offset
and continuing for
length
characters:
public abstract void write(char[] text, int offset, int length) throws IOException
For example, given some Writer
object
w
, you can write the string
Testing
1-2-3
like this:
char[] test = {'T', 'e', 's', 't', 'i', 'n', 'g', ' ', '1', '-', '2', '-', '3'}; w.write(test, 0, test.length);
This method is abstract. Concrete subclasses that convert
char
s into byte
s according to a
specified encoding and write those bytes onto an underlying stream
must override this method. An IOException
may be
thrown if the underlying stream’s write()
method throws an IOException
. You can also write a
single character, an entire array of characters, a string, or a
substring:
public void write(int c) throws IOException public void write(char[] text) throws IOException public void write(String s) throws IOException public void write(String s, int offset, int length) throws IOException
The default implementations of these four methods convert their first
argument into an array of char
s and pass that to
write(char[]
text,
int
offset,
int
length)
. Specific
subclasses may provide more efficient implementations of these
methods.
This is one of the few instances where the
general structure of the Writer
and the
OutputStream
classes diverge, though not in a very
significant way. In OutputStream
the fundamental,
abstract method that must be overridden by subclasses is the
write()
method that writes a single byte.
OutputStream
’s multibyte
write()
methods are implemented in terms of the
single-byte write()
method, whereas
Writer
’s single-character
write()
method is implemented in terms of a
multicharacter write()
method.
Like output streams, writers may be buffered, precisely because their
underlying output stream is buffered. To force the write to take
place, call flush()
:
public abstract void flush() throws IOException
The close()
method closes the writer and releases
any resources associated with it:
public abstract void close() throws IOException
This flushes the writer, then closes the underlying output stream.
3.144.160.142