This post looks at the syntax variations of the try/catch/finally blocks. For further details, see the excellent Java tutorial on the topic.
To start off, below is the basic syntax, with code surrounded by a try-block, and a NullPointerException caught by the catch-block. As can bee seen, the code will fail, since the variable “str” is null, leading to a NullPointerException. The Exception variable in the catch-block is by most common conventions simply “e”. It has a few convenience methods, including printStackTrace() which shows the call trace since the Exception was thrown. Although the print-out might look scary, it does provide useful information to the developer. Thus, keeping the full stack trace is helpful, typically in a detailed log-file. That is beyond the scope of this post.
Exceptions are typed classes, and in the following example the catch-block will not be reached since the expected Exception is not the same or a sub-type of the one which is thrown: NullPointerException vs. ArithmeticException. Instead, the ArithmeticException will be thrown out of the method.
To handle multiple exception types, there are three options: Declare multiple catch-blocks with different types, as seen in the first part below; or declare multiple Exceptions within the same catch statement, as in the second part. The latter syntax has been available since Java 7. Finally, it’s possible to catch multiple Exceptions by specifying a type higher up the class hierarchy, e.g. using Exception or Throwable.
In addition to the try and catch blocks, there is also a finally-block. It is executed at the end, regardless of whether there was an Exceptions thrown or not. This is useful for setting state or cleaning up, and a common example is closing an IO stream. However, as seen below, this can get crufty since we have to consider that the IO object might not have been opened in the first place, and that the close() method throws its own checked Exception.
To clean up the code above, the try-with-resources syntax was introduced in Java 7. It allows the try statement to take an extra block which is executed before its content. In addition, variables declared within this block will be closed at the end through the Closeable interface. This significantly reduces the foot-print of the code above.
A good example for multiple resources is the Socket example discussed previously. Here the Socket and both IO streams are closable resources handled by the try-block.
Finally, a word on messaging and wrapping of Exceptions. As mentioned in the tutorial, it’s poor practice to throw RuntimeExceptions or simply wrap checked Exceptions, as seen below. However, regardless of where you stand in that debate, Exceptions can always be made more helpful and useful by clear messaging and relevant context. The wrapped RuntimeException below adds a more specific message and also includes the filename the IO stream operates on, since it might not be included in all types of IOExceptions. Furthermore, in the case of the File object, is is useful to use the getAbsolutePath() method. It forces the full path to the resolved and included. It really helps when debugging issues where the full path can be copy/pasted and confirmed.
Here is the full listing with all examples as tests.