An Integral Part of Exception Handling, Finally

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:

  1. try {
  2. // Normal processing
  3. }
  4. catch ( Exception e ) {
  5. // Exception handling
  6. }
  7. finally {
  8. // Always executed once after all code in try-catch blocks completes
  9. }

Code in a finally block runs immediately after one of the following three things occurs:

  1. The try block completes execution without throwing an exception. This occurs when the last statement in the try block completes, when a flow control statement like break or continue is encountered (provided they are associated with a loop controlled outside the try block), or a return is executed.
  2. The try block throws an exception that is not handled by a corresponding catch block. In other words, there is no catch block associated with the exception that was thrown.
  3. A corresponding catch block completes execution, even as a result of (another) exception being thrown from within the catch 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: The try-catch paradigm exists in C++, but that language does not support finally. The C++ destructor can serve a similar purpose as the finally 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.
  1. /** Writes request to a shared socket in a pool and places the reply in
  2.   * response. Reply will not exceed the capacity of response.
  3.  */
  4. void performTransaction(StringBuffer request, StringBuffer response)
  5. throws InterruptedIOException, IOException, InterruptedException {
  6.  
  7. PooledSocket s=null; // This is the shared resource
  8. String diagMessage="An exception occurred."; // Default diagnostic message
  9.  
  10. response.setLength(0); // Initialize to empty
  11.  
  12. try {
  13. s=pool.acquireSocket(); // If all sockets are busy, this will block
  14. s.write(request);
  15. s.read(response);
  16. diagMessage=response.toString();
  17. }
  18. catch ( InterruptedIOException e ) {
  19. throw e; // finally will execute before the exception is passed along
  20. }
  21. catch ( InterruptedException e ) {
  22. throw e; // finally will execute before the exception is passed along
  23. }
  24. catch ( IOException e ) {
  25. throw e; // finally will execute before the exception is passed along
  26. }
  27. finally {
  28. pool.releaseSocket(s); // Release to the pool for subsequent use
  29. log("performTransaction", request.toString(), diagMessage);
  30. }
  31. }

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:



Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.