静态代码块在类初始化阶段执行且仅执行一次,前提是同一ClassLoader加载的同一全限定类名;它被编译进JVM自动生成的线程安全的<clinit>方法,仅在首次主动使用类时触发。

静态代码块不是“一加载就执行”,而是等到类真正初始化时才跑,而且只跑一次——这个“一次”是有前提的:同一个类加载器 + 同一个类名。
静态代码块其实在初始化阶段才执行
很多人误以为调用 ClassLoader.loadClass() 或 Class.forName("X") 就会立刻执行 static 块,其实不然。JVM 类生命周期分五步:加载 → 验证 → 准备 → 初始化 → 使用/卸载。静态代码块被编译进 JVM 自动生成的 <clinit> 方法,而这个方法只在“初始化”阶段触发。
-
ClassLoader.loadClass("X")默认只走到加载阶段,<clinit>不执行 -
Class.forName("X", false, cl)第二个参数为false时,同样不初始化 - 只有首次主动使用类(如 new 实例、访问非 final 静态字段、调用静态方法)才会触发初始化
是 JVM 自动合成的线程安全方法
编译器会把所有静态变量赋值语句和 static{} 块,按源码顺序合并成一个叫 <clinit> 的私有静态方法。它不可见、不可调用、不能重写,但 JVM 会在初始化时自动执行。
- JVM 保证每个类在每个 ClassLoader 下,
<clinit>最多执行一次 - 若多个线程同时首次触发初始化,JVM 用类对象的 monitor 锁住该过程,仅一个线程执行,其余等待
- 执行完成后,该类被标记为“已初始化”,后续任何访问都不再进入
<clinit>
“只执行一次”的边界在哪?
所谓“仅一次”,是针对“ClassLoader 实例 + 全限定类名”这个组合而言的。以下情况会导致 static 块再次执行:
立即学习“Java免费学习笔记(深入)”;
- Web 容器热部署:旧 ClassLoader 卸载,新 ClassLoader 加载同名类 → 新的
<clinit> - 每次 new 一个自定义 ClassLoader 去 load 类 → 每个 ClassLoader 是独立命名空间
- OSGi 或模块系统中,不同模块导出同名类 → 实际是不同 Class 对象
- 可通过
MyClass.class.hashCode()判断是否为同一 Class 实例;不同 ClassLoader 加载的同名类,哈希值一定不同
常见误区与验证方式
别光看代码写了 static 块,得确认它真被触发了。最直接的方式是加日志并观察输出时机:
- 用
javap -c MyClass查看字节码,能找到<clinit>方法体 - 在 static 块里打印日志,配合
ClassLoader.loadClass()和Class.forName("X", true, cl)对比效果 - 注意 final 静态常量(如
public static final int N = 1)属于编译期常量,访问它不会触发初始化


















