编译时在代码中替换Java静态最终值?
在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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
是的,Java 编译器确实将示例中的静态常量值(如
SIZE
)替换为其文字值。因此,如果您稍后更改类
A
中的SIZE
但不重新编译类b
,您仍然会在类中看到旧值b
。您可以轻松地进行测试:文件 A.java
文件 B.java
编译 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 classA
but you don't recompile classb
, you will still see the old value in classb
. You can easily test this out:file A.java
file B.java
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.
您可以通过执行以下操作来防止常量被编译到 B 中
You can keep the constant from being compiled into B, by doing
证明该行为的另一种方法是查看生成的字节码。当常数“小”时(大概< 128):(
我使用 42 而不是 100,所以它更突出)。在这种情况下,它显然被替换在字节码中。但是,假设常数“更大”。然后你会得到如下所示的字节码:
当它更大时,使用操作码“ldc”,根据 JVM 文档 “一个无符号字节,必须是当前类的运行时常量池的有效索引”。
在任何一种情况下,常量都会嵌入到 B 中。我想,由于在操作码中您只能访问当前类运行时常量池,因此将常量写入类文件的决定与实现无关(但我不这样做)事实上我不知道)。
Another route to proving that the behavior is to looking at the generated bytecode. When the constant is "small" (presumably < 128):
(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:
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).
哇——你每天都会学到新东西!
摘自 Java 规范...
Woo - you learn something new everyday!
Taken from the Java spec...
这里的重要概念是,
static final
字段是用 JLS 中定义的编译时常量初始化的。使用非常量初始值设定项(或非static
或非final
)并且它不会被复制:(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:(
null
is not a *compile-time constant`.)事实上,我不久前就遇到过这种奇怪的情况。
这将直接将“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)
作为优化,编译器将内联该 final 变量。
所以在编译时它看起来像。
As an optimization the compiler will inline that final variable.
So at compile time it will look like.
需要注意的一件事是:静态最终值在编译时已知
如果在编译时未知该值,编译器不会将代码中各处的常量名称替换为其值。
首先编译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.
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
有一个例外:-
A.java
B.java
There is an exception to this:-
A.java
B.java
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.