In a few words: The final
keyword, when used in local variables and parameters, does not make it to the generated bytecode (.class
file) and, as expected, its use has no effect during runtime. (Compile-time, it could make a diference, though, check below.)
In those cases, when not enforced due to anonymous inner classes, it is merely a style choice, useful in documenting the intended scope of the variable.
The tests below confirm that information.
1: If the compiler can make something of it, using final
makes difference:
Look at this snippet:
boolean zZ = true;
while (zZ) {
int xX = 1001; // <------------- xX
int yY = 1002; // <------------- yY
zZ = (xX == yY);
}
Two int
variables, xX
and yY
. First time declared both as final
and second time, took away the final
from both. Here are the generated bytecodes (printed with javap -c
):
Both final
:
0: iconst_1 // pushes int 1 (true) onto the stack
1: istore_1 // stores the int on top of the stack into var zZ
2: goto 15
5: sipush 1001 // pushes 1001 onto the operand stack
8: istore_2 // stores on xX
9: sipush 1002 // pushes 1002 onto the operand stack
12: istore_3 // stores on yY
13: iconst_0 // pushes 0 (false): does not compare!! <---------
14: istore_1 // stores on zZ
15: iload_1 // loads zZ
16: ifne 5 // goes to 5 if top int (zZ) is not 0
19: return
Both non-final
:
// 0: to 12: all the same
13: iload_2 // pushes xX onto the stack
14: iload_3 // pushes yY onto the stack
15: if_icmpne 22 // here it compares xX and yY! <------------
18: iconst_1
19: goto 23
22: iconst_0
23: istore_1
24: iload_1
25: ifne 5
28: return
In the case above, when they are final
, the compiler knows that they are not equal and never compares them (false
is generated in the bytecode wherever xX == yY
is).
From this, we can conclude, bytecode-wise, the compiler does can do some optimization on the generated code when using final
. (I'm not saying they are meaningful, but for sure final
is not only a style choice here.)
2: If the compiler can't conclude anything, using final
on local vars is just a design choice:
Now take the following code:
boolean zZ = true;
int aA = 1001;
int bB = 1002;
while (zZ) {
final int xX = aA; // <------- took away the "final" here, didnt matter
final int yY = bB; // <------- took away the "final" here, didnt matter
zZ = (xX == yY);
}
In this case, even using final
, the compiler cannot tell compiler-time if xX
and yY
are equal, right?
Because of this, we can see: the generated bytecode is exactly the same (same MD5!) when we generate the class with or without final
.
While, in the general case, some say and others disagree that there are performance benefits of using final
, in local blocks, final
is definitely only a style choice.
3: Local variables inside or outside loops - no difference at all:
The generated bytecode for this snippet...
boolean zZ = true;
int aA = 1001, bB = 1002;
while (zZ) {
int xX = aA; // <--- declaration is inside WHILE
int yY = bB;
zZ = (xX == yY);
}
...and the generated bytecode for this snippet...
boolean zZ = true;
int aA = 1001, bB = 1002;
int xX, yY; // <--- declaration is outside WHILE
while (zZ) {
xX = aA;
yY = bB;
zZ = (xX == yY);
}
...are exactly the same (only the line numbers changed, of course).
Other tests using objects (not only primitive typed variables) showed the same behaviour.
It is safe to conclude, then, if not used elsewhere, declaring local variables inside or outside loops is pretty much a design choice, with no bytecode effects.
Note: All tests were made under Oracle's JRE, version 1.7.0_13.