有一个常量类Constant,如下:
package com.example.goodforjava.constant;
public class Constant {
public static final int P = 100;
}
有一个测试类Test,测试类中引用了Constant类中定义的常量P,如下:
package com.example.goodforjava.betterCode;
import com.example.goodforjava.constant.Constant;
public class Test {
public static void main(String[] args) {
System.out.println(Constant.P);
}
}
先分别编译下以上两个类,如下:
运行Test类,查看结果:
此时结果为100。
这时候,需求变更了,常量P的值需要改成66,但是因为项目启动太耗时了,所以开发决定直接改Constant类,如下:
package com.example.goodforjava.constant;
public class Constant {
public static final int P = 66;
}
然后编译该类,把该类的class文件放到服务器上直接替换覆盖原先的文件,因为是本地环境,所以只需要再次编译Constant类就好了,如下:
然后运行Test类,查看结果:
发现结果竟然还是100,这样就完全不符合业务要求了。
那么为什么会这样呢?看看Test类的class文件,如下:
会发现常量P的值100已经直接被写到了字节码文件中了,所以如果只编译常量类Constant,而不重新编译引用了它的类比如Test类,那么Test类字节码就不会变,那么修改就是无效的,结果依旧是旧的。
总结
为了提高代码的执行效率,对于final修饰的基本类型和String类型,编译器会认为它是稳定态的,所以就像上图中class文件内容所体现的一样,编译器会直接在编译时就把值100直接写入到字节码文件中,避免了运行期引用。所以建议不要做这种直接替换类文件的操作,因为一旦修改的常量被大量的类引用,而引用该常量的那些类又没有重新编译,那引起的问题就很难排查了,特别是一般会做这种替换文件操作的,都是生产环境。