Java static final values replaced in code when compiling?
Solution 1
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.
Solution 2
You can keep the constant from being compiled into B, by doing
class A
{
public static final int SIZE;
static
{
SIZE = 100;
}
}
Solution 3
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).
Solution 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.
Solution 5
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`.)
Comments
-
Without Me It Just Aweso about 2 years
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?