本文介绍了在循环中将局部变量声明为final的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道已经提出并回答了非常类似的问题,我阅读了能够找到的问题,但仍未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变量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

均为非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时,编译器知道它们不相等并且从不对其进行比较(falsexX == 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,编译器也无法告诉编译器时间xXyY是否相等,对吗?

因此,我们可以看到:生成带有或不带有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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-28 01:24
查看更多