Bruce Eckel’s Thinking in Java | Contents | Prev | Next |
and
useful interfaces
use of layered objects to dynamically and transparently add responsibilities to
individual objects is referred to as the decorator
pattern. (Patterns
[47]
are the subject of Chapter 16.) The decorator pattern specifies that all
objects that wrap around your initial object have the same interface, to make
the use of the decorators transparent – you send the same message to an
object whether it’s been decorated or not. This is the reason for the
existence of the “filter” classes in the Java IO library: the
abstract “filter” class is the base class for all the decorators.
(A decorator must have the same interface as the object it decorates, but the
decorator can also extend the interface, which occurs in several of the
“filter” classes).
are often used when subclassing requires a large number of subclasses to
support every possible combination needed – so many that subclassing
becomes impractical. The Java IO library requires many different combinations
of features which is why the decorator pattern is a good approach. There is a
drawback to the decorator pattern, however. Decorators give you much more
flexibility while you’re writing a program (since you can easily mix and
match attributes), but they add complexity to your code. The reason that the
Java IO library is awkward to use is that you must create many classes –
the “core” IO type plus all the decorators – in order to get
the single IO object that you want.
classes that provide the decorator interface to control a particular
InputStream
or
OutputStream
are the
FilterInputStream
and
FilterOutputStream
– which don’t have very intuitive names. They are derived,
respectively, from
InputStream
and
OutputStream,
and they are abstract classes, in theory to provide a common interface for all
the different ways you want to talk to a stream. In fact,
FilterInputStream
and
FilterOutputStream
simply mimic their base classes, which is the key requirement of the decorator.
Reading
from an InputStream
with
FilterInputStream
FilterInputStream
classes accomplish two significantly different things.
DataInputStream
allows you to read different types of primitive data as well as
String
objects. (All the methods start with “read,” such as
readByte( ),
readFloat( ),
etc.) This, along with its companion
DataOutputStream,
allows you to move primitive data from one place to another via a stream. These
“places” are determined by the classes in Table 10-1. If
you’re reading data in blocks and parsing it yourself, you won’t
need
DataInputStream,
but in most other cases you will want to use it to automatically format the
data you read.
remaining classes modify the way an
InputStream
behaves internally: whether it’s buffered or unbuffered, if it keeps
track of the lines it’s reading (allowing you to ask for line numbers or
set the line number), and whether you can push back a single character. The
last two classes look a lot like support for building a compiler (that is, they
were added to support the construction of the Java compiler), so you probably
won’t use them in general programming.
probably need to buffer your input almost every time, regardless of the IO
device you’re connecting to, so it would have made more sense for the IO
library to make a special case for unbuffered input rather than buffered input.
10-3. Types of FilterInputStream
Class
|
Function
|
Constructor
Arguments |
How
to use it |
||
Data-InputStream
|
Used
in concert with DataOutputStream, so you can read primitives (int, char, long, etc.) from a stream in a portable fashion. |
InputStream
|
Contains
a full interface to allow you to read primitive types. |
Buffered-InputStream
|
Use
this to prevent a physical read every time you want more data. You’re saying “Use a buffer.” |
InputStream,
with optional buffer size. |
This
doesn’t provide an interface per se , just a requirement that a buffer be used. Attach an interface object. |
||
LineNumber-InputStream
|
Keeps
track of line numbers in the input stream; you can call getLineNumber( ) and setLineNumber(int). |
InputStream
|
This
just adds line numbering, so you’ll probably attach an interface object. |
||
Pushback-InputStream
|
Has
a one byte push-back buffer so that you can push back the last character read. |
InputStream
|
Generally
used in the scanner for a compiler and probably included because the Java compiler needed it. You probably won’t use this. |
Writing
to an OutputStream
with
FilterOutputStream
complement to
DataInputStream
is
DataOutputStream,
which formats each of the primitive types and
String
objects onto a stream in such a way that any
DataInputStream,
on any machine, can read them. All the methods start with “write,”
such as
writeByte( ),
writeFloat( ),
etc.
you want to do true formatted output, for example, to the console, use a
PrintStream.
This is the endpoint that allows you to print all of the primitive data types
and
String
objects in a viewable format as opposed to
DataOutputStream,
whose goal is to put them on a stream in a way that
DataInputStream
can portably reconstruct them. The
System.out
static object is a
PrintStream.
two important methods in
PrintStream
are
print( )
and
println( ),
which are overloaded to print out all the various types. The difference between
print( )
and
println( )
is that the latter adds a newline when it’s done.
is a modifier and tells the stream to use buffering so you don’t get a
physical write every time you write to the stream. You’ll probably always
want to use this with files, and possibly console IO.
10-4. Types of FilterOutputStream
Data-OutputStream
|
Used
in concert with DataInputStream so you can write primitives (int, char, long, etc.) to a stream in a portable fashion. |
OutputStream
|
Contains
full interface to allow you to write primitive types. |
||
PrintStream
|
For
producing formatted output. While DataOutputStream handles the storage of data, PrintStream handles display. |
OutputStream,
with optional boolean indicating that the buffer is flushed with every newline. |
Should
be the “final” wrapping for your OutputStream object. You’ll probably use this a lot. |
||
Buffered-OutputStream
|
Use
this to prevent a physical write every time you send a piece of data. You’re saying “Use a buffer.” You can call flush( ) to flush the buffer. |
OutputStream,
with optional buffer size. |
This
doesn’t provide an interface per se , just a requirement that a buffer is used. Attach an interface object. |