Java类初始化顺序是“静态优先、父类先行、源码顺序决定执行次序”:类加载时先执行父类静态变量与静态块(按源码顺序),再子类静态部分;对象创建时先父类实例变量与构造块,再父类构造方法,然后子类实例变量与构造块,最后子类构造方法。

Java类初始化不是按“写在哪”随意执行,而是严格遵循JVM规范的确定性流程:静态优先、父类先行、源码顺序决定执行次序。
静态成员:类加载时一次性按序执行
静态变量赋值和静态代码块被合并进同一个<clinit>方法,JVM从上到下逐行扫描执行:
- 谁在源码里写得靠前,谁就先运行——比如
static int a = initA();在static { ... }前面,那initA()一定先调用 - 静态块中不能引用其后才声明的静态变量(编译报错“非法前向引用”),哪怕只是读取也不行
- 变量即使只声明没赋值(如
static String s;),也会在准备阶段设为null,但显式初始化动作仍按位置执行 - 任一静态初始化语句抛出未捕获异常(如
NullPointerException),整个<clinit>中断,后续对该类所有访问都会触发NoClassDefFoundError
继承场景:父子类静态部分强制分层执行
子类首次主动使用时,JVM保证父类静态成员全部初始化完成后再执行子类静态部分:
- 顺序固定为:父类静态变量 → 父类静态块 → 子类静态变量 → 子类静态块
- 这个顺序由双亲委派机制保障,无法跳过或倒置
- 子类静态块里可安全使用父类
public static字段;反过来则不行——父类静态块不能依赖子类静态状态 - final static基本类型常量(如
public static final int MAX = 100;)属于编译期常量,不触发类初始化
实例成员:每次new都重走一遍父→子链路
对象创建时,实例变量、非静态代码块和构造方法按继承层级逐层展开:
立即学习“Java免费学习笔记(深入)”;
- 先父类:按源码顺序执行实例变量赋值 + 非静态代码块,再进入父类构造方法体
- 再子类:同样顺序执行子类实例变量赋值 + 非静态代码块,最后执行子类构造方法体
- 非静态代码块和字段初始化语句地位等同,编译后被插入每个构造方法开头(
super()之后),所以总在构造逻辑之前运行 - 注意:实例部分每次
new都执行,而静态部分全局只执行一次
验证与调试:看清真实执行流
最直接的方式是加带标识的日志输出,配合首次触发点观察:
- 在
main第一行前访问某个静态字段(如System.out.println(Child.flag);),就能完整触发类初始化流程 - 区分“类加载”(字节码载入内存)和“类初始化”(
<clinit>执行)——只有后者才运行静态块和变量赋值 - 用
javap -c 类名查看生成的<clinit>字节码,能清晰看到语句合并后的实际执行顺序 - 避免在静态工厂方法里做复杂初始化却不兜住底层异常,否则会导致类永久不可用


















