主动引用触发类初始化(执行<clinit>方法),被动引用则不会;五种主动引用场景包括:new实例、读写非final静态字段、调用静态方法、Class.forName反射、子类初始化时父类未初始化则先触发父类初始化。

主动引用会触发类的初始化(执行 <clinit> 方法),被动引用则不会——这是理解 Java 类加载机制最关键的分水岭。类可能早已被加载进内存,但静态代码块、静态变量的显式赋值,只在初始化阶段发生,且仅一次;而是否走到这一步,全看 JVM 是否判定为“主动引用”。
主动引用的五种明确场景
JVM 规范严格限定,有且仅有以下五种情况属于主动引用,会强制触发类的初始化(连带完成此前未进行的加载、验证、准备、解析):
-
使用 new 创建实例:如
new User(),对应字节码new指令;注意new User[10]是anewarray指令,属于被动引用,不触发初始化。 -
读取或设置非 final 静态字段:如
User.count++或System.out.println(User.name),对应getstatic/putstatic指令;但static final int VERSION = 1这类编译期常量,值已内联到调用处,访问它不触发初始化。 -
调用静态方法:如
User.doLogin(),对应invokestatic指令,JVM 必须确保类已就绪才能执行该方法。 -
反射调用 Class.forName("xxx"):该方法默认
initialize = true,会触发初始化;而ClassLoader.loadClass("xxx")不触发。 -
初始化子类时,父类未初始化则先触发父类初始化:如
new Son(),若Father尚未初始化,则先执行Father.<clinit>();接口则不同,父接口不因子接口初始化而被初始化。
被动引用的三种典型表现
这些行为看似“用了类”,实则只需类的部分信息,JVM 不必执行其静态逻辑:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
通过子类访问父类的静态字段:如
Son.value(value在Parent中定义),只会初始化Parent,Son的静态块不会执行。 -
仅声明数组类型:如
User[] users = new User[5],JVM 动态生成的是[LUser;这一新数组类型,User类本身不初始化。 -
访问 static final 编译期常量:如
Config.TIMEOUT(值为3000),编译后该数值直接写入调用类的常量池,运行时完全不接触Config类,哪怕删掉Config.class文件也不影响运行。
为什么这个区分特别重要
初始化是类生命周期中唯一执行 Java 代码的阶段。很多关键逻辑依赖它:JDBC 驱动注册靠 Driver 类的静态块、配置类加载靠静态初始化器、框架插件自动发现也常基于此。若误用被动引用(比如只声明数组却指望配置类初始化),程序就会静默失败——没有报错,但功能缺失。反过来,若在不该初始化的地方触发(如误用 Class.forName 加载大量工具类),又可能拖慢启动、浪费资源。
立即学习“Java免费学习笔记(深入)”;

















