在循环中将局部变量声明为final

PM *_*7-1 4 java final local-variables

我知道已经提出并回答了非常相似的问题,我阅读了我能够找到的并且仍然没有100%清楚的问题.

考虑此代码段:

public static void fooMethod {

   while(<...>) {
     ....
     final int temp = <something>;
     ....
   }
}
Run Code Online (Sandbox Code Playgroud)

没有内部阶级,没有别的特殊或不寻常.对我来说似乎是违反直觉的.

final在上面的示例中声明局部变量是否可以用于任何目的?

我是否理解正确有或没有final这里编译器将产生完全相同的字节码?

我在这里错过了什么吗?如果是RTFM案例,请指出正确的方向.

后续问题(如果可以的话)

通过这样的重写来获得和/或失去什么(理解temp不必是原始的)?

public static void fooMethod2 {

   int temp;
   while(<...>) {
     ....
     temp = <something>;
     ....
   }
}
Run Code Online (Sandbox Code Playgroud)

acd*_*ior 9

在几句话:final关键字,在局部变量和参数一起使用时,不会使其对生成的字节码(.class文件),并如市场预期,它的使用在运行时没有任何影响.(编译时,它可能会有所不同,但请查看下面的内容.)

在这些情况下,当由于匿名内部类而未强制执行时,它仅仅是一种样式选择,在记录变量的预期范围时非常有用.

以下测试证实了这些信息.



1:如果编译器可以创建它,使用final有所不同:

看看这个片段:

boolean zZ = true;
while (zZ) {
    int xX = 1001;         // <------------- xX
    int yY = 1002;         // <------------- yY
    zZ = (xX == yY);
}
Run Code Online (Sandbox Code Playgroud)

两个int变量,xXyY.第一次宣布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        
Run Code Online (Sandbox Code Playgroud)

两者都是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        
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,当它们是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);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,即使使用final,编译器不能告诉编译器时,如果xXyY是相等的,对不对?

因此,我们可以看到:当我们生成有或没有的类时,生成的字节码完全相同(相同的MD5!).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);
}
Run Code Online (Sandbox Code Playgroud)

...以及此代码段的生成字节码...

boolean zZ = true;
int aA = 1001, bB = 1002;
int xX, yY;                           // <--- declaration is outside WHILE
while (zZ) {
    xX = aA;
    yY = bB;
    zZ = (xX == yY);
}
Run Code Online (Sandbox Code Playgroud)

... 完全相同(当然只改变了行号).

使用对象的其他测试(不仅是原始类型变量)也表现出相同的行为.

结论是安全的,如果不在别处使用,在循环内部或外部声明局部变量几乎是一种设计选择,没有字节码效应.

注意:所有测试均在Oracle的JRE 1.7.0_13版本下进行.