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

时间:2011-03-02 20:58:44

标签: java static compilation final

在java中,说我有以下

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

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

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

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

谢谢,
斯蒂芬妮

10 个答案:

答案 0 :(得分:27)

是的,Java编译器会将示例中的SIZE等静态常量值替换为其文字值。

因此,如果您稍后更改了课程SIZE中的A但未重新编译课程b,则仍会在课程b中看到旧值。你可以轻松地测试出来:

文件A.java

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

文件B.java

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

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

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

答案 1 :(得分:8)

您可以通过执行

来保持常量不被编译为B.
class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  

答案 2 :(得分:4)

证明行为是查看生成的字节码的另一种途径。当常数为"小" (大概是< 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

}

(我用了42而不是100,所以它更突出)。在这种情况下,它在字节代码中明显地被替换。但是,说这个常数更大。&#34;然后你会得到如下所示的字节代码:

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

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

在任何一种情况下,常量都嵌入到B.我想,因为在操作码中你只能访问当前类的运行时常量池,这就是将常量写入类文件的决定与实现无关(但是我不知道这个事实。

答案 3 :(得分:3)

呜 - 你每天都学到新东西!

取自Java规范......

  

注意:如果是基本类型或字符串   被定义为常量和值   编译时在编译时就知道了   在任何地方替换常量名称   在代码中有它的价值。这是   称为编译时常量。如果   外部常数的值   世界变化(例如,如果是   立法实际上应该是pi   3.975),您将需要重新编译使用此常量来获取的任何类   当前价值。

答案 4 :(得分:3)

这里的重要概念是static final字段使用编译时常量进行初始化,如JLS中所定义。使用非常量初始化(或非 - static或非 - final)并且不会被复制:

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

null不是*编译时常量`。)

答案 5 :(得分:2)

实际上我不久前遇到了这种奇怪的现象。

这将直接将“100”编译成b类。如果您只是重新编译A类,则不会更新B类中的值。

最重要的是,编译器可能没有注意到重新编译类b(在我编译单个目录时,类B在一个单独的目录中并且编译一个目录没有触发B的编译)

答案 6 :(得分:1)

作为优化,编译器将内联最终变量。

所以在编译时它会看起来像。

class b
{
      Object[] temp = new Object[100];
}

答案 7 :(得分:0)

有一点需要注意:静态最终值在编译时已知 如果在编译时未知该值,编译器将不会将代码中的常量名称替换为其值。

  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);
    }
}

首先编译TestA和TestB,运行TestB

然后改变TestA.getValue()返回200,编译TestA,运行TestB ,TestB将获得新值 enter image description here

答案 8 :(得分:0)

有一个例外: -

  

如果静态最终字段在编译时为空,则它不会被替换为空(实际上是其值)

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);
     }
}
  

编译A.java和B.java并运行 java B

     
    

输出 null

  
  

现在使用以下代码更新 A.java 并仅编译此类。

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

现在运行 java B

     
    

输出 Omg!在没有重新编译的情况下选择更新的值

  

答案 9 :(得分:-2)

Java确实优化了这些类型的值,但前提是它们属于同一个类。在这种情况下,由于您正在考虑的使用情况,JVM在A.SIZE中查找而不是优化它。