2MUCH

编译期常量

2022-11-22


编译期常量

先说结论,编译期常量=final+常量

java四个阶段

参考:https://www.modb.pro/db/211851

除了第一个阶段我们能直接干预,剩余三个阶段,都是jvm自己执行的。第二阶段是 非人工干预的 第一阶段。在这个阶段就能确定的值,我们就称为**「编译期常量」。那么即使后面第三阶段和第四阶段不走,对它也没有影响,而类加载就发生在第三阶段,所以: 「编译期常量不会触发类加载」。

编译期常量定义方式

// final定义,且是常量,即为编译期常量。而且,编译期常量不存在赋值语句,只存在初始化语句。
public final int a = 10000;
public static final int b  = 10000;

反例:

public int a = 10; // 没用final修饰,不是编译期常量
public final int b = System.currentTimeMillis(); // 值不是常量,所以不是编译期常量

编译期常量的表现

编译后的.class文件中,编译期变量被**「ConstantValue」**修饰。

public class Class {
	public final int a = 10000;
	public static final int b  = 10000;
}
// 查看编译后...
public final int a;
    descriptor: I
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    ConstantValue: int 10000  // 有ConstantValue,说明是编译期常量
public static final int b;
    descriptor: I
    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 10000 // 有ConstantValue,说明是编译期常量

此外

被static修饰的是类一级的,非static修饰的是对象一级的。对象一级的要先创建对象才能使用,所以肯定会触发类加载(不管是不是编译期常量)。

在实际开发中,如果A类之前调用到了B类的编译期常量,当修改B的编译期常量值后,还需要再次编译A类,否则A类对于该值的调用还是修改之前的值(因为已经固定在class文件里了)。

参考链接

https://www.modb.pro/db/211851