静态代码块本身不会重复执行,所谓“重复”实为多个类加载器各自加载同一类并独立初始化;需通过日志输出类加载器身份确认,常见于热部署、OSGi、微服务等场景。

静态代码块本身不会重复执行,所谓“重复”,其实是多个类加载器各自加载了同一个类,导致每个加载器都独立触发了一次初始化。排查关键不在代码逻辑,而在运行时环境的类加载机制。
看日志输出类加载器身份
在可疑静态块里加一行打印:
System.out.println("Init by: " + MyClass.class.getClassLoader());
如果看到不同地址或不同类名(如 WebAppClassLoader@1a2b3c 和 RestartClassLoader@4d5e6f),就确认是多加载器问题。常见于:
立即学习“Java免费学习笔记(深入)”;
- Tomcat 或 Spring Boot DevTools 的热重启机制
- OSGi 插件容器中模块级隔离
- 微服务中自定义 ClassLoader 加载配置类
检查是否被子类意外触发
父类静态块里若直接引用子类的静态字段(如 Child.CONFIG),会强制子类初始化——但此时子类自己的静态变量还没赋值,可能返回默认值,还可能引发二次初始化错觉。典型表现是:
- 父类静态块刚执行一半,突然跳进子类静态块
- 子类静态字段输出为 null 或 0,而非预期值
- 日志顺序混乱,比如“Parent init”后紧接“Child init”,再回到“Parent continue”
验证是否真有多个类实例
用 JVM 工具确认类是否被多次定义:
jcmd
更直接的方式是在启动后执行:
System.out.println(MyClass.class + " loaded by " + MyClass.class.getClassLoader());
如果同一类名对应多个 Class 对象(hashcode 不同),说明 JVM 视其为不同类,静态块自然各跑一次。
用加载器标识做单次守门
不依赖 JVM 的“类只加载一次”假设,改用应用层控制:
private static final Map
static {
ClassLoader loader = MyClass.class.getClassLoader();
if (INIT_MAP.putIfAbsent(loader, true) == null) {
doRealInit();
}
}
这种方式能跨不同加载器实例确保逻辑只执行一次,尤其适合配置加载、监听器注册等敏感操作。


















