被动引用不会触发类初始化,典型情形包括:编译期常量(static final 基本类型/String)的访问、子类引用父类非final静态字段、创建类的数组对象、使用.class字面量或ClassLoader.loadClass获取Class对象。

引用一个类但不触发其初始化,是 JVM 类加载机制中“被动引用”的典型表现。关键在于:只有主动使用才会执行 <clinit>(静态初始化块和静态变量赋值),而被动引用仅完成符号解析或类型检查,跳过初始化阶段。
编译期常量的直接访问
当字段被声明为 static final,且类型是基本类型(如 int、boolean)或 String,并且值在编译期就能确定,JVM 会把该值内联到调用处的字节码中。运行时根本不会加载、更不会初始化定义它的类。
- ✅ 正确示例:
public static final int MAX_RETRY = 3;→ 引用它不会触发类初始化 - ❌ 非编译期常量(会触发):
static final Integer PORT = 8080;(包装类型)、static final String MSG = new String("ok");(运行期创建)、static final long TS = System.currentTimeMillis();
子类访问父类的静态字段(非 final 或非常量)
子类代码中读取父类定义的静态字段(如 Parent.count),只要该字段不是编译期常量,且子类未在自身静态上下文中首次写入或调用依赖它的逻辑,就属于被动引用。此时父类只完成加载和链接(准备、解析),<clinit> 不执行。
- ✅ 安全场景:
System.out.println(Parent.VERSION);(VERSION 是static int,未被子类静态块读写) - ❌ 触发初始化:
Parent.count++;或子类static { System.out.println(Parent.count); }—— 因为这是对字段的“主动使用”
创建类的数组对象
执行 new SomeClass[10] 时,JVM 只会加载并链接 SomeClass(确保类型存在),但绝不会触发其初始化。因为数组本身是 JVM 动态生成的新类型(如 [LSomeClass;),与元素类型的静态初始化无关。
- 数组创建只初始化数组类型(如
[Ljava.lang.String;),不初始化元素类型(String) - 即使
SomeClass有静态块输出日志,new SomeClass[10]也不会打印
获取 Class 对象(.class 字面量或 ClassLoader.loadClass)
使用 SomeClass.class 或 ClassLoader.loadClass("xxx.SomeClass") 获取 Class 实例,属于“类型加载”,只走到连接阶段,不执行初始化。
-
Class> c = SomeClass.class;→ 不触发初始化 -
Class> c = ClassLoader.getSystemClassLoader().loadClass("xxx.SomeClass");→ 同样不触发 - ⚠️ 注意:
Class.forName("xxx.SomeClass")默认会初始化(因重载方法含initialize=true参数)

















