Java类初始化严格遵循静态优先、父类优先、代码顺序执行原则,仅在六种主动使用场景触发,分静态(父→子)和实例(父→子)两阶段按源码顺序执行。

分析 Java 类加载机制下的初始化顺序,关键在于区分“类加载阶段”和“类初始化阶段”,并抓住触发时机与执行层级。类加载本身不等于初始化,只有在特定条件下才会进入初始化,而初始化又严格遵循静态优先、父类优先、代码顺序执行三大原则。
一、先确认是否真的触发了类初始化
类加载(Loading)只是把字节码读入内存并生成 Class 对象;真正的初始化(Initialization)只在以下六种主动使用场景发生:
- 用 new 创建该类实例
- 访问该类的静态变量(非编译期常量,即非 static final 基本类型或字符串字面量)
- 调用该类的静态方法
- 通过反射调用 Class.forName()
- 初始化子类时,会强制先初始化父类(但不会反过来)
- 运行含 main() 方法的类(该类被 JVM 主动启动)
注意:访问 static final 编译期常量、数组创建、子类引用父类静态字段等属于“被动引用”,不会触发子类初始化。
二、初始化内部顺序分两层:静态 → 实例,父类 → 子类
一旦触发初始化,JVM 按固定顺序执行,不可跳过、不可乱序:
立即学习“Java免费学习笔记(深入)”;
-
静态部分(只执行一次,按类首次主动使用时发生):
① 父类静态变量默认值(准备阶段已设,如 int=0)→ 显式赋值或静态块
② 父类静态代码块(static{})
③ 子类静态变量显式赋值
④ 子类静态代码块 -
实例部分(每次 new 时执行):
⑤ 父类实例变量默认值 → 显式赋值
⑥ 父类实例代码块({})
⑦ 父类构造器(隐含先调super())
⑧ 子类实例变量显式赋值
⑨ 子类实例代码块
⑩ 子类构造器
所有静态内容按源码从上到下顺序执行;实例内容同样严格按书写位置先后执行,哪怕变量声明在代码块之后,也会先完成赋值再进代码块。
三、验证顺序最直接的方法:加打印日志
在每个静态/实例变量赋值、静态/实例块、构造器中插入 System.out.println(),例如:
// 示例片段
class Parent {<br> static String s1 = log("Parent 静态变量");<br> static { log("Parent 静态块"); }<br> String i1 = log("Parent 实例变量");<br> { log("Parent 实例块"); }<br> Parent() { log("Parent 构造器"); }<br>}
运行 new Child() 后观察输出顺序,就能清晰对应到上述 10 步。这是理解复杂继承链初始化逻辑最可靠的方式。
四、常见误区提醒
容易混淆的点需要特别注意:
- “准备阶段”不执行任何 Java 代码:静态变量仅设默认值(0/null/false),显式赋值和 static{} 都在“初始化阶段”才执行
- 静态代码块不是类加载时立刻执行:类加载(Loading)可能早于初始化,真正执行 static{} 是在首次主动使用触发初始化时
- 子类构造器第一行默认是 super(),它会拉起父类实例初始化流程,但父类静态部分早已在之前完成
- 接口也有初始化,规则类似类,但接口无构造器、无实例块,只有 static 变量和 static{},且实现类初始化不会自动触发接口初始化


















