Java单例模式:从实战到面试的深度解析

结论先行

  1. 饿汉式:线程安全但可能造成资源浪费,推荐在初始化成本低的场景使用
  2. 懒汉式:需要解决线程安全问题,推荐使用双重检查锁+volatile优化
  3. 静态内部类:最佳实践方案,完美平衡延迟加载与线程安全
  4. 枚举单例:JDK1.5+推荐方案,天然防反射/序列化破坏
  5. 实际开发中优先选择枚举或静态内部类实现

文章持续更新,可以微信搜一搜「 半个脑袋儿 」第一时间阅读


一、核心实现方式

1. 饿汉式
java 复制代码
class ClassA {
    private static final ClassA INSTANCE = new ClassA();
    
    public static ClassA getInstance() {
        return INSTANCE;
    }
    
    private ClassA() {} // 防止反射创建
}

特点

  • 类加载时立即初始化(可能造成资源浪费)
  • 天然线程安全
  • 需处理反射攻击(添加私有构造器判空逻辑)
2. 双重检查锁懒汉式
java 复制代码
class ClassB {
    private static volatile ClassB instance;
    
    public static ClassB getInstance() {
        if (instance == null) {                     // 第一次检查
            synchronized (ClassB.class) {            // 同步锁
                if (instance == null) {             // 第二次检查
                    instance = new ClassB();
                }
            }
        }
        return instance;
    }
    
    private ClassB() {}
}

关键点

  • volatile防止指令重排序(JDK5+的JMM修复)
  • 两次null检查确保性能与线程安全
  • 仍可能被反射破坏单例
3. 静态内部类
java 复制代码
class ClassC {
    private static class Holder {
        static final ClassC INSTANCE = new ClassC();
    }
    
    public static ClassC getInstance() {
        return Holder.INSTANCE;
    }
    
    private ClassC() {}
}

优势

  • 利用类加载机制保证线程安全
  • 实现延迟加载(调用getInstance时才会初始化)
  • 代码简洁无锁
4. 枚举式
java 复制代码
enum EnumSingleton {
    INSTANCE;
    
    public void businessMethod() {
        // 业务方法
    }
}

绝对优势

  • 天生防反射攻击(枚举类没有构造器)
  • 自动处理序列化/反序列化
  • 代码极度简洁

二、实战应用场景

1. 配置管理类
java 复制代码
public enum ConfigManager {
    INSTANCE;
    
    private Properties props = new Properties();
    
    ConfigManager() {
        try(InputStream is = getClass().getResourceAsStream("/app.properties")) {
            props.load(is);
        }
    }
    
    public String getProperty(String key) {
        return props.getProperty(key);
    }
}
2. 数据库连接池
java 复制代码
public class ConnectionPool {
    private static final int MAX_SIZE = 100;
    private BlockingQueue<Connection> pool = new ArrayBlockingQueue<>(MAX_SIZE);
    
    private static class Holder {
        static final ConnectionPool INSTANCE = new ConnectionPool();
    }
    
    private ConnectionPool() {
        // 初始化连接池
    }
    
    public static ConnectionPool getInstance() {
        return Holder.INSTANCE;
    }
    
    public Connection getConnection() throws InterruptedException {
        return pool.take();
    }
}
3. Spring中的单例
  • Spring默认的Bean作用域就是单例
  • 通过IOC容器管理生命周期
  • 与设计模式单例的区别:每个容器对应一个实例

三、高频面试题解析

Q1:DCL(双重检查锁)为什么要加volatile?

:防止指令重排序导致返回未初始化完成的对象。new操作不是原子操作,分为:

  1. 分配内存空间
  2. 初始化对象
  3. 将引用指向内存地址

不加volatile可能导致步骤2和3重排序,其他线程可能拿到未初始化完成的对象。

Q2:如何防止反射攻击?
java 复制代码
private ClassC() {
    if (Holder.INSTANCE != null) {
        throw new RuntimeException("禁止反射创建!");
    }
}
Q3:枚举单例如何防止反射?
  • 枚举类的构造方法由JVM特殊处理
  • 反射newInstance方法会直接抛出异常
Q4:单例对象什么时候会被回收?
  • 只有当加载该类的ClassLoader被回收时才会被回收
  • 一般情况(使用系统类加载器)会与JVM生命周期一致
Q5:单例模式的优缺点?

优点

  • 内存中只有一个实例,减少内存开销
  • 避免对资源的多重占用

缺点

  • 违背单一职责原则(既要管理实例又要处理业务)
  • 扩展困难(需要修改源码)
  • 测试困难(全局状态难以隔离)
相关推荐
酷ku的森13 分钟前
4.LinkedList的模拟实现:
java·开发语言
嘵奇27 分钟前
Spring Boot 断点续传实战:大文件上传不再怕网络中断
java·spring boot·后端
爱的叹息1 小时前
AI推荐系统的详细解析 +推荐系统中滤泡效应(Filter Bubble)的详细解析+ 基于Java构建电商推荐系统的分步实现方案,结合机器学习与工程实践
java·人工智能·机器学习
sinat_262292111 小时前
Java面试实战:谢飞机的求职记 - Spring Boot、Redis与微服务技术问答解析
java·spring boot·redis·微服务·分布式事务
东方芷兰1 小时前
Javase 基础入门 —— 02 基本数据类型
java·开发语言·笔记·spring·intellij-idea·idea
pwzs1 小时前
掌握常见 HTTP 方法:GET、POST、PUT 到 CONNECT 全面梳理
java·后端·http
chendilincd1 小时前
C++ 的史诗级进化:从C++98到C++20
java·c++·c++20
独行soc1 小时前
2025年渗透测试面试题总结-拷打题库08(题目+回答)
java·linux·运维·服务器·python·面试·职场和发展
~欸嘿2 小时前
pdf多文件合并
java·pdf
艾文伯特2 小时前
Maven集成模块打包&使用
java·maven