静态代码块在JVM中只执行一次,因其被编译进<clinit>方法并在类初始化阶段由JVM自动调用,而类初始化仅在首次主动使用时触发且受类加载器隔离约束。

静态代码块在JVM中只执行一次,是因为它绑定在类的初始化阶段,而每个类在同一个类加载器下只会被初始化一次。
类初始化触发时机决定执行次数
JVM规范规定,类初始化(<clinit></clinit>方法执行)仅在首次主动使用该类时发生。所谓“主动使用”,包括以下几种情况:
- 创建该类的实例(如
new MyClass()) - 访问该类的静态字段(非编译期常量,如
MyClass.staticVar) - 调用该类的静态方法(如
MyClass.doSomething()) - 反射调用(如
Class.forName("MyClass")) - 初始化子类时,若父类尚未初始化,则先触发父类初始化
- 启动包含main方法的类
一旦完成初始化,JVM会将该类标记为“已初始化”,后续所有使用都不会再次执行静态代码块。
静态代码块被合并进<clinit>方法
编译器会把类中所有静态变量赋值语句和静态代码块,按出现顺序合并生成一个名为 <clinit></clinit> 的特殊方法。这个方法:
- 由JVM在类初始化阶段自动调用,程序员无法直接调用
- 带有隐式锁(与类对象关联),保证多线程下也只执行一次
- 如果多个线程同时触发初始化,JVM确保只有一个线程执行
<clinit></clinit>,其余阻塞等待
类加载器隔离影响“仅一次”的范围
“仅执行一次”是针对特定类加载器 + 特定类全限定名而言的。这意味着:
- 同一个类被不同类加载器(如自定义ClassLoader)分别加载,会形成两个独立的Class对象,各自执行一次静态代码块
- 系统类加载器加载的
java.lang.String和自定义加载器加载的同名类,互不影响 - Web应用中,每个应用通常有独立的ClassLoader,所以重启应用或热部署时,静态代码块可能再次执行——这不是违反机制,而是进入了新的类生命周期
常见误解澄清
很多人误以为“只要类被加载(Loading)就执行静态块”,其实不是:
- 类加载(Loading)只是把字节码读入方法区,不执行任何Java代码
- 连接(Linking)中的验证、准备阶段,也只是分配内存、设置默认值(如 static int x = 0),不会执行
= 123或静态块 - 只有到初始化(Initialization)阶段,
<clinit></clinit>才真正运行
因此,“加载 ≠ 初始化”,静态代码块的执行严格受控于初始化规则,而非加载行为。

















