以下是一个线程安全、防反射攻击、防序列化破坏的单例模式完整实现,结合真实场景问题解决方案,附带逐行中文注释:
import java.io.Serializable;
import java.lang.reflect.Constructor;/*** 单例模式终极实现方案(解决:线程安全、反射攻击、序列化破坏问题)*/
public class UltimateSingleton implements Serializable {// 1. volatile保证可见性和禁止指令重排private static final volatile UltimateSingleton instance = new UltimateSingleton();// 2. 私有构造器(防止外部new实例化)private UltimateSingleton() {// 防御反射攻击:当尝试通过反射创建实例时,抛出异常// 由于instance 是static只会在初始化构建一次,instance 必定是初始化创建的// final的目的是防止静态对象被攻击者直接通过反射设置instance if (instance != null) {throw new RuntimeException("禁止通过反射创建单例!");}}/*** 3. 双重检查锁定实现延迟加载,如果是静态的就不需要这里,但是这种会导致可以通过反射加载单例*/public static UltimateSingleton getInstance() {// 第一次检查:避免不必要的同步,加快性能if (instance == null) {synchronized (UltimateSingleton.class) {// 第二次检查:确保只有一个线程进入后创建实例if (instance == null) {instance = new UltimateSingleton();}}}return instance;}/*** 4. 防御反序列化破坏单例* 在反序列化时直接返回已有实例* 如果类定义了 readResolve() 方法,反序列化完成后,JVM 会自动调用该方法* 并用其返回值替换新创建的对象。通过在该方法中返回已有的单例实例,可以避免创建新对象。*/protected Object readResolve() {return getInstance();}// 示例方法public void showMessage() {System.out.println("我是终极单例对象!");}// ------------------- 测试代码 -------------------public static void main(String[] args) throws Exception {// 正常获取实例UltimateSingleton s1 = UltimateSingleton.getInstance();s1.showMessage(); // 输出:我是终极单例对象!// 尝试通过反射破坏单例try {Class<UltimateSingleton> clazz = UltimateSingleton.class;Constructor<UltimateSingleton> constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true);UltimateSingleton s2 = constructor.newInstance(); // 这里会抛出异常} catch (Exception e) {System.out.println("反射攻击被防御:" + e.getMessage());// 输出:反射攻击被防御:禁止通过反射创建单例!}// 验证序列化/反序列化安全性UltimateSingleton s3 = SerializationUtils.roundTripSerialize(s1);System.out.println("序列化是否破坏单例:" + (s1 != s3)); // 输出:false(说明未破坏)}
}// 辅助序列化工具类(模拟序列化操作)
class SerializationUtils {public static <T> T roundTripSerialize(T obj) throws Exception {// 这里需要实现真实的序列化/反序列化逻辑// 由于演示需要简化,实际应该用ObjectOutputStream/ObjectInputStreamreturn obj; // 假设返回同一个对象(实际测试时应替换为真实序列化代码)}
}
真实场景问题解决
多线程环境竞争问题
使用双重检查锁定 + volatile 确保线程安全
比纯synchronized方法性能更高
反射攻击问题
构造器中增加实例存在性检查
防御通过反射创建新实例
序列化破坏问题
实现readResolve()方法
保证反序列化返回现有实例
延迟加载优化
避免饿汉式的资源浪费
真正需要时才初始化实例
方案对比
实现方式 | 线程安全 | 防反射 | 防序列化 | 延迟加载 | 代码复杂度 |
---|---|---|---|---|---|
饿汉式 | ✅ | ❌ | ❌ | ❌ | 低 |
懒汉式(sync) | ✅ | ❌ | ❌ | ✅ | 中 |
静态内部类 | ✅ | ❌ | ❌ | ✅ | 低 |
枚举 | ✅ | ✅ | ✅ | ❌ | 低 |
DCL | ✅ | ✅ | ✅ | ✅ | 高 |
建议选择:
简单场景:枚举单例(JDK5+)
复杂需求:本文的DCL防御增强版
需要极致性能:静态内部类+防御增强