标题有点绕,意思是说如果A类中定义了一个常量,且这个常量是一个“编译期常量”,那么当其他类在引用这个常量的时候。A类本身可以不被JVM加载,这个常量可以被直接引用。
是不是更绕了,怎么还多出来一个“编译期常量”。即在编译期即可确定常量值。直接说重点,JVM这个编译优化会引起令人匪夷所思的BUG。让你查也查不到。
还是要回到标题,不加载A类就能直接引用A类的常量。这个常量会被存在调用类的常量池中。这样很容易让我想到既然是在编译期就存到了调用类中,那么如果编译的时候和运行的时候A类发生了变化,常量的值变了,在调用类没有重新编译的情况下,调用类就会引用到一个错误常量值。JVM真的会让这种事发生吗?!
用代码说话:
/**
* @Author: tiantao
* @Date: 2019/1/16 10:37 AM
* @Version 1.0
*/
public class StaticClass {
static {
System.out.println("StaticClass loading...");
}
public static String VALUE = "static value loading A";
public static final String FIANL_VALUE = "fianl value loading A";
}
/**
* @Author: tiantao
* @Date: 2019/1/16 10:38 AM
* @Version 1.0
*/
public class StaticClassLoadTest {
public static void main(String[] args) {
System.out.println("StaticClassLoadTest...");
printStaticVar();
}
private static void printStaticVar() {
System.out.println(StaticClass.FIANL_VALUE);
System.out.println(StaticClass.VALUE);
}
}
执行结果:
StaticClassLoadTest...
fianl value loading A
StaticClass loading...
static value loading A
说明一下 StaticClass类的FIANL_VALUE是静态常量 而 VALUE 只是静态变量 或者说类变量。
从输出结果可以看出 在打印FIANL_VALUE的时候并没有加载StaticClass类,而是在需要调用到类变量的时候才加载的。
以上只是说明了加载顺序,但是不能说明静态常量 也就是例子中的FIANL_VALUE是被放到了调用类StaticClassLoadTest的常量池中。
接下来我们做如下操作。
1.备份当前的StaticClass的class文件。

2.修改StaticClass
/**
* @Author: tiantao
* @Date: 2019/1/16 10:37 AM
* @Version 1.0
*/
public class StaticClass {
static {
System.out.println("StaticClass loading...");
}
public static String VALUE = "static value loading B";
public static final String FIANL_VALUE = "fianl value loading B";
}
执行结果:
StaticClassLoadTest...
fianl value loading B
StaticClass loading...
static value loading B
这个结果毋庸置疑,没毛病。
3.删除现在的StaticClass.class文件
4.将之前备份的StaticClass的副本.class重命名为:StaticClass.class
5.执行程序,看执行结果,见证奇迹:
StaticClassLoadTest...
fianl value loading B
StaticClass loading...
static value loading A
怎么可以这样!
曾经那个修改常量不生效的bug,本地测试怎么都不复现,一上生产死活不生效。我明明没有改调用类,谁知道调用者也要更新!

本文探讨了JVM如何处理编译期常量,特别是在其他类引用这些常量时的行为。通过实例展示了静态常量在编译时如何被放入调用类的常量池,以及这可能导致的常量值不一致问题。
436

被折叠的 条评论
为什么被折叠?



