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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5173372/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 09:49:06  来源:igfitidea点击:

Java static final values replaced in code when compiling?

javastaticcompilationfinal

提问by Without Me It Just Aweso

In java, say I have the following

在java中,说我有以下内容

==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 SIZEget replaced with the value 100, so that if i were to down the road 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 thats the value when it was originally built?

当它被编译时确实SIZE被替换为值 100,所以如果我要替换 FileA.jar 而不是 FileB.jar,对象数组会获得新值还是会被硬编码为 100,因为那是最初建造时的价值?

Thanks,
Stephanie

谢谢,
斯蒂芬妮

回答by Jesper

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

是的,Java 编译器确实SIZE用它们的文字值替换了你的例子中的静态常量值。

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

因此,如果您稍后SIZE在 class 中更改A但不重新编译 class b,您仍然会在 class 中看到旧值b。您可以轻松地对此进行测试:

file A.java

文件 A.java

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

file B.java

文件 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

编译 A.java 和 B.java。现在运行: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.

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

回答by MeBigFatGuy

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

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

class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  

回答by James Kingsbery

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

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

(我用 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

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

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

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

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

回答by Tom Hawtin - tackline

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

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

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

(nullis not a *compile-time constant`.)

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

回答by mmccomb

Woo - you learn something new everyday!

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

Taken from the Java spec...

取自 Java 规范...

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.

注意:如果原始类型或字符串被定义为常量并且该值在编译时已知,则编译器将代码中各处的常量名称替换为其值。这称为编译时常量。如果外部世界中的常量值发生变化(例如,如果立法规定 pi 实际上应该是 3.975),则需要重新编译任何使用此常量的类来获取当前值。

回答by Bill K

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.

这将直接将“100”编译为类 b。如果只是重新编译 A 类,则不会更新 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)

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

回答by Robby Pond

As an optimization the compiler will inline that finalvariable.

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

So at compile time it will look like.

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

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

回答by Jay Zhou

One thing should note is: static final value is known at compile timeif the value is not known at compile time, compiler won'treplaces 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

先编译TestA和TestB,运行TestB

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

然后将 TestA.getValue() 改为返回 200,编译 TestA,运行 TestB,TestB 将获得新值 在此处输入图像描述

回答by amandeep1991

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

一个.java

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

B.java

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

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

输出将为



Now Update A.javawith following code and compile only this class.

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

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

现在运行java B

输出将是天哪!无需重新编译即可选择更新的值

回答by satinyou

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.

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