Performing
cleanup
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.
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?
In
a language without garbage collection
and
without automatic destructor
calls,
[45]
finally
is important because it allows the programmer to guarantee the release of
memory regardless of what happens in the try
block. But Java has garbage collection, so releasing memory is virtually never
a problem. Also, it has no destructors to call. So when do you need to use finally
in Java?
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
In
general, Java’s exception implementation is quite outstanding, but
unfortunately there’s a flaw. Although exceptions are an indication of a
crisis in your program and should never be ignored, it’s possible for an
exception to simply be lost.
This happens with a particular configuration using a finally
clause:
//: 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();
}
}
} ///:~
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).