单例模式详解(java)

以下是一个线程安全、防反射攻击、防序列化破坏的单例模式完整实现,结合真实场景问题解决方案,附带逐行中文注释:

java 复制代码
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/ObjectInputStream
        return obj; // 假设返回同一个对象(实际测试时应替换为真实序列化代码)
    }
}

真实场景问题解决

多线程环境竞争问题

使用双重检查锁定 + volatile 确保线程安全

比纯synchronized方法性能更高

反射攻击问题

构造器中增加实例存在性检查

防御通过反射创建新实例

序列化破坏问题

实现readResolve()方法

保证反序列化返回现有实例

延迟加载优化

避免饿汉式的资源浪费

真正需要时才初始化实例

方案对比

实现方式 线程安全 防反射 防序列化 延迟加载 代码复杂度
饿汉式 ✅ ❌ ❌ ❌ 低
懒汉式(sync) ✅ ❌ ❌ ✅ 中
静态内部类 ✅ ❌ ❌ ✅ 低
枚举 ✅ ✅ ✅ ❌ 低
DCL ✅ ✅ ✅ ✅ 高

建议选择:

简单场景:枚举单例(JDK5+)

复杂需求:本文的DCL防御增强版

需要极致性能:静态内部类+防御增强

相关推荐
智能直播16 分钟前
RK3528 V4L2 框架 UVC 接口自动获取 H.264 / MJPEG / YUYV 格式:从内核采集到硬解分发
java·网络·实时音视频·视频编解码·h.264
171320330计算机毕设编程24 分钟前
计算机毕业设计 基于YOLO改进算法的车辆特征识别系统设计与实现--lw
java·大数据·python·sql·算法
Web极客码25 分钟前
Python Ollama 本地 Agent 入门与订单查询实践
开发语言·jvm·python
badhope37 分钟前
Spring AI Alibaba停更:Java程序员的信任,还能被辜负几次?
java·spring
const_qiu44 分钟前
第一章 1.1-1.2:基础模型调用与 TypeScript 入门 —— Java 开发者的视角
java·javascript·typescript
Wang's Blog1 小时前
Java 项目实战: 交友项目 - Dubbo 负载均衡策略与实战验证
java·dubbo·交友
SL_staff1 小时前
销售知识库7天落地实践:面向业务侧的零代码嵌入方案
java·开源·产品
kimnoic1 小时前
PHP 8.2的DNF类型怎么用才规范
开发语言·php
GreatSQL社区1 小时前
主从复制SQL线程中断——字符集砸出的坑
java·数据库·算法
_Evan_Yao1 小时前
前后端联调怎么排查问题?从 Network 到后端日志,一步步定位错误
开发语言·后端·团队开发·需求分析