静态代码块本身不直接保证对象唯一性,而是依赖JVM类初始化机制:同一类加载器下仅执行一次,多线程安全,配合static变量实现单例唯一性;类加载器隔离会导致重复执行。

静态代码块本身不直接“保证对象唯一性”,它只是类初始化阶段的执行入口。真正保障静态对象唯一性的,是 JVM 对类加载和初始化过程的严格控制——静态代码块在其中扮演了关键但有限的角色。
静态代码块执行时机受 JVM 初始化机制保护
静态代码块不会随类加载(Loading)立即运行,而是在类的“初始化(Initialization)”阶段首次被主动使用时才执行。这个阶段由 JVM 用内部标记(initialization flag)管控,天然具备以下特性:
- 同一类加载器下,一个类只初始化一次,静态代码块也就只执行一次
- 多线程同时首次触发(如多个线程同时调用 getInstance()),JVM 会阻塞其余线程,仅允许一个线程执行静态块,完成后唤醒其他线程
- 父类静态块总在子类之前完成,且各自独立计数,互不影响
静态对象唯一性依赖 static 变量 + 类生命周期绑定
真正承载“唯一对象”的,是声明在静态代码块中或与其配合使用的 static 变量(如 private static ConfigManager instance)。它的唯一性来源于:
- 该变量属于类本身,存放在方法区,内存中仅一份
- 其生命周期与类一致:类加载时分配内存并初始化,类卸载时才销毁(通常整个应用运行期间都不会卸载)
- 所有线程、所有实例访问的都是同一个 static 字段,不存在副本
常见写法如何协同实现唯一性
典型单例实现中,静态代码块常与静态字段配合使用。例如饿汉式:
立即学习“Java免费学习笔记(深入)”;
public class ConfigManager {
private static ConfigManager instance;
static {
instance = new ConfigManager(); // 唯一实例在此创建
System.out.println("ConfigManager initialized");
}
private ConfigManager() {}
public static ConfigManager getInstance() {
return instance; // 总返回同一个引用
}
}
这种写法能确保唯一性,是因为:
- 静态代码块只执行一次,instance 只被赋值一次
- 后续所有 getInstance() 调用都返回已创建好的那个对象
- 构造器私有,外部无法通过 new 绕过控制
破坏唯一性的典型场景其实是类加载器问题
所谓“静态代码块执行了多次”,往往不是代码逻辑失效,而是 JVM 中出现了多个类加载器加载了同一个类:
- Spring Boot DevTools 热重启 → 新旧 WebAppClassLoader 并存 → 各自初始化一次
- OSGi 或模块化容器 → 不同 Bundle 使用独立 ClassLoader → 同名类被视为不同类
- 自定义 ClassLoader 每次 new 一个 → 加载的是“新类”,静态块重跑
验证方式很简单:在静态块里打印 getClassLoader().hashCode(),若输出多个不同值,说明是加载器隔离,而非静态机制失灵。


















