主动引用会触发类初始化(执行<clinit>),被动引用仅加载不初始化;主动引用包括:new实例、访问/修改非final静态字段、调用静态方法、Class.forName反射、启动main类、子类初始化时父类未初始化;被动引用包括:子类访问父类静态字段、创建数组、引用编译期常量。

类的主动引用会触发初始化(执行 <clinit>()),被动引用则只可能走到加载、准备阶段,但跳过初始化——关键看是否真正“用到”类的运行时行为,比如执行静态代码块、给非编译期常量赋值、或创建实例。
哪些操作算主动引用?
以下情形一定会触发类初始化:
-
用 new 创建实例:如
new MyClass(),JVM 必须确保类已初始化才能分配对象内存并执行构造逻辑。 -
读取或修改非 final 静态字段:如访问
MyClass.count(count是普通static变量)或赋值MyClass.count = 5;但若字段是static final且为编译期常量(如int、String字面量),则不触发。 -
调用静态方法:如
MyClass.doWork(),JVM 必须先完成初始化,才能执行方法体中的静态逻辑。 -
反射调用:如
Class.forName("MyClass")或MyClass.class.getDeclaredMethod(...).invoke(...),属于显式要求类就绪。 -
启动主类或首次初始化子类:JVM 启动时先初始化含
main方法的类;首次初始化子类时,若父类未初始化,则先触发父类初始化(接口不适用此规则)。
哪些看似“用了类”,其实不算主动引用?
这些操作不会触发初始化,属于被动引用:
-
通过子类访问父类的静态字段:例如
SubClass.value,其中value在SuperClass中定义。JVM 只初始化SuperClass,SubClass的静态块不会执行。 -
仅声明或创建数组:如
MyClass[] arr = new MyClass[10]。此时 JVM 创建的是「数组类」(如[LMyClass;),而非初始化MyClass本身。 -
引用编译期常量:如
public static final boolean ENABLE = true。该值在编译时就被内联到调用方字节码中,运行时完全不访问原类,删掉其.class文件也不影响运行。
为什么区分主动和被动很重要?
初始化阶段执行静态代码块和静态变量显式赋值,是类获得运行时状态的关键一步。如果误以为某次引用会触发初始化(比如声明数组、访问 final 常量),可能导致:
立即学习“Java免费学习笔记(深入)”;
- 数据库连接、日志配置等依赖静态块的逻辑未执行;
- 静态变量仍为默认值(如
0、null),而非预期的显式初值; - 调试时发现“类明明被用了,但静态块没打印”,其实是被动引用绕过了初始化。
不复杂但容易忽略。


















