Exceptions: the finally block
In our explanation of the try/catch block we mentioned
the issue that we needed to ensure that a file was closed whether or not an exception occurred.
Java provides an extension to the try/catch block for such an eventuality. We
can use the following structure:
try {
...
} catch (IOException ioex) {
...
} finally {
...
}
With this structure, the code in the finally block will always be executed
whether or not an exception occurs within the given try block. So we can put
"cleanup" code such as closing a file inside the finally block.
We actually don't need a catch block with the finally: we can still
let exceptions be passed up to the caller.
In this case, we end up with a try/finally block.
For example, our method to read a number from a file can look like something this:
public int readNumber(File f) throws IOException, NumberFormatException {
BufferedReader br = new BufferedReader(new
InputStreamReader(new FileInputStream(f), "ASCII"));
try {
return Integer.parseInt(br.readLine());
} finally {
br.close();
}
}
What if an exception occurs in the finally block?
An issue for which there's no really neat solution is that code in the
finally block could itself throw an exception. In this case, the
exception in the finally block would be thrown from the exception
instead of any exception occurring inside the try block. Since
code in the finally block is intended to be "cleanup" code, we could decide
to treat exceptions occurring there as secondary, and to put an excplicit
catch:
public int readNumber(File f) throws IOException, NumberFormatException {
BufferedReader br = new BufferedReader(new
InputStreamReader(new FileInputStream(f), "ASCII"));
try {
return Integer.parseInt(br.readLine());
} finally {
try { br.close(); } catch (IOException e) {
// possibly log e
}
}
}
We might often choose to at least log the exception. In the case of
an exception occurring while closing an input stream1, this is one of the few
cases where it's fairly legitimate to just ignore the exception, or else take the
risk that it will override the main exception. (If you log the
exception to somewhere, then you would have a chance of catching cases where your
logic was wrong and an exception during closure turned out to be significant for
some reason.) For something as unlikely as an exception while closing an
input stream, "taking the risk" is generally my preferred solution,
favouring more succinct code.
More care needs to be taken during closure of an output stream,
since in this case pending data may actually be written before closure and an "important"
error is more likely. For different reasons, we may actually arrive at the same
code pattern: since data is potentially being written, we probably would allow an
exception on closure to be propagated by the method. The 'overriding' exception in the
finally clause is still likely to indicate the underlying cause of the
initial exception.
Some other things to note about finally blocks:
- The same 'overriding' problem that we mentioned with exceptions occurs when
returning a value from a finally block: this would override any return value
that the code in the try block wanted to return. In practice, returning a value from a
finally clause is rare and not recommended.
- Actually exiting the program (either by calling System.exit() or by
causing a fatal error that causes the process to abort: sometimes referred to informally
as a "hotspot" or "Dr Watson" in Windows) will prevent your finally block from being
executed!
- There's nothing to stop us nesting try/catch/finally blocks (for
example, putting a try/finally block inside a try/catch block, or vice versa),
and it's not such an uncommon thing to do.
Next...
Next, it is recommended that you take a look at our discussion of the
exception hierarchy, which also looks at the
difference between what are called checked and unchecked exceptions.
1. The unlikely cases where this would happen include closing network sockets, where
a final 'handshake' occurs across the network to close the connection, and potentially when
updating metadata (last access date etc) of a file on disk. But in either case, your program
would by that time have the data it needed, so probably wouldn't care terribly about the
error.
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.