Finally finally (block)
Let's talk about the
(amazon) specifies when the
(amazon) that a compilation of a finally statement occurs by creating a
finally block for a bit, as it holds a few "getchas" in the way it works.
The Java Language Specificationfinally block is called, and how the try block ends. In short, it means that the finally block will always occur no matter what happened in the try or any of the optional catch blocks. I will try to see how can this feature be used, touch a bit on how it is implemented by the compiler, and discuss some pitfalls this feature might bring to unexperienced programmers.
What is the finally block for?
Classically, thefinally block is used to close any resources that either can't be closed by the garbage collection (such as semaphores or resources ackquired using native code) or should be closed immediately and not wait for garbage collection to do it (such as database connections or file handlers). So, for example, one might write the following code:
Connection conn; // opened before, at the beginning of the method
try {
// execute a query
} catch (SQLException e) {
// handles the exception, dispalying a message, whatever
} finally {
conn.close();
}
This would ensure that no matter what happens, even if you return from the method in the try block, even if an exception was thrown, anything - The connection will be closed.
How does it work?
The Java Virtual Machine Specificationcatch block which catches all uncaught exceptions and calls the finally block as a sub-routine. Further, the try block itself is calling the finally block as well.
With my compiler I received different results, though - Either the book is outdated, or the Apple Java compiler works differently. My compiler just copies the finally block's code to the try block's, but leaves it uncovered in the exception table. For example, the following code, having count as a field:
try {
count = 1;
} finally {
count++;
}
Would be compiled to:
Code:
0: aload_0
1: iconst_1
2: putfield #2; //Field count:I
5: aload_0
6: dup
7: getfield #2; //Field count:I
10: iconst_1
11: iadd
12: putfield #2; //Field count:I
15: goto 31
18: astore_1
19: aload_0
20: dup
21: getfield #2; //Field count:I
24: iconst_1
25: iadd
26: putfield #2; //Field count:I
29: aload_1
30: athrow
31: return
Exception table:
from to target type
0 5 18 any
18 19 18 any
count++ line. The finally block is clearly copied. Line 15 prevents the code to be called twice by jumping to the end of the method, and returning from it. The exception table at the end gives a "catch-any" protection to the try block's code by catching any uncaught exception and jumping into the finally block if that occurs.
It might make one think that if there was an unchecked exception (division by zero, for example) right after the count++ (which would be copied as well), it would cause count to increment twice (as it would be incremented in the try block, and then in the finally block when the exception is caught). This will not happen though. If you take a look at the exception table at the end, you will notice that the copied finally block is not protected by a "catch-any" block. That would make an exception in the copied finally block to just be thrown normally, without entering the real finally block.
By the way, the code in line 18 and lines 29-30 are there to re-throw any uncaught exception in the try block that was accidently caught in the finally block.
Return values
If thefinally block always occurs, which takes precedence: The return value from the try block or the one from the finally block? The answer is simple and clean: The finally block always takes precedence. This can cause some pitfalls though, if you take the following example of a method which performs some database operation and returns whether it was successful:
try {
// update the database
return true;
} catch (SQLException e) {
// rolls-back
return false;
} finally {
// closes connection
return true;
}
Now, obviously the code was intended to return false in the event of an exception. However, because of the finally block, the code will return true! I would've actually imagined that it would be possible to not specify a return value and then the original return value will be kept, but that's not possible. However, as an aside, if there is a finally block you may leave unspecified return values in the try and any catch blocks.
Catch-all semantics
Since thefinally block occurs no matter what happened during the try block, it's possible that certain actions did no occur due to some NullPointerException. Since the finally block has no way of knowing it (unless you specify it using some out-of-scope variable) the finally block should not assume anything has happened since the beginning of the try block, otherwise, it might fail itself.
Well, hopefully I managed to clear some clouds over the finally statement. Let me know if there's anything I left uncovered!
Comments (11)