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:

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.