java静态代码块只执行一次,靠jvm类初始化机制保障:它被编译进线程安全的方法,在首次主动使用(如new、调用静态方法等)触发初始化阶段时执行,且同一类加载器下仅一次。

Java 中 static 静态代码块能保证只执行一次,靠的不是程序员手动控制,而是 JVM 类初始化机制的底层设计。
它绑定在类的初始化阶段,而非加载阶段
静态代码块不会在类被 ClassLoader.loadClass() 或 Class.forName("X", false, cl) 加载时运行。JVM 把类生命周期严格分为加载(Loading)、验证、准备、初始化(Initialization)、使用和卸载五个阶段。只有进入「初始化」阶段,静态代码块才会执行——此时它已被编译器合并进一个叫 <clinit></clinit> 的特殊方法里,由 JVM 自动调用。
-
<clinit></clinit>是私有、静态、无参数、不可重载、不可显式调用的方法 - 它按源码中静态变量赋值语句和
static{}块的文本顺序,自上而下合并生成 - 哪怕类被多次加载(同一 ClassLoader 下),只要没触发初始化,
<clinit></clinit>就一直不执行
JVM 用初始化标记 + 类对象锁确保线程安全
当多个线程首次同时访问该类(比如同时 new 实例或调用静态方法),JVM 会用该类对应的 Class 对象作为 monitor 锁住初始化流程:
- 仅一个线程获得锁并执行
<clinit></clinit> - 其余线程阻塞等待,直到初始化完成
- 初始化成功后,JVM 将该类标记为“已初始化”,后续所有访问直接跳过
<clinit></clinit> - 若初始化失败(如 static 块抛出未捕获异常),类被标记为“初始化失败”,之后任何访问都抛
NoClassDefFoundError,且不可恢复
“只一次”是针对类加载器 + 类名组合而言的
所谓“只执行一次”,本质是 JVM 对「同一个类加载器实例 + 同一个全限定类名」这一组合的承诺:
- Tomcat 中两个 WAR 包含同名类 → 各自有 WebAppClassLoader → 各跑一次 static 块
- Spring Boot DevTools 热部署 → 启用新 ClassLoader 替换旧的 → 新加载器视为全新类 → static 块重跑
- OSGi Bundle 或自定义 ClassLoader 每次 new 一个 → 每个加载器独立命名空间 → 各自初始化
- 可通过
MyClass.class.getClassLoader()打印哈希值验证是否真属同一加载器
主动使用才触发,非主动则静默
以下操作首次发生时,才会触发初始化和 static 块执行:
- new 该类实例
- 调用该类任意静态方法
- 读写该类非 final 的静态字段
-
Class.forName("X")(默认initialize = true) - 子类初始化且父类尚未初始化
而这些操作不会触发:
- 仅声明引用(
MyClass ref;) - 访问
public static final编译期常量(如int PORT = 8080;) -
ClassLoader.loadClass("X")或Class.forName("X", false, cl)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











