Wednesday, November 12, 2008

Playing with try-catch-finally in Java. Intersting scenarios.


Various possible scenarios for try-catch-finally blocks and their execution

Do you feel that you understand how try-catch and finally blocks are executed in Java? If no then you would first like to read the article - finally explained >>


So now I can safely assume that you have a fair understanding of how the exception handling mechanism actually works in Java. Okay, let's try that out with few interesting programming scenarios. You'll find few code-snippets below and you may like to figure out the corresponding outputs to see how many of them you actually hit correct :-) Hope you get all of them right!


Scenario #1: try throwing an exception; catch and finally both having return statements



public class TestFinally {

/**
* @param args
*/
public static void main(String[] args) {

System.out.println("Inside main method!");
int iReturned = new TestFinally().testMethod();
System.out.println("Returned value of i = " + iReturned);

}

public int testMethod(){

int i = 0;
try{
System.out.println("Inside try block of testMethod!");
i = 100/0;
return i;
}catch(Exception e){
System.out.println("Inside catch block of testMethod!");
i = 200;
return i;
}
finally{
System.out.println("Inside finally block of testMethod!");
i = 300;
return i;
}
}
}



Output: a return (or any control transfer for that matter) in finally always rules!


Inside main method!
Inside try block of testMethod!
Inside catch block of testMethod!
Inside finally block of testMethod!
Returned value of i = 300



Scenarios #2: try having exception-free code and a return; catch and finally both have return



...
try{
System.out.println("Inside try block of testMethod!");
i = 100;
return i;
}catch(Exception e){
System.out.println("Inside catch block of testMethod!");
i = 200;
return i;
}
finally{
System.out.println("Inside finally block of testMethod!");
i = 300;
return i;
}
...



Output: did you get the first one right? This is a cakewalk then. With the same logic that any control transfer in finally always rules we can easily predict the output to be similar to that of Scenario #1 with the only difference that in this case the catch block won't be executed as no exception thrown... all right? Here is the output:



Inside main method!
Inside try block of testMethod!
Inside finally block of testMethod!
Returned value of i = 300



Scenario #3: try having exception; finally doesn't have a return



...
try{
System.out.println("Inside try block of testMethod!");
i = 100/0;
return i;
}catch(Exception e){
System.out.println("Inside catch block of testMethod!");
i = 200;
return i;
}
finally{
System.out.println("Inside finally block of testMethod!");
i = 300;
//return i;
}
...



Output: no return in finally means whatever executable return encountered on the way to finally will be executed once finally completes its execution, so the output would be:



Inside main method!
Inside try block of testMethod!
Inside catch block of testMethod!
Inside finally block of testMethod!
Returned value of i = 200



Scenario #4: try and catch both having exception; finally having a return



...
try{
System.out.println("Inside try block of testMethod!");
i = 100/0;
return i;
}catch(Exception e){
System.out.println("Inside catch block of testMethod!");
i = 200/0;
return i;
}
finally{
System.out.println("Inside finally block of testMethod!");
i = 300;
return i;
}
...



Output: control transfer in finally overrules the exceptions thrown in try/catch, hence the output would be:



Inside main method!
Inside try block of testMethod!
Inside catch block of testMethod!
Inside finally block of testMethod!
Returned value of i = 300



Scenario #5: try and catch both having exception; finally NOT having any return



...
try{
System.out.println("Inside try block of testMethod!");
i = 100/0;
return i;
}catch(Exception e){
System.out.println("Inside catch block of testMethod!");
i = 200/0;
return i;
}
finally{
System.out.println("Inside finally block of testMethod!");
i = 300;
//return i;
}
...



Output: since no return in finally, hence after the execution of the finally block the sheer need to have an executable return statement (which doesn't exist in this case as catch also has an exception) would throw the exception encountered right before the finally execution started, which would be the exception in catch block in our case...right? So, the output would be:



Exception in thread "main" java.lang.ArithmeticException: / by zero
at TestFinally.testMethod(TestFinally.java:24)
at TestFinally.main(TestFinally.java:10)
Inside main method!
Inside try block of testMethod!
Inside catch block of testMethod!
Inside finally block of testMethod!



Scenario #6: try, catch, and finally all three having exceptions



...
try{
System.out.println("Inside try block of testMethod!");
i = 100/0;
return i;
}catch(Exception e){
System.out.println("Inside catch block of testMethod!");
i = 200/0;
return i;
}
finally{
System.out.println("Inside finally block of testMethod!");
i = 300;
return i/0;
}
...



Output: evidently the exception would be thrown, but which one? The one which was encountered last i.e., the one encountered in the finally block. Output would be:



Inside main method!
Inside try block of testMethod!
Inside catch block of testMethod!
Inside finally block of testMethod!
Exception in thread "main" java.lang.ArithmeticException: / by zero
at TestFinally.testMethod(TestFinally.java:30)
at TestFinally.main(TestFinally.java:10)



Scenario #7: try and catch both fine; finally doesn't have any return



...
try{
System.out.println("Inside try block of testMethod!");
i = 100;
return i;
}catch(Exception e){
System.out.println("Inside catch block of testMethod!");
i = 200;
return i;
}
finally{
System.out.println("Inside finally block of testMethod!");
i = 300;
//return i;
}
...



Output: well... first thing first. If try is fine, do we need to even think about catch? A BIG No... right? Okay, so we have try and finally blocks to focus on. Let me first show you the output and then we would discuss if you have any doubts. Here is it:



Inside main method!
Inside try block of testMethod!
Inside finally block of testMethod!
Returned value of i = 100



In response to the article on 'finally explained', an anonymous visitor left a comment asking why the value of i set in the try block is returned in this case even though we know that finally completes first. Well... a nice pick I must say? Did you also notice the same?


Let's try to understand how all these statements would be executed. try-block would of course be started first and executed till any exception or a control transfer statement is encountered. If it's an exception, the control would go to catch and subsequently to finally to complete the execution. If it's a control transfer (like a return, break, continue, etc.) then the control transfer statement is evaluated (if any variable or expression involved as is the case here. 'return' is having a variable 'i' and hence the current value '100' in this case would be evaluated the statement would look something like 'return 100') and kept in a pending state. Once the finally is executed then the runtime sees if finally-block has any valid control transfer statement or not and if found then the pending control transfer statement is forgotten and the one present in finally is actually executed. In case finally-block doesn't have any control transfer statement then the pending one is executed and mind you it's not re-evaluated. This is not unusual as well. Try to understand it this way: in a normal case if we have a statement like 'return i*i' what would happen first? Since this is a complex statement involving a expression hence the expression would first be evaluated and once evaluation is complete then the value would replace the placeholder originally occupied by the expression. For example: if i = 10, then 'return i*i' would become 'return 100' and now this statement doesn't have any reference to 'i' to have any other value change in future...right? The pending control transfer statement would always be in a ready-to-run shape and it would not have any reference to any variable and hence any change to 'i' in the finally block would not affect the value associated with the pending 'return' statement. Makes sense?


Update [08-Jan-2009]: find another interesting try-catch-finally scenario involving possible combinations of inner and outer finally and catch blocks with nested try blocks - another interesting try-catch-finally scenario >>

Liked the article? You may like to Subscribe to this blog for regular updates. You may also like to follow the blog to manage the bookmark easily and to tell the world that you enjoy GeekExplains. You can find the 'Followers' widget in the rightmost sidebar.



Share/Save/Bookmark


6 comments:

Avenue 2 said...

I was wondering how this try-try-finally-catch would be executed

try{
try{
throw new NullPointerException ("e1");
finally{
System.out.println("e2");
}
}catch(Exception e){
e.printStackTrace();
}
I just came across this code. Found a little weird. I don't think so it is normal practise. Please let me know how would it get executed

Geek said...

You probably missed to have a closing brace for the inner try block. You are right in saying that such an usage is not a normal practice, but since it's possible hence we should understand how exactly will it be executed.

I could create two possible cases out of this scenario and considering the length and the suitability of the point raised, I thought I would be better off writing a separate article on this point. Please refer to the article - another interesting try-catch-finally scenario for having a detailed explanation of how will such a scenario be executed. Thank you for raising this point. Keep visiting/posting!

Anonymous said...

Don't you think that in Scenario#4 it will just throw exception as exception is thrown in catch block and there is nobody from there to catch it! Correct me If I am wrong ..
BTW too good posts , this site is increasing my knowledge and encouraging to me post more and more.

Geek said...

I think the answer to your question is given in the corresponding 'Output' section itself... pasting it again for you 'control transfer in finally overrules the exceptions thrown in try/catch'. Does this help?

Ben said...

Great article! However, I think using primatives somewhat complicates the issue. Scenerio #3, for example, works the way it does only because the int is handled specially (they're immutable). The following code showcases the difference:

public ArrayList catcherTest() {
ArrayList list = new ArrayList();
list.add(0);

try {
list.set(0, 100 / 0);
return list;
} catch (Exception ex) {
list.set(0, 200);
return list;
} finally {
list.set(0, 300);
//return i;
}
}

The returned list has one value, 300.

Anonymous said...

Awesome post! This one page is also a great reference as far as info on finally blocks go:

Finally block in Java