编译时代码中是否替换了Java静态最终值?

Wit*_*eso 15 java static final compilation

在java中,说我有以下内容

==fileA.java==
class A
{  
    public static final int SIZE = 100;
}  
Run Code Online (Sandbox Code Playgroud)

然后在另一个文件中我使用此值

==fileB.java==  
import A;
class b
{
      Object[] temp = new Object[A.SIZE];
}
Run Code Online (Sandbox Code Playgroud)

当这个被编译时会SIZE被替换为值100,所以如果我要在路上替换FileA.jar而不是FileB.jar,对象数组将获得新值或者它是否已被硬编码为100,因为那是它最初建成时的价值?

谢谢,
斯蒂芬妮

Jes*_*per 28

是的,Java编译器确实将SIZE示例中的静态常量值替换为其文字值.

因此,如果您稍后SIZE在课堂上进行更改A但不重新编译课程b,则仍会在课堂上看到旧值b.你可以轻松测试一下:

文件A.java

public class A {
    public static final int VALUE = 200;
}
Run Code Online (Sandbox Code Playgroud)

文件B.java

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

编译A.java和B.java.现在运行:java B

更改A.java中的值.重新编译A.java,但不重新编译B.java.再次运行,您将看到打印旧值.

  • 有没有什么方法可以避免编译器这样做? (3认同)

MeB*_*Guy 8

你可以通过这样做来保持常量不被编译成B.

class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  
Run Code Online (Sandbox Code Playgroud)


Jam*_*ery 5

证明该行为的另一种方法是查看生成的字节码。当常数“小”时(大概 < 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

}
Run Code Online (Sandbox Code Playgroud)

(我使用 42 而不是 100,所以它更突出)。在这种情况下,它显然被替换在字节码中。但是,假设常数“更大”。然后你会得到如下所示的字节代码:

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
Run Code Online (Sandbox Code Playgroud)

当它更大时,使用操作码“ldc”,根据JVM 文档,它是“一个无符号字节,必须是当前类的运行时常量池的有效索引”。

在任何一种情况下,常量都会嵌入到 B 中。我想,由于在操作码中您只能访问当前类运行时常量池,因此将常量写入类文件的决定与实现无关(但我不这样做)事实上我不知道)。