148

I have a simple Java class as shown below:

public class Test {

    private String s;

    public String foo() {
        try {
            s = "dev";
            return s;
        } 
        finally {
            s = "override variable s";
            System.out.println("Entry in finally Block");  
        }
    }

    public static void main(String[] xyz) {
        Test obj = new Test();
        System.out.println(obj.foo());
    }
}

And the output of this code is this:

Entry in finally Block
dev  

Why is s not overridden in the finally block, yet control printed output?

Devendra
  • 1,864
  • 6
  • 29
  • 49
  • obj.foo() return the value of s; – Devendra Apr 16 '13 at 07:11
  • 11
    you should put the return statement on the finally block, repeat with me, finally block is always executed – Juan Antonio Gomez Moriano Apr 16 '13 at 07:12
  • 1
    in try block it return s – Devendra Apr 16 '13 at 07:12
  • 6
    The order of statements matters. You are returning `s` before you change it's value. – Peter Lawrey Apr 16 '13 at 07:27
  • @Juvanis it's working properly and running. – Devendra Apr 16 '13 at 07:31
  • 6
    But, [you **can** return the new value in the `finally` block](http://ideone.com/Zty7ic), unlike in C# (which you cannot) – Alvin Wong Apr 16 '13 at 07:46
  • @AlvinWong yes we can return in finally,It gives some warning.... finally block does not complete normally – Devendra Apr 16 '13 at 08:00
  • Maybe this is just you missed, but get in the habit of writing class names starting with upper case all the time. – treaz Apr 16 '13 at 09:20
  • 4
    Related: [does return "happen after" finally](http://stackoverflow.com/questions/5708834/does-return-happen-after-finally) – Alvin Wong Apr 16 '13 at 09:41
  • Apart from java being able to work that way, I don't think it is a good coding practice to use mechanisms that arent easily understood (and work differently in many languages). Many people (like myself) will assume a return leaves the method and not execute the finally block. So why not just avoid such design and avoid any misunderstandings? just put the return even below your finally block. – ToBe Apr 18 '13 at 10:08
  • 1
    @ToBe read this java doc http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html – Devendra Apr 18 '13 at 10:12
  • @dev you are right, i can see the benefits now. Just not sure I like it in most situations. But tnx for the info! – ToBe Apr 18 '13 at 10:16
  • Possible duplicate of [Behaviour of return statement in catch and finally](http://stackoverflow.com/questions/5701793/behaviour-of-return-statement-in-catch-and-finally) – Peter Mortensen Apr 21 '13 at 20:27
  • @PeterMortensen question as you pointed out, only one part of this question return with finally,but here another thing is that why "s" not overwrite by the compiler in finally block. – Devendra Apr 22 '13 at 06:12

7 Answers7

168

The try block completes with the execution of the return statement and the value of s at the time the return statement executes is the value returned by the method. The fact that the finally clause later changes the value of s (after the return statement completes) does not (at that point) change the return value.

Note that the above deals with changes to the value of s itself in the finally block, not to the object that s references. If s was a reference to a mutable object (which String is not) and the contents of the object were changed in the finally block, then those changes would be seen in the returned value.

The detailed rules for how all this operates can be found in Section 14.20.2 of the Java Language Specification. Note that execution of a return statement counts as an abrupt termination of the try block (the section starting "If execution of the try block completes abruptly for any other reason R...." applies). See Section 14.17 of the JLS for why a return statement is an abrupt termination of a block.

By way of further detail: if both the try block and the finally block of a try-finally statement terminate abruptly because of return statements, then the following rules from §14.20.2 apply:

If execution of the try block completes abruptly for any other reason R [besides throwing an exception], then the finally block is executed, and then there is a choice:

  • If the finally block completes normally, then the try statement completes abruptly for reason R.
  • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

The result is that the return statement in the finally block determines the return value of the entire try-finally statement, and the returned value from the try block is discarded. A similar thing occurs in a try-catch-finally statement if the try block throws an exception, it is caught by a catch block, and both the catch block and the finally block have return statements.

Community
  • 1
  • 1
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • If i use StringBuilder class instead of String than append some value in finally block,its changes the return value.why? – Devendra Apr 17 '13 at 13:05
  • 6
    @dev - I discuss that in the second paragraph of my answer. In the situation you describe, the `finally` block does not change what object is returned (the `StringBuilder`) but it can change the internals of the object. The `finally` block executes before the method actually returns (even though the `return` statement has finished), so those changes occur before the calling code sees the returned value. – Ted Hopp Apr 17 '13 at 14:08
  • try with List, you get same behavior like StringBuilder. – Yogesh Prajapati Jul 02 '13 at 05:52
  • 1
    @yogeshprajapati - Yep. The same is true with any mutable return value (`StringBuilder`, `List`, `Set`, ad nauseum): if you change the contents in the `finally` block, then those changes are seen in the calling code when the method finally exits. – Ted Hopp Jul 02 '13 at 06:14
67

Because the return value is put on the stack before the call to finally.

Tordek
  • 10,628
  • 3
  • 36
  • 67
  • 3
    That's true, but it doesn't address the OP's question as to why the returned string didn't change. That has to do with string immutability and references versus objects more than pushing the return value on the stack. – templatetypedef Apr 17 '13 at 04:27
  • 2
    @templatetypedef even if String were mutable, using `=` would not mutate it. – Owen Apr 17 '13 at 15:00
  • @Owen - I think you are confusing variables with memory objects they refer. `=` does not mutate a string, it assigns a reference to a different memory object. Mutability is a property of values (garbage collected memory objects), not variables referring to them. – Saul Apr 19 '13 at 06:45
  • 1
    @Saul - Owen's point (which is correct) was that immutability has nothing to do with why OP's `finally` block did not affect the return value. I think that what templatetypedef might have been getting at (although this isn't clear) is that because the returned value is a reference to a immutable object, even changing the code in the `finally` block (other than using another `return` statement) could not affect the value returned from the method. – Ted Hopp Apr 19 '13 at 15:15
  • @TedHopp - Ugh.. apparently I misread Owen's comment. For some strange reason I remember seeing him asking rhetorically that if String was mutable then why would `=` mutate it. Hence the comment. – Saul Apr 19 '13 at 15:38
  • 1
    @templatetypedef No it doesn't. Nothing to do with String immutability whatsoever. Same would happen with any other type. It has to do with evaluating the return-expression before entering the finally-block, in other words exacfly 'pushing the return value on the stack'. – user207421 Dec 23 '19 at 07:06
33

If we look inside bytecode, we'll notice that JDK has made a significant optimization, and foo() method looks like:

String tmp = null;
try {
    s = "dev"
    tmp = s;
    s = "override variable s";
    return tmp;
} catch (RuntimeException e){
    s = "override variable s";
    throw e;
}

And bytecode:

0:  ldc #7;         //loading String "dev"
2:  putstatic   #8; //storing it to a static variable
5:  getstatic   #8; //loading "dev" from a static variable
8:  astore_0        //storing "dev" to a temp variable
9:  ldc #9;         //loading String "override variable s"
11: putstatic   #8; //setting a static variable
14: aload_0         //loading a temp avariable
15: areturn         //returning it
16: astore_1
17: ldc #9;         //loading String "override variable s"
19: putstatic   #8; //setting a static variable
22: aload_1
23: athrow

java preserved "dev" string from being changed before returning. In fact here is no finally block at all.

Mikhail
  • 4,175
  • 15
  • 31
22

There are 2 things noteworthy here:

  • Strings are immutable. When you set s to "override variable s", you set s to refer to the inlined String, not altering the inherent char buffer of the s object to change to "override variable s".
  • You put a reference to the s on the stack to return to the calling code. Afterwards (when the finally block runs), altering the reference should not do anything for the return value already on the stack.
0xCAFEBABE
  • 5,576
  • 5
  • 34
  • 59
  • 1
    so if i take stringbuffer than sting will be overridden?? – Devendra Apr 16 '13 at 07:20
  • 10
    @dev - If you were to change the _content_ of the buffer in the `finally` clause, that would be seen in the calling code. However, if you assigned a new stringbuffer to `s`, then the behavior would be the same as now. – Ted Hopp Apr 16 '13 at 07:21
  • yes if i append in string buffer in finally block changes reflect on output. – Devendra Apr 16 '13 at 07:30
  • @0xCAFEBABE you also give very good answer and concepts,i highly thank full to you. – Devendra Apr 17 '13 at 05:13
13

I change your code a bit to prove the point of Ted.

As you can see in the output s is indeed changed but after the return.

public class Test {

public String s;

public String foo() {

    try {
        s = "dev";
        return s;
    } finally {
        s = "override variable s";
        System.out.println("Entry in finally Block");

    }
}

public static void main(String[] xyz) {
    Test obj = new Test();
    System.out.println(obj.foo());
    System.out.println(obj.s);
}
}

Output:

Entry in finally Block 
dev 
override variable s
k1eran
  • 4,492
  • 8
  • 50
  • 73
Frank
  • 16,476
  • 7
  • 38
  • 51
5

Technically speaking, the return in the try block won't be ignored if a finally block is defined, only if that finally block also includes a return.

It's a dubious design decision that was probably a mistake in retrospect (much like references being nullable/mutable by default, and, according to some, checked exceptions). In many ways this behaviour is exactly consistent with the colloquial understanding of what finally means - "no matter what happens beforehand in the try block, always run this code." Hence if you return true from a finally block, the overall effect must always to be to return s, no?

In general, this is seldom a good idiom, and you should use finally blocks liberally for cleaning up/closing resources but rarely if ever return a value from them.

Kiran Jujare
  • 178
  • 10
0

Try this: If you want to print the override value of s.

finally {
    s = "override variable s";    
    System.out.println("Entry in finally Block");
    return s;
}
Achintya Jha
  • 12,735
  • 2
  • 27
  • 39