4.3 Filter Streams
InputStream and OutputStream
are fairly raw classes. They read and write bytes singly or in
groups, but that's all. Deciding what those bytes
meanwhether they're integers or IEEE 754
floating point numbers or Unicode textis completely up to the
programmer and the code. However, there are certain extremely common
data formats that can benefit from a solid implementation in the
class library. For example, many integers passed as parts of network
protocols are 32-bit big-endian integers. Much text sent over the Web
is either 7-bit ASCII, 8-bit Latin-1, or multi-byte UTF-8. Many files
transferred by FTP are stored in the zip format. Java provides a
number of filter classes you can attach to raw streams to translate
the raw bytes to and from these and other formats.The filters come in two versions: the filter streams and the readers
and writers. The filter streams still work primarily with raw data as
bytes: for instance, by compressing the data or interpreting it as
binary numbers. The readers and writers handle the special case of
text in a variety of encodings such as UTF-8 and ISO 8859-1. Filter
streams are placed on top of raw streams such as a
TelnetInputStream or a
FileOutputStream or other filter streams. Readers
and writers can be layered on top of raw streams, filter streams, or
other readers and writers. However, filter streams cannot be placed
on top of a reader or a writer, so we'll start with
filter streams and address readers and writers in the next section.Filters
are organized in a chain, as shown in Figure 4-2. Each link in the
chain receives data from the previous filter or stream and passes the
data along to the next link in the chain. In this example, a
compressed, encrypted text file arrives from the local network
interface, where native code presents it to the undocumented
TelnetInputStream. A
BufferedInputStream buffers the data to speed up
the entire process. A CipherInputStream decrypts
the data. A GZIPInputStream decompresses the
deciphered data. An InputStreamReader converts the
decompressed data to Unicode text. Finally, the text is read into the
application and processed.
Figure 4-2. The flow of data through a chain of filters

filter output stream has the same write( ),
close( ), and flush( ) methods
as java.io.OutputStream. Every filter input stream
has the same read( ), close( ),
and available( ) methods as
java.io.InputStream. In some cases, such as
BufferedInputStream and
BufferedOutputStream, these may be the only
methods they have. The filtering is purely internal and does not
expose any new public interface. However, in most cases, the filter
stream adds public methods with additional purposes. Sometimes these
are intended to be used in addition to the usual read() and write( ) methods, like the
unread( ) method of
PushbackInputStream. At other times, they almost
completely replace the original interface. For example,
it's relatively rare to use the write() method of PrintStream instead of one
of its print( ) and println( )
methods.
4.3.1 Chaining Filters Together
Filters
are connected to streams by their constructors. For example, the
following code fragment buffers input from the file
data.txt. First, a
FileInputStream object fin is
created by passing the name of the file as an argument to the
FileInputStream constructor.
Then, a BufferedInputStream object
bin is created by passing fin
as an argument to the BufferedInputStream
constructor:
FileInputStream fin = new FileInputStream("data.txt");From this point forward, it's possible to use the
BufferedInputStream bin = new BufferedInputStream(fin);
read( ) methods of both fin and
bin to read data from the file
data.txt. However, intermixing calls to
different streams connected to the same source may violate several
implicit contracts of the filter streams. Most of the time, you
should only use the last filter in the chain to do the actual reading
or writing. One way to write your code so that it's
at least harder to introduce this sort of bug is to deliberately lose
the reference to the underlying input stream. For example:
InputStream in = new FileInputStream("data.txt");After these two lines execute, there's no longer any
in = new BufferedInputStream(in);
way to access the underlying file input stream, so you
can't accidentally read from it and corrupt the
buffer. This example works because it's not
necessary to distinguish between the methods of
InputStream and those of
BufferedInputStream.
BufferedInputStream is simply used polymorphically
as an instance of InputStream in the first place.
In cases where it is necessary to use the additional methods of the
filter stream not declared in the superclass, you may be able to
construct one stream directly inside another. For example:
DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(Although these statements can get a little long,
new FileOutputStream("data.txt")));
it's easy to split the statement across several
lines, like this:
DataOutputStream dout = new DataOutputStream(Connection is permanent. Filters cannot be disconnected from a
new BufferedOutputStream(
new FileOutputStream("data.txt")
)
);
stream.There are times when you may need to use the methods of multiple
filters in a chain. For instance, if you're reading
a Unicode text file, you may want to read the byte order mark in the
first three bytes to determine whether the file is encoded as
big-endian UCS-2, little-endian UCS-2, or UTF-8, and then select the
matching Reader filter for the encoding. Or, if
you're connecting to a web server, you may want to
read the header the server sends to find the
Content-encoding and then use that content
encoding to pick the right Reader filter to read
the body of the response. Or perhaps you want to send floating point
numbers across a network connection using a
DataOutputStream and then retrieve a
MessageDigest from the
DigestOutputStream that the
DataOutputStream is chained to. In all these
cases, you need to save and use references to each of the underlying
streams. However, under no circumstances should you ever read from or
write to anything other than the last filter in the chain.