我知道已经提出并回答了非常相似的问题,我阅读了我能够找到但仍然不是 100% 清楚的问题。
考虑这段代码:
public static void fooMethod {
while(<...>) {
....
final int temp = <something>;
....
}
}
没有内部类,没有其他特别或不寻常的东西。对我来说似乎违反直觉。
在上述示例中声明局部变量 final
是否有任何用途?
我的理解是否正确,无论是否使用 final
,编译器都会生成完全相同的字节码?
我是不是漏掉了什么?如果是 RTFM 案例,请指出正确的方向。
后续问题(如果可以的话)
像这样重写(理解 temp
不必是原语)我得到了什么和/或失去了什么?
public static void fooMethod2 {
int temp;
while(<...>) {
....
temp = <something>;
....
}
}
最佳答案
简而言之: 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
是否相等,对吗?
正因为如此,我们可以看到:生成的字节码完全相同(相同的MD5!)当我们生成类有或没有final
.
同时,在一般情况下,some say和 others disagree使用 final
有性能优势,在本地 block 中,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 下进行。
关于java - 在循环中将局部变量声明为 final,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16755554/