Java类初始化仅在六种主动调用情形下触发:new实例、访问非final静态字段、调用静态方法、Class.forName反射、启动main类、子类初始化时父类未初始化;被动引用如声明变量、创建数组等不触发初始化。

Java 类在第一次被主动调用时触发初始化,核心在于 JVM 对“主动使用”的判定——不是声明、不是引用,而是真正触及类的运行时行为。这个过程不依赖编译期信息,也不随类加载(Loading)立即发生,而是在“初始化”阶段才执行静态代码块和静态变量赋值。
哪些操作算“主动调用”
只有以下六种情形之一发生,JVM 才会强制对该类执行初始化(即运行 <clinit> 方法):
- 用
new创建该类的第一个实例(包括通过反射或反序列化) - 访问该类的非
final静态字段(读或写),比如MyClass.count - 调用该类的任意静态方法,如
MyClass.doInit() - 通过
Class.forName("MyClass")反射加载(注意:ClassLoader.loadClass()不触发) - JVM 启动时直接运行含
main()的主类 - 初始化某个子类时,若其父类尚未初始化,则先完成父类初始化
哪些看似调用其实不算
这些操作属于“被动引用”,只可能触发类加载(到准备阶段),但不会进入初始化,也就不会执行 static{} 块:
- 仅声明变量:
MyClass obj; - 创建数组:
MyClass[] arr = new MyClass[10]; - 引用
public static final编译期常量(如int PORT = 8080),值已被内联进调用方字节码 - 使用
MyClass.class字面量 - 子类访问父类的静态字段(仅初始化父类,不触发子类初始化)
初始化的实际执行顺序
一旦触发,JVM 按源码顺序交错执行静态变量显式赋值与 static{} 块,且严格遵循继承链:父类先于子类完成整个初始化流程。
立即学习“Java免费学习笔记(深入)”;
- 准备阶段已为所有
static字段分配内存并设默认值(如int为 0,Object为null) - 初始化阶段才真正执行
public static int x = 5;中的= 5,以及所有static{}内容 - 接口的静态字段同理,但无
static{}块;其初始化表达式(如static int N = compute();)在此阶段求值
怎么验证是否真的触发了初始化
最直接的方式是在 static{} 里加日志或断点:
- 运行时首次出现该日志,说明初始化刚发生
- 后续任何调用(再
new、再访问静态字段)都不会重复执行 - 用
javap -c查看调用方字节码:若看到getstatic指令,说明未内联,确有初始化;若为ldc,则已被编译器优化跳过


















