静态内部类单例是Java中兼顾延迟加载和线程安全的最简可靠方案,依赖JVM类加载机制保证初始化一次且线程安全,但需手动防御反射和反序列化攻击。

静态内部类单例是目前 Java 中兼顾延迟加载和线程安全的最简可靠方案,不需要 synchronized、volatile 或双重检查,JVM 类加载机制天然兜底。
为什么 SingletonHolder 被加载时才创建实例
Java 规范保证:一个类只有在首次主动使用时才会触发初始化( 执行),而 SingletonHolder 是静态内部类,它的初始化只发生在 getInstance() 方法里第一次访问 Holder.INSTANCE 时。
这意味着:
- 外部类
Singleton加载时,SingletonHolder完全不加载,更不会执行new Singleton() - 多个线程同时调用
getInstance(),JVM 会确保只有一个线程执行SingletonHolder的初始化逻辑,其余阻塞等待——这是 JVM 层面的互斥,不是代码写的锁 - 一旦初始化完成,后续所有访问都直接返回已构造好的
INSTANCE,零开销
getInstance() 返回的是 final 引用,为什么不能被替换
Holder.INSTANCE 是 static final 字段,其引用在类初始化阶段就固定了,且 JVM 保证该赋值对所有线程立即可见(happens-before 关系)。
所以你不需要:
- 给
INSTANCE加volatile(它已经是不可变引用) - 在
getInstance()里加同步块(JVM 已经做了) - 担心指令重排序导致部分构造(对象构造完成才赋值给
final字段)
反序列化或反射攻击下是否还安全
静态内部类方案本身不防御反射或反序列化——这点常被忽略。
如果业务场景涉及网络传输、持久化或框架自动反序列化(如 Spring Bean 反序列化),必须额外补两处:
- 私有构造器里加非空校验:
if (instance != null) throw new RuntimeException("Use getInstance()"); - 实现
readResolve()方法,强制返回getInstance(),防止反序列化新建对象
否则,仅靠静态内部类结构,无法阻止通过 Reflection.getConstructor().newInstance() 或反序列化绕过单例约束。
真正关键的不是写法多漂亮,而是意识到:JVM 类加载机制只保“初始化一次”,不保“构造逻辑不被绕过”。生产环境只要存在反射调用或反序列化入口,就必须手动加固。











