An Integral Part of Exception Handling, Finally
By Paul King, OCI Senior Software Engineer
May 2000
The mechanics of the finally block
For Java developers, the try-catch block is a way of life. A fundamental part of this mechanism is the finally block.
As the name suggests, code in a finally block is always executed last.
A finally can appear optionally at the end of a try-catch block as shown:
- try {
- // Normal processing
- }
- catch ( Exception e ) {
- // Exception handling
- }
- finally {
- // Always executed once after all code in try-catch blocks completes
- }
Code in a finally block runs immediately after one of the following three things occurs:
- The
tryblock completes execution without throwing an exception. This occurs when the last statement in thetryblock completes, when a flow control statement likebreakorcontinueis encountered (provided they are associated with a loop controlled outside thetryblock), or areturnis executed. - The
tryblock throws an exception that is not handled by a correspondingcatchblock. In other words, there is nocatchblock associated with the exception that was thrown. - A corresponding
catchblock completes execution, even as a result of (another) exception being thrown from within thecatchblock.
So, finally will be executed once if no exceptions occur in the try block, or if multiple exceptions occur in the try-catch blocks.
This article focuses on the technical details of the finally block, and situations where it can be put to good use.
Note: Thetry-catchparadigm exists in C++, but that language does not supportfinally. The C++ destructor can serve a similar purpose as thefinallyblock in Java; however, for those of you who know C++, it should be clear that they are quite different animals.
When to use finally
finally blocks are useful when you want to make sure that particular processing always happens prior to completing a method or a block of code in a method.
To accomplish this in many other languages, multiple lines of redundant code are often necessary. Such redundant code can be prevalent when attempting to handle errors and can obscure the fundamentals of the processing. They are a ripe source of programming errors.
Error handling often involves freeing or releasing shared resources, such as sockets, CORBA objects, database connections, and files, and performing other clean-up operations.
The example below demonstrates how finally can be used to ensure that a socket obtained from a group (or pool) of sockets is released upon completion of a transaction.
By placing the release of the socket in the finally block, we are guaranteed that the resource will be freed for subsequent use. Even if an exception that we do not handle occurs in the try block, or if an exception is thrown while executing a catch block, the release will still occur.
The finally block provides a convenient means to buttress the try-catch with a defensive block to perform clean-up that needs to occur, whether we complete the transaction normally or fail as a result of an error.
We also include logging a diagnostic message in our finally block, since it is a convenient place to update an audit-trail.
Note: For the sake of brevity, the definition of the class that implements the socket pool (the pool member variable shown below), a member of the pool (the PooledSocket class shown below), and a file-logging utility (the log member shown below) are not included with this example. These classes may be included in a future article.- /** Writes request to a shared socket in a pool and places the reply in
- * response. Reply will not exceed the capacity of response.
- */
- void performTransaction(StringBuffer request, StringBuffer response)
- throws InterruptedIOException, IOException, InterruptedException {
-
- PooledSocket s=null; // This is the shared resource
- String diagMessage="An exception occurred."; // Default diagnostic message
-
- response.setLength(0); // Initialize to empty
-
- try {
- s=pool.acquireSocket(); // If all sockets are busy, this will block
- s.write(request);
- s.read(response);
- diagMessage=response.toString();
- }
- catch ( InterruptedIOException e ) {
- throw e; // finally will execute before the exception is passed along
- }
- catch ( InterruptedException e ) {
- throw e; // finally will execute before the exception is passed along
- }
- catch ( IOException e ) {
- throw e; // finally will execute before the exception is passed along
- }
- finally {
- pool.releaseSocket(s); // Release to the pool for subsequent use
- log("performTransaction", request.toString(), diagMessage);
- }
- }
Complications
One must be careful if completion of a try-catch block occurs as a result of executing a return. If a finally block also returns a value, then that return supercedes any previous return in the try-catch block.
Also, if an exception was thrown in the try or catch blocks that was not caught, then execution of a return in the finally block prevents the exception from being thrown to caller (because it is not possible for the caller to simultaneously evaluate the return and catch the exception).
Similarly, if an exception was thrown in the try-catch block, an exception thrown in the finally block will supercede the prior exception.
Due to these complications, it is probably a good idea to avoid returning a value or purposely throwing an exception in a finally block.
Summary
When implementing a try-catch block, consider the following:
- A
finallyblock always executes only once and always executes after all the code in the correspondingtry-catchblock completes. - A
returnexecuted in afinallyblock takes precedence over one executed in the correspondingtry-catchblock and over passing an exception along to the caller. - A exception thrown in a
finallyblock takes precedence over one thrown in the correspondingtry-catch block. - The
finallyblock is a good place to release shared resources, add defensive code, and audit trail logging.
Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.