简而言之:关键字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
,编译器知道它们不相等并且从不比较它们(false
在字节码中生成xX == yY
)。
由此,我们可以得出结论,在字节码方面,编译器确实可以在使用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
.
虽然,在一般情况下,有人说和其他人不同意在本地块final
中使用,有性能优势,这绝对只是一种风格选择。final
3:循环内部或外部的局部变量 - 完全没有区别:
此片段生成的字节码...
boolean zZ = true;
int aA = 1001, bB = 1002;
while (zZ) {
int xX = aA; // <--- declaration is inside WHILE
int yY = bB;
zZ = (xX == yY);
}
...以及为该片段生成的字节码...
boolean zZ = true;
int aA = 1001, bB = 1002;
int xX, yY; // <--- declaration is outside WHILE
while (zZ) {
xX = aA;
yY = bB;
zZ = (xX == yY);
}
...完全相同(当然,只有行号发生了变化)。
使用对象(不仅是原始类型变量)的其他测试显示了相同的行为。
可以安全地得出结论,如果没有在其他地方使用,在循环内部或外部声明局部变量几乎是一种设计选择,没有字节码影响。
注意:所有测试均在 Oracle 的 JRE 版本 1.7.0_13 下进行。