类变量初始化发生在类加载五步中的“初始化”阶段,排在加载、验证、准备之后;该阶段由jvm执行方法,按源码顺序依次执行静态变量显式赋值和静态代码块,且父类优先于子类,仅触发一次。

类变量(即用 static 修饰的字段)在类加载的初始化阶段完成赋值,不是在编译时、也不是在对象创建时,而是在 JVM 第一次主动使用该类时,由 <clinit></clinit> 方法统一执行——这个过程只发生一次,且严格按源码顺序与继承关系推进。
初始化发生在哪个环节?
它属于类加载五步中的“初始化”阶段,排在“加载→验证→准备”之后:
- 准备阶段:JVM 为类变量分配内存,并设默认值(如 int=0、Object=null);此时不执行任何赋值语句或代码块。
-
初始化阶段:JVM 执行
<clinit></clinit>方法,按源码中声明顺序,依次执行静态变量显式赋值和静态代码块;父类的<clinit></clinit>总是先于子类执行。
什么操作会真正触发初始化?
只有“主动引用”才会走到初始化这一步,常见情形包括:
- 使用
new创建该类实例 - 读写该类的非 final 静态字段(如
MyClass.count++) - 调用该类的静态方法
- 反射调用
Class.forName("MyClass")(注意:forName(name, false, loader)中第二个参数为false时不触发) - 初始化子类时,若父类尚未初始化,则强制先初始化父类
反例:访问编译期常量(如 public static final int MAX = 100)不会触发类初始化,因为其值在编译时就内联进调用处了。
初始化顺序的关键细节
顺序不是凭经验猜的,而是 JVM 确保的确定性流程:
- 父类静态部分 → 子类静态部分(哪怕子类先被引用)
- 同一类中,按源码自上而下:先遇到的静态变量赋值,再静态代码块,再下一个静态变量……
- 若某静态变量依赖另一个(如
static int b = a + 1),则a必须已执行完赋值;否则用的是默认值(如a还未初始化就为 0) - 多个静态代码块并存时,也严格按出现顺序执行,彼此之间可相互访问已初始化的静态变量
ClassLoader 对初始化的影响
同一个类名,被不同 ClassLoader 加载,会产生两个独立的 Class 对象:
- 它们各自的静态变量互不可见、互不干扰;各自拥有独立的
<clinit></clinit>执行轨迹 - 比如 Web 容器用自定义加载器重载
Config.class,新类中的static String ENV初始为null,直到它的<clinit></clinit>被触发 - 双亲委派机制防止核心类(如
java.lang.Object)被重复加载,从而避免静态状态分裂











