Performing cleanup with finally

Bruce Eckel’s Thinking in Java Contents | Prev | Next

with
finally

There’s
often some piece of code that you want to execute whether or not an exception
occurs in a
try
block. This usually pertains to some operation other than memory recovery
(since that’s taken care of by the garbage collector). To achieve this
effect, you use a finally
clause
[44]
at the end of all the exception handlers. The full picture of an
exception-handling section is thus:

try
{

// The guarded region:

// Dangerous stuff that might throw A, B, or C

}
catch
(A
a1) {

// Handle A

}
catch
(B b1) {

// Handle B

}
catch
(C c1) {

// Handle C

}
finally
{

// Stuff that happens every time

}

To
demonstrate that the
finally
clause always runs, try this program:

//: FinallyWorks.java
// The finally clause is always executed
 
public class FinallyWorks {
  static int count = 0;
  public static void main(String[] args) {
    while(true) {
      try {
        // post-increment is zero first time:
        if(count++ == 0)
          throw new Exception();
        System.out.println("No exception");
      } catch(Exception e) {
        System.out.println("Exception thrown");
      } finally {
        System.out.println("in finally clause");
        if(count == 2) break; // out of "while"
      }
    }
  }
} ///:~ 

This
program also gives a hint for how you can deal with the fact that exceptions in
Java (like exceptions in C++) do not allow you to resume back to where the
exception was thrown, as discussed earlier. If you place your
try
block in a loop, you can establish a condition that must be met before you
continue the program. You can also add a
static
counter or some other device to allow the loop to try several different
approaches before giving up. This way you can build a greater level of
robustness into your programs.

The
output is:

Exception thrown
in finally clause
No exception
in finally clause

Whether
an exception is thrown or not, the
finally
clause is always executed.

What’s
finally for?

finally
is necessary when you need to set something
other
than memory back to its original state. This is usually something like an open
file or network connection, something you’ve drawn on the screen or even
a switch in the outside world, as modeled in the following example:

//: OnOffSwitch.java
// Why use finally?
 
class Switch {
  boolean state = false;
  boolean read() { return state; }
  void on() { state = true; }
  void off() { state = false; }
}
 
public class OnOffSwitch {
  static Switch sw = new Switch();
  public static void main(String[] args) {
    try {
      sw.on();
      // Code that can throw exceptions...
      sw.off();
    } catch(NullPointerException e) {
      System.out.println("NullPointerException");
      sw.off();
    } catch(IllegalArgumentException e) {
      System.out.println("IOException");
      sw.off();
    }
  }
} ///:~ 

The
goal here is to make sure that the switch is off when
main( )
is completed, so
sw.off( )
is placed at the end of the try block and at the end of each exception handler.
But it’s possible that an exception could be thrown that isn’t
caught here, so
sw.off( )
would
be missed. However, with
finally
you can place the closure code from a try block in just one place:

//: WithFinally.java
// Finally Guarantees cleanup
 
class Switch2 {
  boolean state = false;
  boolean read() { return state; }
  void on() { state = true; }
  void off() { state = false; }
}
 
public class WithFinally {
  static Switch2 sw = new Switch2();
  public static void main(String[] args) {
    try {
      sw.on();
      // Code that can throw exceptions...
    } catch(NullPointerException e) {
      System.out.println("NullPointerException");
    } catch(IllegalArgumentException e) {
      System.out.println("IOException");
    } finally {
      sw.off();
    }
  }
} ///:~ 

Here
the
sw.off( )
has been moved to just one place, where it’s guaranteed to run no matter
what happens.

Even
in cases in which the exception is not caught in the current set of
catch
clauses,
finally
will be executed before the exception-handling mechanism continues its search
for a handler at the next higher level:

//: AlwaysFinally.java
// Finally is always executed
 
class Ex extends Exception {}
 
public class AlwaysFinally {
  public static void main(String[] args) {
    System.out.println(
      "Entering first try block");
    try {
      System.out.println(
        "Entering second try block");
      try {
        throw new Ex();
      } finally {
        System.out.println(
          "finally in 2nd try block");
      }
    } catch(Ex e) {
      System.out.println(
        "Caught Ex in first try block");
    } finally {
      System.out.println(
        "finally in 1st try block");
    }
  }
} ///:~ 

The
output for this program shows you what happens:

Entering first try block
Entering second try block
finally in 2nd try block
Caught Ex in first try block
finally in 1st try block

The
finally
statement will also be executed in situations in which
break
and
continue
statements are involved. Note that, along with the labeled
break
and labeled
continue,
finally
eliminates the need for a
goto
statement
in Java.

Pitfall:
the lost exception

//: LostMessage.java
// How an exception can be lost
 
class VeryImportantException extends Exception {
  public String toString() {
    return "A very important exception!";
  }
}
 
class HoHumException extends Exception {
  public String toString() {
    return "A trivial exception";
  }
}
 
public class LostMessage {
  void f() throws VeryImportantException {
    throw new VeryImportantException();
  }
  void dispose() throws HoHumException {
    throw new HoHumException();
  }
  public static void main(String[] args)
      throws Exception {
    LostMessage lm = new LostMessage();
    try {
      lm.f();
    } finally {
      lm.dispose();
    }
  }
} ///:~ 

The
output is:

A trivial exception
        at LostMessage.dispose(LostMessage.java:21)
        at LostMessage.main(LostMessage.java:29)

You
can see that there’s no evidence of the
VeryImportantException,
which is simply replaced by the
HoHumException
in the
finally
clause. This is a rather serious pitfall, since it means that an exception can
be completely lost, and in a far more subtle and difficult-to-detect fashion
than the example above. In contrast, C++ treats the situation in which a second
exception is thrown before the first one is handled as a dire programming
error. Perhaps a future version of Java will repair the problem. (The above
results were produced with Java 1.1.)


[44]
C++ exception handling does not have the
finally
clause because it relies on destructors to accomplish this sort of cleanup.

[45]
A destructor is a function that’s always called when an object becomes
unused. You always know exactly where and when the destructor gets called. C++
has automatic destructor calls, but Delphi’s Object Pascal versions 1 and
2 do not (which changes the meaning and use of the concept of a destructor for
that language).

More by Author

Must Read