Introduction to Java InputStreams

One of the most fundamental components of the java.io package (and indeed of all the Java I/O packages) is the InputStream class. Whether reading from a normal file, network socket, memory or a compressed stream of data, the basic point of call is often some subclass of InputStream.

What is an InputStream?

An InputStream is a reference to source of data (be it a file, network connection etc), that we want to process as follows:

We'll see later that if we want to read the bytes as characters, then we can "wrap" the InputStream in a Reader to decode the bytes into characters.

Getting and opening an input stream

Various subclasses of InputStream exist, depending on where we want to read our data from. Here are some examples:

Data sourceType of input stream
(InputStream subclass)
How to obtain
a fileFileInputStreamnew FileInputStream(f)
an entry in a zip fileZipFileInputStream
(Internal class to ZipFile)
new ZipFile(f).getInputStream(entry)
a network socketSocketInputStreamnew Socket(...).getInputStream()
a network source referred to by URLDepends on the protocolnew URL(url).openStream()
a byte arrayByteArrayInputStreamnew ByteArrayInputStream(b)
Example types of InputStream in the standard Java libraries.

Note that in Java there's not really a notion of "opening" an InputStream itself. Once you have constructed an InputStream in one way or another, then it is assumed that the underlying data source (e.g. file) has already been opened if necessary, or that this will happen when you attempt to read the first byte.

On the other hand, we'll see that it is generally important to close the stream once we're done with it. The InputStream class provides the close() method which we need to make sure we call once we're done with the stream. We'll come back to this point in more detail when we consider input stream error handling.

Reading bytes from the stream

Once we have obtained our stream, probably using one of the methods in the table above, then we can call one of the read() methods defined by InputSteam:

ArgumentsActionReturn valueEnd of stream return value
None Read a single byte The next unsigned byte in the file, as an int. -1
a byte array Read bytes from the stream into the array, limited by availability and array size Number of bytes read into the array
a byte array, offset and number of bytes to read Read bytes from the stream into the array, starting at the given offset and limited by availability and number of bytes specified Number of bytes read into the array
Different read() methods provided by InputStream.

So for example, to read consecutive bytes from a file, we can write something as follows:

import java.io.*;

File f = new File(dir, filename);
InputStream in = new FileInputStream(f);
int b;
do {
  b = in.read();
  if (b != -1) {
    System.out.println("The next byte is " + b);
  }
} while (b != -1);
in.close();

In real life, we can write the loop above a bit more succinctly and idiomatically as follows:

int b;
while ((b = in.read()) != -1) {
  ...
}

Notice that although we know that we're dealing specifically with a FileInputStream, we always refer to in simply as an instance of InputStream. It is generally good practice to refer only to the most specific interface or base class that we actually need: our routine will then work as is for any type of input stream. Plus in some cases, for example reading from a zip file entry, we might not actually know (or have any reason to care) what the specific flavour of InputStream actually is.

The code above will get us the bytes out of the file (or other stream) in a very basic way. It does have some problems, however, that we'll need to deal with on the following pages: