A stream is a queue of bytes or characters that connects a data source to a data sink:
Technically, both source and sink are programs. For example, the source might be an application and the sink might be a file system manager or a driver that controls some output device such as a disk, printer, or monitor. As another example, both source and sink might be applications. In this case we refer to the stream as a pipe.
The source places data (bytes or characters) into the stream using a special non-blocking write operation:
stream.write(data)
The sink extracts data using a special read operation:
data = stream.read();
The read operation blocks until input data is available, the end of the stream is detected, or an exception is thrown.
If an input stream has an end, it is marked by a special eof sentinel value.
In the following diagram a program has streams connecting it to six "devices". The program plays the role of sink for D1 and D2, the role of source for D4 and D5, and is both a source and a sink for D3 and D6. The devices might be keyboards, modems, other programs, files, or arrays (or strings) inside of the program itself (this is the case with D1 and D4).
We can treat Streams as objects. Here is a hypothetical type hierarchy:
Java provides abstract InputStream and OutputStream classes for representing byte streams. The hierarchies are parallel, so only the input stream classes will be discussed.
The InputStream class specifies an abstract method for reading a single byte from a data source. It also implements a buffered read operation.
public class InputStream {
// returns byte read or -1:
public abstract int read() throws
IOException;
public int read(byte[] b) throws
IOException {
// read bytes, store in b, return count
}
// etc.
}
Java provides concrete subclasses of InputStream that implement the abstract read operation in various ways. For example, the FileInputStream class would implement the read operation so that it extracted a single byte from a file.
Java uses the Decorator Design Pattern to allow users to wrap an input stream with layers of filters that add features to the basic operations. The role of the decorator is played by the FilterInputStream class:
public class FilterInputStream extends FileInputStream {
protected InputStream in;
public FilterInputStream(InputStream
is) { in = is; }
public int read() throws IOException {
return in.read();
}
}
Java provides a variety of filters for doing things like converting between Java values and bytes.
For character strings Java provides separate hierarchies rooted by the Reader and Writer classes. These hierarchies also use the Decorator Design Pattern. Special InputStreamReader and OutputStreamWriter subclasses serve as bridges to the corresponding byte streams. Here's a fragment of the Reader hierarchy:
The standard C++ Stream Library provides input stream, output stream, and input-output stream classes. These are all character-oriented streams.
In addition, C++ provides file and string subclasses of each of these types of streams. A file stream reads and/or writes characters to a file. A string stream reads and/or writes characters to a C++ string.
Officially, a stream consists of three parts: a translator, a specification, and a stream buffer.
The stream buffer encapsulates and manages an array of characters (i.e. the character buffer) waiting to be transferred to the program or to the output sink.
The specification maintains the status and format state of the stream as well as a pointer to the stream buffer.
A translator converts characters to and from C++ types such as int, float, boolean as well as any object type that overides the appropriate translator methods.
Stream buffers are instances of the streambuf class, istream and ostream instances are translators, and specifications are instances of the ios class.
Oddly, the designers of the stream library picked specialization as the relationship between translators and specifiers.