33

I am confused about the try-finally execution when there exists return; in the try block. In my understanding, the finally block will always be executed, i.e. before returning to the calling method. While considering the following simple code:

public class TryCatchTest {
    public static void main(String[] args){
        System.out.println(test());
    }
    static int test(){
        int x = 1;
        try{
            return x;
        }
        finally{
            x = x + 1;
        }
    }
}

The result printed is actually 1. Does this mean the finally block is not executed? Can anyone help me with it?

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
yifei
  • 561
  • 5
  • 18
  • 1
    Why don't you add a `System.out.println()` to the finally block and see yourself? I think it is executed but the value returned is 1. – Andrew Logvinov Aug 08 '13 at 16:41
  • When using try catch, you can "catch" specific types of exceptions (eg: IOException, NullPointerException etc..) the finally statement occurs when the specific exception happens and you need to run some code before the catch anyways... – RicardoE Aug 08 '13 at 16:44
  • 7
    [Here's](http://stackoverflow.com/questions/tagged/java+return+finally?sort=votes&pagesize=40) something to think about while upvoting – keyser Aug 08 '13 at 16:44
  • 1
    @RicardoE - Not exactly correct. The finally clause is (short of some messy recursive exception cases) always executed, if the try region is entered. – Hot Licks Aug 08 '13 at 16:45
  • Why ask if the block is executed. That's what prints are for. – keyser Aug 08 '13 at 16:51
  • 1
    @Keyser Because of answers from people like Rohit who take the time to actually explain what's happening and all of the underlying functionality, in order that everyone, not just the asker, can go forth with a much more comprehensive understanding. – Deactivator2 Aug 08 '13 at 16:54

3 Answers3

29

When you return from try block, the return value is stored on the stack frame for that method. After that the finally block is executed.

Changing the value in the finally block will not change the value already on the stack. However if you return again from the finally block, the return value on the stack will be overwritten, and the new x will be returned.

If you print the value of x in finally block, you will get to know that it is executed, and the value of x will get printed.

static int test(){
    int x = 1;
    try{
        return x;
    }
    finally{
        x = x + 1;
        System.out.println(x);  // Prints new value of x
    }
}

Note: In case of a reference value being returned, the value of reference is stored on the stack. In that case, you can change the value of object, using that reference.

StringBuilder builder = new StringBuilder("");
try {
    builder.append("Rohit ");
    return builder;

} finally {
    // Here you are changing the object pointed to by the reference
    builder.append("Jain");  // Return value will be `Rohit Jain`

    // However this will not nullify the return value. 
    // The value returned will still be `Rohit Jain`
    builder =  null;
}

Suggested Read:

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • 8
    Adding to this: Only the return *value* itself is stored on the stack. If you are returning a reference to an object, modifications to the object's fields done in the `finally` block will still be visible. – Jason C Aug 08 '13 at 16:48
  • @JasonC. Yup true. Will add to the answer. Thanks :) – Rohit Jain Aug 08 '13 at 16:49
  • And if anyone's confused about why `builder = null` doesn't nullify the return value it's because of the [pass by value](http://stackoverflow.com/a/40523/645270) thing. – keyser Aug 08 '13 at 17:12
  • @Keyser. Yes exactly that. – Rohit Jain Aug 08 '13 at 17:13
12

The finally block is executed. The local variable is incremented. But the value of that local variable has already been copied for the return value.

From the Java Language Specification, 14.17: The return statement:

A return statement with an Expression attempts to transfer control to the invoker of the method that contains it; the value of the Expression becomes the value of the method invocation.

...

The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (§14.20) within the method or constructor whose try blocks or catch clauses contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement

Andy Thomas
  • 84,978
  • 11
  • 107
  • 151
0

You are returning x before you exit try. I would do this:

public class TryCatchTest {
    public static void main(String[] args) {
        System.out.println(test());
    }
    static int test() {
        int x = 1;
        try {
            do something with x.
        } finally {
            do something that will happen even in case of error;
            x = x + 1;
            return x;
        }
    }
}
Ike Yousuf
  • 81
  • 5