java静态代码块只执行一次,前提是类未被卸载重加载;它在类初始化阶段由jvm执行且仅触发1次,多个静态块按源码顺序依次执行。

Java静态代码块只执行一次,前提是类没有被卸载再重新加载。JVM规范中并未强制要求类卸载,常规应用(如普通Java SE程序、Spring Boot单体应用)里类一旦加载就不会卸载,所以静态代码块天然“只执行一次”。但类重加载(Class Reloading)属于特殊场景,需要明确触发条件和环境支持。
静态代码块默认只执行一次
静态代码块在类的<clinit>()</clinit>方法中被收集,由JVM在类初始化阶段(Initialization Phase)执行,且整个JVM生命周期内仅触发1次。只要类未被卸载,哪怕反复调用静态方法、创建无数对象、甚至反射获取Class对象,都不会再次执行。
- 首次访问该类的任何静态成员(字段/方法)会触发类加载与初始化
- 使用
new创建实例也会触发(因需确保类已初始化) - 子类初始化时,若父类尚未初始化,会先触发父类的
<clinit>()</clinit> - 多个静态代码块按源码书写顺序依次执行,各自仅运行一次
类重加载才能让静态代码块再次执行
所谓“再次执行”,本质是JVM先卸载原类(即从ClassLoader中移除对应的Class对象),再用新ClassLoader加载同名类——此时视为一个全新类,其<clinit>()</clinit>会重新运行。这在标准JDK中无法通过常规手段实现,需依赖特定机制:
- 热部署工具:如JRebel、Spring DevTools,在代码修改后替换ClassLoader并重新加载类
-
自定义ClassLoader:每次用新实例加载同一类(注意:原类实例与新类实例不兼容,类型转换会抛
ClassCastException) - OSGi容器:模块化环境中可动态安装/卸载Bundle,伴随类的卸载与重载
- Web容器(如Tomcat):应用重启或重部署时,旧WebAppClassLoader被丢弃,新实例加载类,静态块重跑
实战中容易误判“多次执行”的常见原因
开发者有时观察到静态代码块“好像执行了多次”,其实多是环境或理解偏差导致:
- 日志输出被多个进程/容器共用(如IDE多次启动、Docker重复运行)→ 实际是不同JVM实例各自执行1次
- 使用了多个自定义ClassLoader(如测试中反复new URLClassLoader)→ 每个ClassLoader加载的类是独立的,各自有独立的静态块执行
- 误将构造代码块(非static的
{})当成静态代码块 → 构造代码块每次new都会执行 - 日志打印位置在静态块内部但被异步逻辑干扰(如Log4j配置错误导致重复输出)→ 并非代码块重复执行
验证是否真发生重加载的小技巧
可通过以下方式确认静态块是否因重加载而再次触发:
- 打印
MyClass.class.getClassLoader()的hashCode,两次执行结果不同说明ClassLoader已更换 - 检查
MyClass.class.getProtectionDomain().getCodeSource(),路径变化常意味着新类加载 - 在静态块中抛异常并查看堆栈,观察
java.lang.ClassLoader.loadClass调用链是否来自新ClassLoader - 启用JVM参数
-verbose:class,观察类加载/卸载日志(注意:标准JDK不打印卸载,部分增强版JVM或工具支持)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











