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
try
block completes execution without throwing an exception. This occurs when the last statement in thetry
block completes, when a flow control statement likebreak
orcontinue
is encountered (provided they are associated with a loop controlled outside thetry
block), or areturn
is executed. - The
try
block throws an exception that is not handled by a correspondingcatch
block. In other words, there is nocatch
block associated with the exception that was thrown. - A corresponding
catch
block completes execution, even as a result of (another) exception being thrown from within thecatch
block.
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-catch
paradigm exists in C++, but that language does not supportfinally
. The C++ destructor can serve a similar purpose as thefinally
block 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
finally
block always executes only once and always executes after all the code in the correspondingtry-catch
block completes. - A
return
executed in afinally
block takes precedence over one executed in the correspondingtry-catch
block and over passing an exception along to the caller. - A exception thrown in a
finally
block takes precedence over one thrown in the correspondingtry-catch block
. - The
finally
block 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.