Java类初始化本质是执行静态代码块和静态变量赋值,仅在六种主动使用场景下触发:new实例、调用静态方法、访问非final静态字段、Class.forName()反射、子类初始化时父类未初始化、main类启动;其他如.class字面量、编译期常量引用等不触发。

Java 中触发类的初始化阶段,本质是触发 类的静态初始化块(static {})和静态变量的赋值语句 执行。这发生在类首次被“主动使用”时,由 JVM 保证只执行一次。
哪些操作会触发类初始化
根据 Java 虚拟机规范,只有以下六种情况属于“主动使用”,会强制触发类的初始化:
- 创建该类的实例(如 new MyClass())
- 调用该类的静态方法(如 MyClass.doSomething())
- 访问该类的静态字段(非 final 常量;注意:final static 基本类型或字符串字面量常量不会触发初始化)
- 反射调用(如 Class.forName("com.example.MyClass") —— 注意:不带参数的 forName 默认 init=true)
- 初始化子类时,若父类尚未初始化,则先初始化父类(接口的初始化不触发其父接口初始化)
- 作为启动类(含 main 方法)被 JVM 启动时
常见误区与注意事项
有些看似“用了类”的操作并不会触发初始化:
- MyClass.class 或 MyClass.class.getSuperclass():只是获取 Class 对象,属于“被动使用”,不触发初始化
- final static int VAL = 42;:编译期常量,直接内联到调用处,引用它不会触发定义类的初始化
- final static List<String> LIST = Arrays.asList("a");:不是编译期常量(对象引用),访问它会触发初始化
- 数组类型 MyClass[]:创建数组不会触发 MyClass 初始化(只加载类,不初始化)
手动触发初始化的推荐方式
如果需要确保类已初始化(例如在框架、插件或延迟加载场景中),最明确的方式是:
立即学习“Java免费学习笔记(深入)”;
- 调用一个空的 public static 方法:MyClass.ensureInitialized();(自己加个空方法)
- 用反射并显式指定 init=true:Class.forName("MyClass", true, MyClass.class.getClassLoader());
- 读取一个非常量的 static 字段(哪怕只是 MyClass.TRIGGER_INIT;,只要它不是 final 编译期常量)
验证是否已初始化的小技巧
可在静态代码块中加日志或断点观察执行时机:
public class TestInit {
static {
System.out.println("TestInit 正在初始化");
}
public static void doWork() { /* ... */ }
}
然后分别测试:new TestInit()、TestInit.doWork()、TestInit.class —— 只有前两者会输出那行日志。


















