Java Forum / General / August 2007
what's the point in finally?
Crouchez - 07 Aug 2007 13:19 GMT try{ //blah } catch(Exception ex){ // } finally{ //do something }why not justtry{//blah}catch(Exception e){}//do something
bugbear - 07 Aug 2007 13:32 GMT > try{ > //blah } > catch(Exception ex){ > // } > finally{ //do something }why not justtry{//blah}catch(Exception > e){}//do something What if your exception handling code throws an Exception?
BugBear
Michael Jung - 07 Aug 2007 13:47 GMT > > try{ > > //blah } [quoted text clipped - 4 lines] > > What if your exception handling code throws an Exception? In fact, if you throw an exception in the finally block you have a much toughre problem at hand. finally is used, e.g., to free resources in the good and the bad case. Or implement some code that should be executed regardless of whether you want the caller to get an exception or not.
Consider try blocks without catch but with finally or whithout finally but catch.
Michael
Eric Sosman - 07 Aug 2007 13:47 GMT > try{ > //blah } > catch(Exception ex){ > // } > finally{ //do something }why not justtry{//blah}catch(Exception > e){}//do something For one thing, the finally clause will execute no matter how the try-ed code terminates: By throwing an Exception, by throwing some other kind of Throwable, by return-ing or by break-ing or by running all the way to its end -- the code in the finally clause will be executed regardless.
Another point is that it is almost always wrong to catch Exception: You should catch IOException or InterruptedException or some other, more specific kind of condition (or conditions). The code in a catch clause is supposed to "deal with" the exceptional condition, which means you need to have anticipated the possibility and written code expressly for that outcome. It's just not possible to "deal" effectively with every possible kind of Throwable, so it's pretty much a given that some kinds of Throwable will not be caught. Would you like your cleanup code to execute anyhow? Then put it in the finally clause.
Still another issue: Once you catch an Exception, that's the end of the line; Java's processing of it ceases once it's caught. So what do you do if you'd like to clean up "en passant" but let the original Exception propagate outward to your caller? You could re-throw the same Exception after cleaning up, or you could wrap the caught Exception in a new Exception of your own and throw that one instead, but both courses are clumsy. (The second is sometimes forced upon you for other reasons.) Put your cleanup code in the finally clause and you get exactly the desired effect without the extra running around.
 Signature Eric Sosman esosman@ieee-dot-org.invalid
Roedy Green - 07 Aug 2007 14:06 GMT what if you throw an exception you DON'T catch?
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Crouchez - 07 Aug 2007 14:07 GMT Scenario A
try{ //blah } catch(Exception e){ } finally{ //do something }
why not just...
Scenario B
try{ //blah }catch(Exception e){ } //do something
OK right. I'm not following the arguments here. The //do something will always be called in both scenarios unless something isn't returned in the catch method? I don't understand why there is more use of resources either in Scenario B? So what's the use of finally?
Patricia Shanahan - 07 Aug 2007 14:30 GMT > Scenario A > [quoted text clipped - 21 lines] > catch method? I don't understand why there is more use of resources either > in Scenario B? So what's the use of finally? In the second scenario //do something will not be called if, for example, there was a stack overflow, because StackOverflowError does not extend Exception.
Also consider the case in which //blah contains a return statement. Code immediately after the try-catch-finally will not get executed. The finally block will.
Second, catching and hiding Exception is such a dangerous thing to do that I don't really care whether finally is or is not needed in such code. I'm much more interested in:
try{ // blah } catch(SomeException e){ // Do something about it } catch(AnotherException e){ // Do something else } finally{ // Clean up after blah }
In this, much more realistic, case there may be exceptions that do not get caught, and with non-trivial catch blocks the catch blocks themselves may return or throw exceptions of their own. As before, none of the errors are being caught, and blah may contain a return statement.
Most of the very, very few gotos I wrote when I was programming in C were substitutes for finally - situations in which there were a lot of logical returns, but some clean-up code that had to be run on the way out, regardless of why the function was finishing.
Patricia
Crouchez - 07 Aug 2007 15:53 GMT >> Scenario A >> [quoted text clipped - 25 lines] > example, there was a stack overflow, because StackOverflowError does not > extend Exception. are you sure? whether code throws an exception/error/throwable or not it will still get to //do something in both scenarios. With StackOverflowError or OutofMemoryError then the program often stops completely.
> Also consider the case in which //blah contains a return statement. Code > immediately after the try-catch-finally will not get executed. The > finally block will. Ahh i see this point. I thought return was the ultimate last in the chain - finally does indeed get called before return but i can't think of any reason to do that.
Crouchez - 07 Aug 2007 16:10 GMT >>> Scenario A >>> [quoted text clipped - 38 lines] > chain - finally does indeed get called before return but i can't think of > any reason to do that. you're right with the StackOverflowError or OutofMemoryError thing - finally does get called. Cheers for that
Patricia Shanahan - 07 Aug 2007 16:15 GMT >>> Scenario A >>> [quoted text clipped - 28 lines] > will still get to //do something in both scenarios. With StackOverflowError > or OutofMemoryError then the program often stops completely. I am sure. This program:
public class StackOverflowTest { public static void main(String[] args) { try { try { badRecursion(); } catch (Exception e) { System.out.println("Exception caught"); } finally { System.out.println("In finally block"); } System.out.println("After try-catch-finally"); } catch (StackOverflowError e) { System.out.println("Caught " + e); } } static void badRecursion() { badRecursion(); } }
prints:
In finally block Caught java.lang.StackOverflowError
It does not reach the "After try-catch-finally" printout.
In many cases a program could in theory continue after an Error, but it is usually not a good idea. Error implies that something is VERY wrong.
>> Also consider the case in which //blah contains a return statement. Code >> immediately after the try-catch-finally will not get executed. The [quoted text clipped - 3 lines] > finally does indeed get called before return but i can't think of any reason > to do that. finally allows you to write return statements where they logically belong and yet be sure the method's clean-up code runs.
When I saw try-catch-finally, I knew I would not miss "goto". The few goto's I wrote in C were all emulating a finally structure, making sure that no matter how I completed a function I would do the cleanup.
Patricia
bugbear - 07 Aug 2007 16:21 GMT >> Also consider the case in which //blah contains a return statement. Code >> immediately after the try-catch-finally will not get executed. The [quoted text clipped - 3 lines] > finally does indeed get called before return but i can't think of any reason > to do that. I suggest you go back and read Patricia's comments (and code) after she says "I'm much more interested in: "
It's good stuff, and will help you.
BugBear
Daniel Pitts - 07 Aug 2007 17:08 GMT On Aug 7, 7:53 am, "Crouchez" <b...@bllllllahblllbllahblahblahhh.com> wrote:
> >> Scenario A > [quoted text clipped - 37 lines] > finally does indeed get called before return but i can't think of any reason > to do that. One typical use case: try { return parseSomeXml(); } catch (ParserException e) { throw new MyGenericException("Can parse xml", e); } catch (IOException e) { throw new MyGenericException("IO problem loading XML", e); } finally { cleanUpStuff(); }
Thomas Hawtin - 07 Aug 2007 17:45 GMT > One typical use case: > try { [quoted text clipped - 6 lines] > cleanUpStuff(); > } The trouble is that cleanUpStuff is usually declared to throw an exception. Therefore code like that is often incorrect or messy.
My guess is that the evolution of Java went something like:
o Start with C++ where resources are flushed explicitly and destructor do not throw. If you throw an exception from a destructor and the destructor is called due to an exception unwinding the stack then, IIRC, abort is called which by default calls terminate.
o This makes try-catch-finally relatively natural. I believe catch (...) logically comes after catches with explicit exception types in C++. I guess catch-before-finally can also allow the exception object to depend upon the resource still being open (though generally a poor idea).
o Java goes in for virtual methods and decorators. Unfortunately "ownership" of the resource is moved into the decorator (this would make sense if the "decorator" create the resource from a factory rather than being passed through the constructor).
o In order to support such decorators (buffered output streams for example), all resources can throw exceptions when being closed. Closing an input stream is a classic example where a close declares that it throws an exception, but probably shouldn't.
o We are now in a situation where finally needs to be inside the try block, but the syntax is the wrong way around.
There is also the problem that the finally block will need a reference to the resource, even though the construction of the resource needs to be within the try-catch.
So the general pattern should be:
try { acquire-resource; try { decorate-resource; use-decorator; flush-decorator; } finally { release-resource; } } catch (ResourceException exc) { ... erm, do something sensible ... ... probably wrap in a different exception ... }
Now, if the exception makes sense for the method to throw, the catch can be removed.
With closures, such as methods within anonymous inner classes, the common code can be factored out (although with the current syntax, you will still end up with some highly verbose boilerplate).
Tom Hawtin
kaldrenon - 08 Aug 2007 19:19 GMT On Aug 7, 10:53 am, "Crouchez" <b...@bllllllahblllbllahblahblahhh.com> wrote:
> are you sure? whether code throws an exception/error/throwable or not it > will still get to //do something in both scenarios. With StackOverflowError > or OutofMemoryError then the program often stops completely. public class Test { public static void main(String[] args) { try { return; } catch(Exception e){} finally { System.out.println("testing"); } } }
Output: "testing"
Crouchez - 09 Aug 2007 00:14 GMT > On Aug 7, 10:53 am, "Crouchez" <b...@bllllllahblllbllahblahblahhh.com> > wrote: [quoted text clipped - 21 lines] > Output: > "testing" yeah i only figured that after i did a test
bugbear - 07 Aug 2007 16:12 GMT > Scenario A > [quoted text clipped - 21 lines] > catch method? I don't understand why there is more use of resources either > in Scenario B? So what's the use of finally? In your code there is no point in finally. If you had REAL exception handling, i.e. non empty exception blocks (which you should), the distinction may become more obvious.
BugBear
Crouchez - 08 Aug 2007 19:10 GMT So...
You can't actually catch or code in a contigency plan for an OutofMemoryError but with finally you can? because finally runs regardless of whether an exception or error is caught or not? ie. finally always runs if the JVM is?
Lew - 08 Aug 2007 23:13 GMT > You can't actually catch or code in a contigency [sic] plan for an > OutofMemoryError but with finally you can? because finally runs regardless > of whether an exception or error is caught or not? ie. [sic] finally always runs > if the JVM is? Not correct. You can catch Errors and /try/ to do something about them *in the catch block*, just like any other Throwable. The finally block is not the place to handle such things; the catch block is.
That said, the reason you can't effectively catch an Error is that it represents a situation so fubared that there is no effective recovery available. Still, you can declare a catch block to attempt to minimize the damage.
 Signature Lew
Twisted - 09 Aug 2007 00:12 GMT > That said, the reason you can't effectively catch an Error is that it > represents a situation so fubared that there is no effective recovery > available. Still, you can declare a catch block to attempt to minimize the > damage. Curiously, I'd say that StackOverflowError, at minimum, may be an exception to this rule. I can imagine e.g.
public int myMethod (int arg) { if (isBaseCase(arg)) return 0; try { return recursiveMyMethod(arg); } catch (StackOverflowError e) { throw new IllegalArgumentException("Argument " + arg + " to myMethod is too big."); } }
The stack unwind "undoes" the damage of a stack overflow, since the stack ends up as stubby as it was before the recursive call. Of course the caller might still just upchuck on the IllegalArgumentException. It's not hard to imagine a less efficient nonrecursive version of myMethod being called, though, or some other such recovery depending on the situation, or the result of the IAE being a particular user operation failing without the whole app bombing.
In multithreaded parallelized code, another way to "recover" from even OOME is to let it kill the thread it arises within. Another thread goes to allocate something and this forces a GC, which reclaims all the resources the now-dead thread held. Memory pressure thus leads to scaled-back parallelization. With a control thread that holds the job queues and has some ability to detect worker thread disappearance, jobs can be reissued. Performance degrades instead of the task bombing. A factory can attempt to recreate the missing worker threads after some time has elapsed. A "fat job" could cause half the threads to die but eventually be done with and the system would bounce back up to its usual performance after a while.
Ultimately, how recoverable or irrecoverable any of these are is dependent on the requirements and details of what's being implemented.
Certainly typical applications with user documents need to catch all of them, including and especially OOME, and do a panic-save of the user's session and unsaved data to where the next session will attempt to recover, at minimum. (Not over the original files in case of data corruption or other inconsistent transitional states caused by the interrupting throwable!) To be able to do this, the exception handler can trigger a graceful but non-memory-allocating shutdown that tells threads to quit, destroys displayed UI, and drops references to most objects other than those representing unsaved data, then call System.gc() (though this should probably not be necessary), then create some streams and save the unsaved data to the recovery file, then pop up an apologetic error message, before shutting down. The memory freed by first disposing of the previously-visible UI objects alone is probably sufficient to do this successfully during exit.
Wojtek - 08 Aug 2007 19:41 GMT Crouchez wrote :
> try{ > //blah } > catch(Exception ex){ > // } > finally{ //do something }why not justtry{//blah}catch(Exception > e){}//do something public void myMethod() throws MyException { try { grabSomeResources(); somethingDangerous(); // throws MyException } finally { freeSomeResources(); } }
In the above the resources will always be freed.
 Signature Wojtek :-)
Thomas Hawtin - 08 Aug 2007 20:21 GMT > try > { [quoted text clipped - 8 lines] > > In the above the resources will always be freed. Even if it fails to be acquired!
The basic standard form is:
acquire; try { use; } finally { release; }.
Tom Hawtin
steve - 13 Aug 2007 00:26 GMT > try{ > //blah } > catch(Exception ex){ > // } > finally{ //do something }why not justtry{//blah}catch(Exception > e){}//do something finally IS ALWAYS CALLED.
Exception is not
Joshua Cranmer - 14 Aug 2007 00:16 GMT > finally IS ALWAYS CALLED. > > Exception is not From Peter Norvig's Java IAQ (Infrequently Answered Questions):
Q: Finally is always called, right? A:
public class Foo { public static void main(String[] args) { try { System.exit(0); } finally { System.out.println("Oi!"); } } }
I suppose the best way to phrase that is "A finally block is always called except if the VM is in a highly abnormal, unrecoverable state (e.g., SIGKILL) or ceases to exist in the middle of the referent try block (catch block if one is executed)." ^C'ing an infinite loop also classifies as an abnormal, unrecoverable state, as does deadlocks.
 Signature Beware of bugs in the above code; I have only proved it correct, not tried it. -- Donald E. Knuth
bugbear - 14 Aug 2007 16:37 GMT >> finally IS ALWAYS CALLED. >> [quoted text clipped - 20 lines] > block (catch block if one is executed)." ^C'ing an infinite loop also > classifies as an abnormal, unrecoverable state, as does deadlocks. I guess the a pickaxe through the CPU die would also prevent "finally" being called.
I don't normally worry about such things when coding :-)
BugBear
manivannan.palanichamy@gmail.com - 15 Aug 2007 05:47 GMT On Aug 7, 5:19 pm, "Crouchez" <b...@bllllllahblllbllahblahblahhh.com> wrote:
> try{ > //blah } > catch(Exception ex){ > // } > finally{ //do something }why not justtry{//blah}catch(Exception > e){}//do something try { //do task1 } catch(Exception e) { handle exception() } // do task2
Assume that you need to execute task1. Irrespective of the task1's success or failure, you need to execute task3. In the above case, no prob, you dont need to put finally block. task3 will be always executed though task1 succeeds/fails. Because catch block handles 'Exception', which is the root class of exception hierarchy. So, the catch block will catch all Exception.
But, look at this code,
try { //task1 } catch(CustomException e) { } finally { //task3 }
In this example, the catch block will handle only your CustomException. Suppose, if any other exception is thrown (say RuntimeException), then catch block cant handle it, so the execution will break, and task3 will not be executed. Now, if you put the task3 code inside finally block, it will be always executed irrespective of any exception. Generally, 1. logging code will be put in finally block 2. resource free-up code like database connection release, file close will be present in finally block 3. any acknowledgment message sending code can be placed in finally block
-- Manivannan.Palanichamy (@) Oracle.com http://mani.gw.googlepages.com/index.html
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|