简述:当在局部变量和参数中使用时,
final
关键字不会出现在生成的字节码(
.class
文件)中,并且如预期的那样,在运行时不起作用。
(尽管在编译时可能会有所不同,请参见下文。)在这些情况下,如果由于匿名内部类而未强制执行,则仅是一种
风格选择,有助于记录变量的预期范围。下面的测试证实了这些信息。
1:如果编译器可以处理它,使用final
会产生差异:
看一下这个片段:
boolean zZ = true;
while (zZ) {
int xX = 1001; // <------------- xX
int yY = 1002; // <------------- yY
zZ = (xX == yY);
}
两个int
变量,xX
和yY
。第一次声明时都被声明为final
,第二次则从两者中去掉了final
。以下是生成的字节码(使用javap -c
打印):
两者均为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
两者均非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
在上述情况下,当它们是
final
时,
编译器知道它们不相等并且永远不会进行比较(无论何时
xX == yY
都会生成
false
字节码)。从这个可以得出结论,就字节码而言,使用
final
时编译器
确实可以对生成的代码进行一些优化。(我不是说它们有意义,但是
final
在这里绝不仅仅是一种
风格选择。)
2:如果编译器无法得出结论,则在本地变量上使用final
仅是一种设计选择:
现在看下面的代码:
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);
}
在这种情况下,即使使用了
final
,编译器也无法在编译时确定
xX
和
yY
是否相等,对吗?
因此,我们可以看到:当我们使用或不使用
final
生成类时,生成的字节码
完全相同(相同的MD5!)。
虽然在
一般情况下,
有人说使用
final
有性能优势,
其他人则持反对意见,但在局部块中,
final
绝对只是一种
风格选择。
3:循环内或外的局部变量-没有任何区别:
此片段生成的字节码...
boolean zZ = true;
int aA = 1001, bB = 1002;
while (zZ) {
int xX = aA;
int yY = bB;
zZ = (xX == yY);
}
...以及此片段生成的字节码...
boolean zZ = true;
int aA = 1001, bB = 1002;
int xX, yY;
while (zZ) {
xX = aA;
yY = bB;
zZ = (xX == yY);
}
...是完全相同的(当然,只有行号改变了)。
使用对象进行的其他测试(不仅限于原始类型变量)表现出相同的行为。
因此可以安全地得出结论,如果没有在其他地方使用,在循环内部或外部声明局部变量基本上是一种设计选择,没有字节码效果。
注意:所有测试都是在Oracle的JRE版本1.7.0_13下进行的。