Main Page

Previous Next

The Classes for Input and Output

There are quite a number of stream classes, but as you will see, they form a reasonably logical structure. Once you see how they are related, you shouldn't have much trouble using them. We will work through the class hierarchy from the top down, so you will be able to see how the classes hang together, and how you can combine them in different ways to suit different situations.

The package contains the classes that provide the foundation for Java's support for stream I/O:




The base class for byte stream input operations.


The base class for byte stream output operations.

InputStream, and OutputStream are both abstract classes. As you are well aware by now, you cannot create instances of an abstract class – these classes only serve as a base from which to derive classes with more concrete input or output capabilities. However, both of the classes declare methods that define a basic set of operations for the streams they represent, so the fundamental characteristics of how a stream is accessed are set by these classes. Generally, the InputStream and OutputStream classes, and their subclasses, represent byte streams and provide the means of reading and writing binary data.

Basic Input Stream Operations

As we saw in the previous section, the InputStream class is abstract, so you cannot create objects of this class type. Nonetheless, input stream objects are often accessible via a reference of this type, so the methods identified in this class are what you get. The InputStream class includes three methods for reading data from a stream:




This method is abstract in the InputStream class, so it has to be defined in a subclass. The method returns the next byte available from the stream as type int. If the end of the stream is reached, the method will return the value -1. An exception of type IOException will be thrown if an I/O error occurs.

read(byte[] array)

This method reads bytes from the stream into successive elements of array. The maximum of array.length bytes will be read. The method will not return until the input data is read, or the end of the stream is detected. The method returns the number of bytes read, or -1 if no bytes were read because the end of the stream was reached. If an I/O error occurs, an exception of type IOException will be thrown. If the argument to the method is null then a NullPointerException will be thrown. An input/output method that does not return until the operation is completed is referred to as a blocking method, and you say that the methods blocks until the operation is complete.

read(byte[] array, int offset, int length)

This works in essentially the same way as the previous method, except that up to length bytes are read into array starting with the element array[offset].

The data is read from the stream by these methods simply as bytes. No conversion is applied to the bytes read. If any conversion is required, for a stream containing bytes in the local character encoding for instance, you must provide a way to handle this. We will see how this might be done in a moment.

You can skip over bytes in an InputStream by calling its skip() method. You specify the number of bytes to be skipped as an argument of type long, and the actual number of bytes skipped is returned, also a value of type long. This method can throw an IOException if an error occurs.

You can close an InputStream by calling its close() method. Once you have closed an input stream, subsequent attempts to access or read from the stream will cause an IOException to be thrown.

The InputStream class has seven direct subclasses as shown in the diagram below.

Click To expand

We will be using the FileInputStream class in Chapter 10 Writing Files, and the ObjectInputStream class in Chapter 11 Reading Files.

The FilterInputStream class has a further nine direct subclasses that provide more specialized ways of filtering or transforming data from an input stream. We will only be using the BufferedInputStream class, but here's the complete set with an indication of what each of them does:


Buffers input from another stream in memory to make the read operations more efficient.


Reads data of primitive types from a binary stream.


Reads an input stream and maintains a checksum for the data that is read to verify its integrity.


Reads data from an encrypted input stream.


Reads data from an input stream and updates an associated message digest. A message digest is a mechanism for combining an arbitrary amount of data from a stream into a fixed length value that can be used to verify the integrity of the data.


Reads data from a stream that has been compressed, such as a ZIP file for example.


Reads data from a stream and keeps track of the current line number. The line number starts at 0 and is incremented each time a newline character is read.


Reads data from an input stream and uses a progress-monitor to monitor reading the stream. If reading the stream takes a significant amount of time, a progress dialog will be displayed offering the option to cancel the operation. This is used in window-based applications for operations that are expected to be time consuming.


Adds the capability to return the last byte that was read back to the input stream so you can read it again.

You can create a BufferedInputStream object from any other input stream, since the constructor accepts a reference of type InputStream as an argument. The BufferedInputStream class overrides the methods inherited from InputStream. For instance, in the following example:

BufferedInputStream keyboard = new BufferedInputStream(;

the argument,, is the static member of the System class that encapsulates input from the keyboard, and is of type InputStream. We will be looking into how we can best read input from the keyboard a little later in this chapter.

The effect of wrapping a stream in a BufferedInputStream object is to buffer the underlying stream in memory so that data can be read from the stream in large chunks – up to the size of the buffer provided. The data is then made available to the read() methods directly from memory, only executing a real read operation from the underlying stream when the buffer is empty. With a suitable choice of buffer size, the number of input operations that are needed will be substantially reduced, and the process will be a whole lot more efficient. The reason for this is that for most input streams, each read operation carries quite a bit of overhead, beyond the time required to actually transfer the data. In the case of a disk file for instance, the transfer of data from the disk to memory can only start once the read/write head has been positioned over the track that contains the data and the disk has rotated to the point where the read/write head is over the point in the track where the data starts. This delay before the transfer of data begins will typically be several milliseconds and will often be much longer than the time required to transfer the data. Thus by minimizing the number of separate read operations that are necessary you can substantially reduce to total time needed to read a significant amount of data.

The buffer size that you get by default when you call the constructor in the previous code fragment is 2048 bytes. This will be adequate for most situations where modest amounts of data are involved. The BufferedInputStream class also defines a constructor that accepts a second argument of type int that enables you to specify the size in bytes of the buffer to be used.

Deciding on a suitable size for a buffer is a matter of judgment. You need to think about how the buffer size will affect operations in your program. The total amount of data involved as well as the amount that you need to process at one time also come into it. For instance, you will usually want to choose a buffer size that is a multiple of the amount of data that your program will request in each read operation. Suppose you expect your program to read and process 600 bytes at a time for instance. In this case you should choose a buffer size that is a multiple of 600 bytes. The multiple, and therefore the total buffer size, is a balance between the amount of memory required for the buffer and its effect on the efficiency of your program. If you expect to be processing 100 sets of data, each of 600 bytes, you might settle on a buffer size of 6000 bytes as a reasonable choice. Each buffer-full would then consist of 10 sets of data, and there would only need to be ten physical read operations to refill the buffer.

Basic Output Stream Operations

The OutputStream class contains three write() methods for writing binary data to the stream. As can be expected, these mirror the read() methods of the InputStream class. This class is also abstract, so only subclasses can be instantiated. The principal direct subclasses of OutputStream are shown in the diagram below:

Click To expand

We will be using the FileOutputStream class that is derived from OutputStream when we write files in the next chapter, and will investigate the methods belonging to the ObjectOutputStream class in Chapter 12 Serializing Objects, when we learn how to write objects to a file.

Note that this is not the complete set of output stream classes. The FilterOutputStream has a further seven subclasses, including the BufferedOutputStream class that does for output streams what the BufferedInputStream class does for input streams. There is also the PrintStream class, which we will be looking at a little later in this chapter, since output to the command line is via a stream object of this type.

Stream Readers and Writers

Stream readers and writers are objects that can read and write byte streams as character streams. So a character stream is essentially a byte stream fronted by a reader or a writer. The base classes for stream readers and writers are:




The base class for reading a character stream.


The base class for writing a character stream.

Reader and Writer are both abstract classes. The Reader and Writer classes and their subclasses are not really streams themselves, but provide the methods you can use for reading and writing binary streams as character streams. Thus, a Reader or Writer object is typically created using an underlying InputStream or OutputStream object that encapsulates the connection to the external device, which is the ultimate source or destination of the data.

Using Readers

The Reader class has the direct subclasses shown in the diagram below:

Click To expand

The concrete class that you would use to read a binary input stream as a character stream is InputStreamReader. You could create an InputStreamReader object like this:

InputStreamReader keyboard = new InputStreamReader(;

The parameter to the InputStreamReader constructor is of type InputStream, so you can pass an object of any class derived from InputStream to it. The sample above creates an InputStreamReader object, keyboard, from the object, the keyboard input stream.

The InputStreamReader class defines three varieties of read() method that will read one or more bytes from the underlying stream, and return them as Unicode characters, using the default conversion from the local character encoding. There are also two further constructors for InputStreamReader objects that will convert data from the stream using a charset that you specify.

Of course, it would be much more efficient if you buffered the stream using a BufferedReader object like this:

BufferedReader keyboard = new BufferedReader(new InputStreamReader(;

Here, we wrap an InputStreamReader object around, and then buffer it using a BufferedReader object. This will make the input operations much more efficient.

A CharArrayReader object is created from an array and enables you to read data from the array as though it is from a character input stream. A StringReader object class does essentially the same thing, but with a String object.

Using Writers

The main subclasses of the Writer class are as shown below:

Click To expand

We will just look at a few details of the most commonly used of these classes.

The OutputStreamWriter class writes characters to an underlying binary stream. It also has a subclass, FileWriter that writes characters to a stream encapsulating a file. Both of these are largely superseded by the new I/O facilities that we will explore starting in the next chapter.

Note that the PrintWriter class has no particular relevance to printing, in spite of its name. The PrintWriter class defines methods for formatting binary data as characters, and writing it to a stream. It defines overloaded print() and println() methods that accept an argument of each of the basic data types, of type char[], of type String, and of type Object. The data that is written is a character representation of the argument. Numerical values and objects are converted to a string representation using the static valueOf() method in the String class. There are overloaded versions of this method for all of the primitive types plus type Object. In the case of an argument that is an Object reference, the valueOf() method just calls the toString() method for the object to produce the string to be written to the stream. The print() methods just write the string representation of the argument whereas the println() method appends \n to the output. You can create a PrintWriter object from a stream or from another Writer object.

An important point to note when using a PrintWriter object is that its methods do not throw I/O exceptions. To determine whether any I/O errors have occurred, you have to call the checkError() method for the PrintWriter object. This method will return true if an error occurred and false otherwise.

The StringWriter and CharArrayWriter classes are for writing character data to a StringBuffer object, or an array of type char[]. You would typically use these to perform data conversions so that the results are available to you from the underlying array, or string. For instance, you could combine the capabilities of a PrintWriter with a StringWriter to obtain a String object containing binary data converted to characters:

StringWriter strWriter = new StringWriter();
PrintWriter writer = new PrintWriter(strWriter);

Now you can use the methods for the writer object to write to the StringBuffer object underlying the StringWriter object:

double value = 2.71828;

You can get the result back as a StringBuffer object from the original StringWriter object:

StringBuffer str = strWriter.getBuffer();

Of course, the formatting done by a PrintWriter object does not help make the output line up in neat columns. If you want that to happen, you have to do it yourself. We'll take a look at how we might do this for command line output a little later in this chapter.

Let's now turn to keyboard input and command line output.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor