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.
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 there are extra helper classes that we can "wrap around" an input stream if we want to process the bytes in a different way, for example as text.
Various subclasses of InputStream exist, depending on where we want to read our data from. Here are some examples:
| Data source | Type of input stream (InputStream subclass) | How to obtain |
|---|---|---|
| a file | FileInputStream | new FileInputStream(f) |
| an entry in a zip file | ZipFileInputStream (Internal class to ZipFile) | new ZipFile(f).getInputStream(entry) |
| a network socket | SocketInputStream | new Socket(...).getInputStream() |
| a network source referred to by URL | Depends on the protocol | new URL(url).openStream() |
| a byte array | ByteArrayInputStream | new ByteArrayInputStream(b) |
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.
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:
| Arguments | Action | Return value | End 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 |
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:
Written by Neil Coffey. Copyright © Javamex UK 2008. All rights reserved.