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?
The
try
block completes with the execution of thereturn
statement and the value ofs
at the time thereturn
statement executes is the value returned by the method. The fact that thefinally
clause later changes the value ofs
(after thereturn
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 thefinally
block, not to the object thats
references. Ifs
was a reference to a mutable object (whichString
is not) and the contents of the object were changed in thefinally
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 thetry
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 areturn
statement is an abrupt termination of a block.By way of further detail: if both the
try
block and thefinally
block of atry-finally
statement terminate abruptly because ofreturn
statements, then the following rules from §14.20.2 apply:The result is that the
return
statement in thefinally
block determines the return value of the entiretry-finally
statement, and the returned value from thetry
block is discarded. A similar thing occurs in atry-catch-finally
statement if thetry
block throws an exception, it is caught by acatch
block, and both thecatch
block and thefinally
block havereturn
statements.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.Output:
Try this: If you want to print the override value of s.
Technically speaking, the
return
in the try block won't be ignored if afinally
block is defined, only if that finally block also includes areturn
.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 thetry
block, always run this code." Hence if you return true from afinally
block, the overall effect must always to be toreturn 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.If we look inside bytecode, we'll notice that JDK has made a significant optimization, and foo() method looks like:
And bytecode:
java preserved "dev" string from being changed before returning. In fact here is no finally block at all.
Because the return value is put on the stack before the call to finally.