Input stream error handling in Java
On the previous page, we saw how to open an input stream in Java
and read sequential bytes from it. But we didn't look properly at error handling.
Many I/O-related methods in Java are declared as throwing IOException.
This is a checked exception (i.e. one that we must explicitly deal with), because
getting an error while reading from a file, network connection etc is generally a
foreseeable event. (A disadvantage is that we must also spuriously catch this exception
in cases where we basically know it will never be thrown, e.g. when reading from a
ByteArrayInputStream.)
If we are defining a utility method to perform a particular operation on a file, the most
common idiom is to throw IOException up: the caller probably wants to know that
there was a problem. In this example, we test to see if a particular file is a JPEG file
by seeing if the first four bytes are the letters "JFIF":
public boolean isJpegFile(File f) throws IOException {
InputStream in = new FileInputStream(f);
boolean ret = (in.read() == 'J' &&
in.read() == 'F' &&
in.read() == 'I' &&
in.read() == 'F');
in.close();
return ret;
}
Throwing IOException also makes the code a little neater as we'll see, because
of what happens when we come to close the file.
Closing the stream in a finally clause
In the isJpegFile() method above, a problem occurs if an I/O error occurs
while reading the file: we won't call close() on the stream. This wouldn't be a
total disaster: input streams are generally defined to close themselves in their
finalize methods. But it would certainly delay closing and the point
at which the stream was closed would be unpredictable.
We can solve this problem by putting the close() call in a finally clause:
public boolean isJpegFile(File f) throws IOException {
InputStream in = new FileInputStream(f);
try {
return (in.read() == 'J' &&
in.read() == 'F' &&
in.read() == 'I' &&
in.read() == 'F');
} finally {
in.close();
}
}
If an exception is thrown while opening the stream, we assume that there
is no stream to close, so in this example we leave the construction of the FileInputStream
outside of the try/catch block.
What isn't obvious in this code is that close() itself is declared to throw
IOException. So if we don't throw IOException up from our method, we actually
end up with two try/catch blocks:
public boolean isJpegFile(File f) throws IOException {
InputStream in = new FileInputStream(f);
try {
return (in.read() == 'J' &&
in.read() == 'F' &&
in.read() == 'I' &&
in.read() == 'F');
} finally {
try { in.close(); } catch (IOException ignore) {}
}
}
Although our method is declared to throw IOException, we need to catch
the exception inside the finally clause. Otherwise, if an exception occurred
on reading, then again on trying to close, the "more interesting" exception that
occurred on reading would be masked.
When would InputStream.close() throw an exception?
You may at this point be wondering when closing an input stream would actually cause an
error to be raised. In the case of closing a file, probably never in practice.
(Conceivably,
a file system could throw an error while updating "last access time" metadata, or some
other metadata, on a file during closure.)
However, in the case of an input stream reading from a network connection,
an error on closure is a bit more conceivable. A normal closure of a network socket actually involves
a closure request (TCP/IP FIN packet) being sent over the connection and waiting for the other
end to acknowledge this closure request. (In fact, the other end of the connection then in turn
sends a closure request, which the closing end acknowledges.) So in the case of a socket input stream,
a closure operation actually
involves sending traffic over the connection and the closure can thus fail with an error.
Note that in many implementations, close() generally doesn't throw an IOException if the
stream is already closed; it simply "fails silently" to close the stream again.
Next: buffering input
On the next page, we'll see how to use the convenience BufferedInputStream
class for buffering input streams.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.