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

Java 类的初始化不是一上来就执行的,而是等到第一次被“主动使用”时才触发,并且严格按继承链和代码书写顺序执行静态内容。关键不在“什么时候加载”,而在“什么时候真正运行 static 代码块和静态变量赋值”。
哪些操作算主动引用
只有以下六种情形之一发生,JVM 才会强制执行类的初始化(即运行 <clinit> 方法):
- 用
new创建该类的第一个实例(包括反射或反序列化) - 访问或修改该类的非
final静态字段(读/写都算) - 调用该类的任意静态方法
- 通过
Class.forName("全限定名")反射加载(注意:ClassLoader.loadClass()不触发) - JVM 启动时直接运行含
main()方法的主类 - 初始化某个子类时,其父类尚未初始化,则先完成父类初始化
哪些看似调用其实不算主动引用
这些属于“被动引用”,只可能触发类加载(到准备阶段),但不会进入初始化,也就不会执行 static{} 块:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 声明变量但未实际访问:如
MyClass obj; - 创建数组:如
MyClass[] arr = new MyClass[10];(只加载数组类,不初始化MyClass) - 引用
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,则已被编译器优化跳过

















