19

In java, say I have the following

==fileA.java==
class A
{  
    public static final int SIZE = 100;
}  

Then in another file I use this value

==fileB.java==  
import A;
class b
{
      Object[] temp = new Object[A.SIZE];
}

When this gets compiled does SIZE get replaced with the value 100, so that if I were to replace the FileA.jar but not FileB.jar, would the object array get the new value or would it have been hardcoded to 100 because that's the value when it was originally built?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Without Me It Just Aweso
  • 4,593
  • 10
  • 35
  • 53

10 Answers10

32

Yes, the Java compiler does replace static constant values like SIZE in your example with their literal values.

So, if you would later change SIZE in class A but you don't recompile class b, you will still see the old value in class b. You can easily test this out:

file A.java

public class A {
    public static final int VALUE = 200;
}

file B.java

public class B {
    public static void main(String[] args) {
        System.out.println(A.VALUE);
    }
}

Compile A.java and B.java. Now run: java B

Change the value in A.java. Recompile A.java, but not B.java. Run again, and you'll see the old value being printed.

Jesper
  • 202,709
  • 46
  • 318
  • 350
8

You can keep the constant from being compiled into B, by doing

class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  
MeBigFatGuy
  • 28,272
  • 7
  • 61
  • 66
  • 1
    Thanks: this helped me in a weird scenario: I'm using ikvm to compile java to .NET and my static final fields were rendered as 'const' in C# (which exhibits the same inlining behavior). I wanted to have ikvm generate 'static readonly' which is constant but is not inlined in dependant assemblies. I applied you solution to my java code, and ikvm gracefully generated public static readonly in SIZE = 100; which is ok in C#! – odalet Oct 06 '14 at 20:23
  • Hi @MeBigFatGuy Could you explain whats goes on behind the scene when we define a constant inline like final static final String str = "some big string" compared to static { str = "some big string"; } Why static constants gets compiled into classes that references them? Any help would be nice – Lalit Rao Aug 17 '17 at 09:09
  • When you define a public static final String, all that happens is whereever you reference that field, the compiler will inject a LDC opcode referencing that class's Constant pool in which the LDC is issued. So if you define a field in one class, but reference that variable in 100 classes, each of those other 100 classes will get a copy of that String in each of those class's Constant Pool, and those other classes will not refer to the original class to get the value of the String. This can cause problems if you modify and compile the original class without compiling the 100 other classes. – MeBigFatGuy Aug 21 '17 at 06:22
  • If the string in question is really massive, you will have a bunch of really big class files. What ever class references that field will be big. If you really are dealing with massive strings, you probably should be putting them in property files that are on the classpath, anyway. – MeBigFatGuy Aug 21 '17 at 06:27
5

Another route to proving that the behavior is to looking at the generated bytecode. When the constant is "small" (presumably < 128):

public B();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   bipush  42
   7:   anewarray       #3; //class java/lang/Object
   10:  putfield        #12; //Field temp:[Ljava/lang/Object;
   13:  return

}

(I used 42 instead of 100 so it stands out more). In this case, it is clearly substituted in the byte code. But, say the constant is "bigger." Then you get byte code that looks like this:

public B();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   ldc     #12; //int 86753098
   7:   anewarray       #3; //class java/lang/Object
   10:  putfield        #13; //Field temp:[Ljava/lang/Object;
   13:  return

When it is bigger, the opcode "ldc" is used, which according to the JVM documentation "an unsigned byte that must be a valid index into the runtime constant pool of the current class".

In either case, the constant is embedded into B. I imagine, since that in opcodes you can only access the current classes runtime constant pool, that this the decision to write the constant into the class file is independent of implementation (but I don't know that for a fact).

James Kingsbery
  • 7,298
  • 2
  • 38
  • 67
  • 1
    You can get that bytecode from a `*.class` file by using the `javap` tool that's included with the JDK. – Jesper Mar 02 '11 at 22:14
4

Woo - you learn something new everyday!

Taken from the Java spec...

Note: If a primitive type or a string is defined as a constant and the value is known at compile time, the compiler replaces the constant name everywhere in the code with its value. This is called a compile-time constant. If the value of the constant in the outside world changes (for example, if it is legislated that pi actually should be 3.975), you will need to recompile any classes that use this constant to get the current value.

mmccomb
  • 13,516
  • 5
  • 39
  • 46
  • Really? Replacing a jar implies replacing the class, which is compiled. So that statement does not validate your 'not replaced' claim. A quick decompile of the class file would reveal the correct answer. – Brent Worden Mar 02 '11 at 21:06
3

The important concept here is that the static final field is initialised with a compile-time constant, as defined in the JLS. Use a non-constant initialiser (or non-static or non-final) and it wont be copied:

public static final int SIZE = null!=null?0: 100;

(null is not a *compile-time constant`.)

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • I think creating a dummy CONST() method and then doing "SIZE = CONST(100);" would be easier to read. http://stackoverflow.com/a/12065326/290254 – Julius Musseau Aug 22 '12 at 01:47
2

Actually I ran into this bizarreness a while ago.

This will compile "100" into class b directly. If you just recompile class A, this will not update the value in class B.

On top of that, the compiler may not notice to recompile class b (at the time I was compiling single directories and class B was in a separate directory and compiling a's directory did not trigger a compile of B)

Bill K
  • 62,186
  • 18
  • 105
  • 157
1

As an optimization the compiler will inline that final variable.

So at compile time it will look like.

class b
{
      Object[] temp = new Object[100];
}
Robby Pond
  • 73,164
  • 16
  • 126
  • 119
0

One thing should note is: static final value is known at compile time if the value is not known at compile time, compiler won't replaces the constant name everywhere in the code with its value.

  public class TestA {
      // public static final int value = 200;
      public static final int value = getValue();
      public static int getValue() {
        return 100;
      }
  }

public class TestB {
    public static void main(String[] args) {
        System.out.println(TestA.value);
    }
}

first compile TestA and TestB, run TestB

then change TestA.getValue() to return 200, compile TestA, run TestB, TestB will get the new value enter image description here

0

There is an exception to this:-

If static final field is null at the time of compiling then it doesn't get replaced with null (which is actually its value)

A.java

class A{
     public static final String constantString = null;
}

B.java

class B{
     public static void main(String... aa){
         System.out.println(A.constantString);
     }
}

Compile both A.java and B.java and run java B

Output will be null


Now Update A.java with following code and compile only this class.

class A{
     public static final String constantString = "Omg! picking updated value without re-compilation";
}

Now run java B

Output will be Omg! picking updated value without re-compilation

amandeep1991
  • 1,344
  • 13
  • 17
-2

Java does optimise these sorts of values but only if they are in the same class. In this case the JVM looks in A.SIZE rather than optimizing it because of the usage case you are considering.

satinyou
  • 11
  • 1