4

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>;
     ....
   }
}
PM 77-1
  • 12,933
  • 21
  • 68
  • 111
  • 1
    Presumably it serves the same purpose as anywhere else: when you try to modify the variable, the compiler stops you. – Cairnarvon May 26 '13 at 01:59
  • This variable is *very* local (just to the body of the loop). It's not even referenced anywhere in the same loop. Is it protecting readability by disallowing possible reuse for a different purpose? – PM 77-1 May 26 '13 at 02:16

3 Answers3

9

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. (Compile-time, it could make a diference, though, check below.)

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.

Note: All tests were made under Oracle's JRE, version 1.7.0_13.

Community
  • 1
  • 1
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • 1) It is unwise to draw conclusions about Java's ability to optimize code by looking at bytecode files. The serious optimization work is done by the JIT compiler at runtime. 2) In fact, the JIT compiler is perfectly capable of working out if a variable doesn't change beyond a certain point. It is unlikely that adding a `final` will help it. – Stephen C May 26 '13 at 04:46
  • @StephenC Thanks for the input, I appreciate it. Really seems unwise to draw conclusions from the bytecode files, I must admit. But you see, I only concluded that the generated bytecode is the same (with or without `final`). The moment the JIT takes it, he can't tell if there was a `final` keyword there or not. So it can't make a difference. What do you think? – acdcjunior May 26 '13 at 04:51
  • 1
    You are correct. If there were differences they could in theory be encoded in other descriptors, but from my brief reading of the JVM spec, they aren't ... – Stephen C May 26 '13 at 09:28
  • @StephenC So are you guys suggesting that though the byte-code generated in 1st case for class with non-final local variables is different, i.e. it does instructs JIT to perform comparison step of the two values at runtime, but JIT may optimize it further (seeing variables are not reassigned again) and skip that comparison step at runtime? – sactiw Sep 16 '14 at 08:30
  • Yes that is what I am saying. I haven't examined the native code to see if it *actually* does that, I would certainly *expect* the JIT compiler to optimize away the test, if it could. – Stephen C Sep 16 '14 at 13:28
4

final is a keyword for constant variables. Declaring it final prevents you from reassigning it later inside the loop.

temp will be re-declared on every iteration, regardless of whether it is final or not.

for example:

while (...)
{
    final int temp = ...;

    temp = 5; // compiler error
}

But if its not constant (final):

while (...)
{
    int temp = ...;

    temp = 5; // fine
}
Supericy
  • 5,866
  • 1
  • 21
  • 25
  • So it's just like a note to yourself: "I have no intentions to modify this variable"? And no effect on run-time? – PM 77-1 May 26 '13 at 02:02
  • @PM77-1 Here are some good answers to that question: http://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance – Supericy May 26 '13 at 02:04
  • Thank you. This one I have not seen yet. – PM 77-1 May 26 '13 at 02:07
  • its wrong, in my case its changing its value, plz refer this- https://stackoverflow.com/questions/57175610/after-service-call-automatically-variable-value-changing-in-loop – sachin Jul 26 '19 at 11:21
1

Consider this from a completly different perspective: In functional programming languages, it's the normal case that almost all assignments are final, and that the classes are immutable. That means that non-final assignments and/or mutable classes are the exception.

If your code was written in Scala, the IntelliJ IDE would show a hint "that this assignment can be changed to be final".

I really appreciate "finals", because if you read your code later, you see at the first glance that this assignment never changes some lines further down. And if you know that the instance is immutable, this will help as well.

Further, if you use "finals" consistently, the non-final ones will gain visibility, and these variables are normally the most important ones to observe.

Beryllium
  • 12,808
  • 10
  • 56
  • 86