在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,因为它是最初构建时的值?
谢谢,
斯蒂芬妮
答案 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中查找而不是优化它。