编译时在代码中替换Java静态最终值?

发布于 2024-10-19 21:10:46 字数 354 浏览 9 评论 0原文

在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,因为这是最初构建时的值?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

禾厶谷欠 2024-10-26 21:10:46

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

因此,如果您稍后更改类 A 中的 SIZE 但不重新编译类 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。再次运行,您将看到打印旧值。

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.

强者自强 2024-10-26 21:10:46

您可以通过执行以下操作来防止常量被编译到 B 中

class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  

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

class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  
顾冷 2024-10-26 21:10:46

证明该行为的另一种方法是查看生成的字节码。当常数“小”时(大概< 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,所以它更突出)。在这种情况下,它显然被替换在字节码中。但是,假设常数“更大”。然后你会得到如下所示的字节码:

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

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

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

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).

我纯我任性 2024-10-26 21:10:46

哇——你每天都会学到新东西!

摘自 Java 规范...

注意:如果是基本类型或者字符串
被定义为一个常数,其值
在编译时已知,编译器
到处替换常量名称
在代码中及其值。这是
称为编译时常量。如果
外部常数的值
世界发生变化(例如,如果
立法规定 pi 实际上应该是
3.975),您将需要重新编译使用此常量的所有类以获得
当前值。

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.

一口甜 2024-10-26 21:10:46

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

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

null 不是 *compile -时间常数`。)

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`.)

孤独陪着我 2024-10-26 21:10:46

事实上,我不久前就遇到过这种奇怪的情况。

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

最重要的是,编译器可能不会注意到重新编译类b(当时我正在编译单个目录,类B位于单独的目录中并编译a的目录没有触发B的编译)

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)

神经大条 2024-10-26 21:10:46

作为优化,编译器将内联该 final 变量。

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

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

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];
}
温柔嚣张 2024-10-26 21:10:46

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

  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将获得新值
在此处输入图片说明

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

温柔少女心 2024-10-26 21:10:46

有一个例外:-

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

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

<块引用>

输出将是天啊!选择更新的值而不重新编译

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

无声静候 2024-10-26 21:10:46

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文