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?

//: 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).



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Savvy enterprises are discovering that the cloud holds the power to transform IT processes and support business objectives. IT departments can use the cloud to redefine the continuum of development and operations—a process that is becoming known as DevOps. Download the Executive Brief DevOps: Why IT Operations Managers Should Care About the Cloud—prepared by Frost & Sullivan and sponsored by IBM—to learn how IBM SmartCloud Application services provide a robust platform that streamlines …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds