问题描述
我知道已经提出并回答了非常类似的问题,我阅读了能够找到的问题,但仍未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
文件)中,并且按预期的方式使用它在运行时无效.
在这些情况下,由于匿名内部类而没有实施时,它只是一种 style 选择,可用于记录变量的预期作用域.
以下测试确认了该信息.
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
不仅是 style 的选择.)
2:如果编译器无法得出任何结论,则在本地vars上使用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!). /p>
在一般情况下,,有人说和其他人不同意表示,在本地块中使用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);
}
... 完全相同(当然,只更改了行号).
其他使用对象(不仅是基本类型变量)的测试也表现出相同的行为.
可以得出结论,如果没有在其他地方使用,则在循环内部或外部声明局部变量几乎是 设计选择 ,没有字节码效果.
I know that very similar questions have been asked and answered already, I read the ones I was able to locate and still not 100% clear.
Considering this code snippet:
public static void fooMethod {
while(<...>) {
....
final int temp = <something>;
....
}
}
No inner classes, nothing else special or unusual. Seems counter-intuitive to me.
Does declaring a local variable final
in the above sample serve any purpose whatsoever?
Do I understand correctly that with or without final
here compiler will produce exactly the same byte-code?
Am I missing something here? If it's an RTFM case, please point me in the right direction.
Follow-up Question (if I may)
What do I gain and/or lose by re-writing like this (with the understanding that temp
does not have to be a primitive)?
public static void fooMethod2 {
int temp;
while(<...>) {
....
temp = <something>;
....
}
}
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.
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.
这篇关于在循环中将局部变量声明为final的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!