我知道已经提出并回答了非常相似的问题,我读了我能找到的那些并且仍然不是100%明确的。
考虑此代码段:
public static void fooMethod {
while(<...>) {
....
final int temp = <something>;
....
}
}
没有内在的课程,没有其他特殊的或不寻常的。对我来说似乎是违反直觉的。
在上面的示例中声明局部变量final
是否可以用于任何目的?
我是否正确理解无论有没有final
,编译器都会产生完全相同的字节码?
我在这里遗漏了什么吗?如果是RTFM案例,请指出正确的方向。
后续问题(如果可以的话)
通过这样的重写来获得和/或失去什么(理解temp
不必是原始的?)
public static void fooMethod2 {
int temp;
while(<...>) {
....
temp = <something>;
....
}
}
答案 0 :(得分:9)
简而言之: final
关键字,用于局部变量和参数时,不会生成字节码({{1并且,正如预期的那样,它的使用在运行时没有任何影响。 (编译时,它可能会有所不同,但请查看以下内容。)
在这些情况下,当由于匿名内部类而未强制执行时,它只是样式选项,在记录变量的预期范围时非常有用。
以下测试确认了这些信息。
.class
会产生差异:请看这个片段:
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
1}}是)。
从这一点来看,我们可以得出结论,在字节码方面,编译器可以在使用final
时对生成的代码进行一些优化。 (我不是说它们有意义,但肯定final
不仅仅是样式选择。)
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
,会带来性能优势,{{ 1}}绝对只是 style 的选择。
此代码段的生成字节码...
final
...以及此代码段的生成字节码...
boolean zZ = true;
int aA = 1001, bB = 1002;
while (zZ) {
int xX = aA; // <--- declaration is inside WHILE
int yY = bB;
zZ = (xX == yY);
}
... 完全相同(当然只改变了行号)。
使用对象的其他测试(不仅是原始类型变量)也表现出相同的行为。
可以安全地得出结论,如果没有在其他地方使用,在循环内部或外部声明局部变量几乎是 设计选择 ,没有字节码效果。
注意:所有测试均在Oracle的JRE版本1.7.0_13下进行。
答案 1 :(得分:4)
final
是常量变量的关键字。将其声明为最终会阻止您稍后在循环内重新分配它。
temp
将在每次迭代时重新声明,无论它是否是最终的。
例如:
while (...)
{
final int temp = ...;
temp = 5; // compiler error
}
但如果它不是常数(最终):
while (...)
{
int temp = ...;
temp = 5; // fine
}
答案 2 :(得分:1)
从完全不同的角度考虑这一点:在功能编程语言中,几乎所有赋值都是最终的,并且类是不可变的是正常情况。这意味着非最终作业和/或可变类是例外。
如果您的代码是用Scala编写的,IntelliJ IDE会显示一条提示“此分配可以更改为最终”。
我非常感谢“决赛”,因为如果您稍后阅读了您的代码,您会在第一眼看到此分配从未进一步改变某些行。如果你知道实例是不可变的,这也会有所帮助。
此外,如果你一直使用“韵母”,那么非最终版本将获得可见性,而这些变量通常是最重要的观察点。