编译期常量在类加载准备阶段即被jvm直接注入值,不参与初始化阶段执行顺序;非编译期常量与静态代码块同属初始化阶段,严格按源码自上而下执行。

静态代码块的执行优先级高于常量(static final 基本类型或字符串字面量)的显式初始化,但前提是该常量不是编译期常量(即未被 JVM 视为“编译时常量”)。
什么是编译期常量
满足以下全部条件的 static final 字段才是编译期常量:
- 声明为 public static final(访问修饰符不影响判定,但常见)
- 类型是基本类型(如 int、boolean)或 String
- 初始化表达式是编译期可确定的常量表达式(如字面量、常量间运算: 10 + 5、"a" + "b")
例如:
public static final int MAX = 100; → 编译期常量
public static final String NAME = "Tom"; → 编译期常量
public static final int CODE = new Random().nextInt(); → 非编译期常量(运行时计算)
public static final List
执行顺序取决于是否为编译期常量
对于编译期常量,其值在类加载的**准备阶段(Preparation)**就被直接写入类的常量池,不经过初始化阶段;静态代码块则在**初始化阶段(Initialization)**按出现顺序执行。
因此:
- 编译期常量:值在静态代码块执行前就已“就位”,但不是靠代码块赋的值,而是由 JVM 直接注入
- 非编译期常量(如 static final Object、static final int 来自方法调用):其初始化语句与静态代码块一样,在初始化阶段按源码顺序执行——谁在前,谁先运行
典型执行顺序示例
看这段代码:
class Test {
static final int A = 1; // 编译期常量 → 准备阶段设为 1
static final int B = getValue(); // 非编译期常量 → 初始化阶段执行
static { System.out.println("Block 1"); } // 初始化阶段执行(在 B 之后?看位置!)
static final int C = 2; // 编译期常量 → 准备阶段设为 2
static { System.out.println("Block 2"); } // 初始化阶段执行
static int D = 3; // 普通静态变量 → 初始化阶段执行
static int getValue() {
System.out.println("getValue called");
return 99;
}
}
输出为:
getValue called Block 1 Block 2
说明:
• A 和 C 在准备阶段已确定,不触发任何打印
• getValue() 在初始化阶段首次用到 B 时调用 → 先于所有静态代码块(因 B 的初始化语句在第一个 static 块之前)
• 静态代码块按书写顺序执行
关键结论
- 编译期常量不参与“执行顺序”竞争,它们的值由 JVM 提前固化
- 非编译期常量的初始化语句和 static {} 块同属初始化阶段,严格按源码自上而下执行
- 不要依赖静态代码块去“修改” static final 字段(即使反射也不推荐,且对编译期常量无效)
- 若需复杂逻辑初始化 final 静态字段,建议用 static {} 块 + private static 变量中转,或使用 Holder 模式延迟初始化
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











