深入理解设计模式之单例模式

深入理解设计模式之单例模式(Singleton Pattern)

一、引言

在软件开发中,有些对象我们只需要一个实例。比如:配置管理器、线程池、缓存、日志对象、数据库连接池等。如果创建多个实例,不仅浪费资源,还可能导致数据不一致等问题。

想象这样的场景:你的应用需要读取配置文件,如果每个模块都创建自己的配置对象,不仅浪费内存,还可能导致配置不一致。更好的方式是整个应用共享一个配置实例。

单例模式就像公司里的CEO:无论你从哪个部门、哪个入口找他,找到的都是同一个人。全公司只有一个CEO,所有人都通过同一个渠道(如CEO办公室)来访问他。

本文将深入探讨单例模式的多种实现方式、线程安全问题、如何防止反射和序列化破坏单例,并结合Spring、Runtime等框架应用,帮助你全面掌握这一最常用的设计模式。

二、什么是单例模式

2.1 定义

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

2.2 核心思想

  • 唯一实例:确保类只有一个实例
  • 私有构造:防止外部通过new创建实例
  • 全局访问:提供静态方法获取实例
  • 延迟加载:可选择在首次使用时才创建(懒汉式)

2.3 模式结构

scss 复制代码
单例模式结构:

┌────────────────────────────────┐
│         Singleton              │
├────────────────────────────────┤
│ - instance: Singleton          │  私有静态实例
├────────────────────────────────┤
│ - Singleton()                  │  私有构造函数
│ + getInstance(): Singleton     │  公共静态方法
└────────────────────────────────┘

调用方式:
Singleton obj = Singleton.getInstance();

内存模型:
┌─────────────────────────────────────┐
│              JVM内存                 │
├─────────────────────────────────────┤
│  方法区(元空间)                     │
│  ┌─────────────────────────────┐    │
│  │ Singleton.class             │    │
│  │  └─ static instance ────────┼────┼──┐
│  └─────────────────────────────┘    │  │
│                                     │  │
│  堆                                  │  │
│  ┌─────────────────────────────┐    │  │
│  │   Singleton对象              │◀───┼──┘
│  │   (唯一实例)                 │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

所有调用getInstance()的地方都指向同一个对象

三、单例模式的多种实现

3.1 饿汉式(静态常量)

类加载时就创建实例,线程安全但不支持延迟加载。

java 复制代码
/**
 * 饿汉式单例(静态常量)
 * 优点:线程安全,实现简单
 * 缺点:类加载时就创建,可能造成资源浪费
 */
public class EagerSingleton {

    // 类加载时就创建实例
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    // 私有构造函数
    private EagerSingleton() {
        System.out.println("[EagerSingleton] 实例被创建");
    }

    // 公共访问方法
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }

    public void doSomething() {
        System.out.println("执行业务逻辑");
    }
}

3.2 懒汉式(线程不安全)

首次调用时才创建,但多线程环境下不安全。

java 复制代码
/**
 * 懒汉式单例(线程不安全)
 * 优点:延迟加载
 * 缺点:多线程环境下可能创建多个实例
 */
public class LazySingletonUnsafe {

    private static LazySingletonUnsafe instance;

    private LazySingletonUnsafe() {
        System.out.println("[LazySingletonUnsafe] 实例被创建");
    }

    // 线程不安全!
    public static LazySingletonUnsafe getInstance() {
        if (instance == null) {
            // 多个线程可能同时进入这里
            instance = new LazySingletonUnsafe();
        }
        return instance;
    }
}

/**
 * 演示线程不安全问题
 */
class UnsafeDemo {
    public static void main(String[] args) {
        // 多线程并发获取实例
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                LazySingletonUnsafe instance = LazySingletonUnsafe.getInstance();
                System.out.println(Thread.currentThread().getName() +
                                 " : " + instance.hashCode());
            }).start();
        }
        // 可能输出不同的hashCode,说明创建了多个实例
    }
}

3.3 懒汉式(同步方法)

使用synchronized保证线程安全,但性能较差。

java 复制代码
/**
 * 懒汉式单例(同步方法)
 * 优点:线程安全
 * 缺点:每次调用都加锁,性能差
 */
public class LazySingletonSafe {

    private static LazySingletonSafe instance;

    private LazySingletonSafe() {
        System.out.println("[LazySingletonSafe] 实例被创建");
    }

    // synchronized保证线程安全
    public static synchronized LazySingletonSafe getInstance() {
        if (instance == null) {
            instance = new LazySingletonSafe();
        }
        return instance;
    }
}

3.4 双重检查锁(DCL)

兼顾线程安全和性能,是最常用的实现方式。

java 复制代码
/**
 * 双重检查锁单例(Double-Checked Locking)
 * 优点:线程安全,性能好,延迟加载
 * 注意:必须使用volatile防止指令重排序
 */
public class DCLSingleton {

    // volatile防止指令重排序
    private static volatile DCLSingleton instance;

    private DCLSingleton() {
        System.out.println("[DCLSingleton] 实例被创建");
    }

    public static DCLSingleton getInstance() {
        // 第一次检查:避免不必要的同步
        if (instance == null) {
            synchronized (DCLSingleton.class) {
                // 第二次检查:确保只创建一个实例
                if (instance == null) {
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

/**
 * 为什么需要volatile?
 *
 * instance = new DCLSingleton() 不是原子操作,包括:
 * 1. 分配内存空间
 * 2. 初始化对象
 * 3. 将instance指向内存地址
 *
 * 没有volatile时,JVM可能重排序为1→3→2
 * 线程A执行到3时,线程B判断instance!=null
 * 但对象还未初始化完成,导致使用未初始化的对象
 */

3.5 静态内部类

利用类加载机制实现延迟加载和线程安全。

java 复制代码
/**
 * 静态内部类单例
 * 优点:线程安全,延迟加载,实现简单
 * 推荐使用!
 */
public class StaticInnerSingleton {

    private StaticInnerSingleton() {
        System.out.println("[StaticInnerSingleton] 实例被创建");
    }

    /**
     * 静态内部类
     * 只有在调用getInstance()时才会加载
     * 类加载机制保证线程安全
     */
    private static class SingletonHolder {
        private static final StaticInnerSingleton INSTANCE =
            new StaticInnerSingleton();
    }

    public static StaticInnerSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

/**
 * 类加载过程:
 * 1. 加载StaticInnerSingleton类时,不会加载SingletonHolder
 * 2. 调用getInstance()时,才加载SingletonHolder
 * 3. 类加载过程是线程安全的(由JVM保证)
 * 4. 所以既实现了延迟加载,又保证了线程安全
 */

3.6 枚举单例

最简洁、最安全的实现方式,天然防止反射和序列化攻击。

java 复制代码
/**
 * 枚举单例
 * 优点:
 * 1. 线程安全(JVM保证)
 * 2. 防止反射攻击
 * 3. 防止序列化破坏
 * 4. 代码简洁
 *
 * 《Effective Java》推荐的最佳实现!
 */
public enum EnumSingleton {

    INSTANCE;

    // 可以有自己的属性和方法
    private String config;

    public void setConfig(String config) {
        this.config = config;
    }

    public String getConfig() {
        return config;
    }

    public void doSomething() {
        System.out.println("枚举单例执行业务逻辑");
    }
}

/**
 * 使用方式
 */
class EnumSingletonDemo {
    public static void main(String[] args) {
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;

        System.out.println("instance1 == instance2: " + (instance1 == instance2));

        instance1.setConfig("配置信息");
        instance1.doSomething();
    }
}

3.7 各实现方式对比

diff 复制代码
┌─────────────────┬──────────┬──────────┬──────────┬──────────┐
│    实现方式      │ 线程安全 │ 延迟加载 │ 防反射   │ 防序列化  │
├─────────────────┼──────────┼──────────┼──────────┼──────────┤
│ 饿汉式          │    ✓     │    ✗     │    ✗     │    ✗     │
│ 懒汉式(不安全)  │    ✗     │    ✓     │    ✗     │    ✗     │
│ 懒汉式(同步)    │    ✓     │    ✓     │    ✗     │    ✗     │
│ 双重检查锁      │    ✓     │    ✓     │    ✗     │    ✗     │
│ 静态内部类      │    ✓     │    ✓     │    ✗     │    ✗     │
│ 枚举            │    ✓     │    ✗     │    ✓     │    ✓     │
└─────────────────┴──────────┴──────────┴──────────┴──────────┘

推荐:
- 一般情况:静态内部类
- 需要防止反射/序列化:枚举
- 简单场景:饿汉式

四、防止单例被破坏

4.1 防止反射攻击

java 复制代码
/**
 * 防止反射攻击的单例
 */
public class ReflectionSafeSingleton {

    private static volatile ReflectionSafeSingleton instance;
    private static boolean created = false;

    private ReflectionSafeSingleton() {
        // 防止反射攻击
        synchronized (ReflectionSafeSingleton.class) {
            if (created) {
                throw new RuntimeException("单例已存在,禁止通过反射创建!");
            }
            created = true;
        }
        System.out.println("[ReflectionSafeSingleton] 实例被创建");
    }

    public static ReflectionSafeSingleton getInstance() {
        if (instance == null) {
            synchronized (ReflectionSafeSingleton.class) {
                if (instance == null) {
                    instance = new ReflectionSafeSingleton();
                }
            }
        }
        return instance;
    }
}

/**
 * 测试反射攻击
 */
class ReflectionAttackDemo {
    public static void main(String[] args) throws Exception {
        // 正常获取实例
        ReflectionSafeSingleton instance1 = ReflectionSafeSingleton.getInstance();
        System.out.println("instance1: " + instance1.hashCode());

        // 尝试通过反射创建
        try {
            java.lang.reflect.Constructor<ReflectionSafeSingleton> constructor =
                ReflectionSafeSingleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            ReflectionSafeSingleton instance2 = constructor.newInstance();
            System.out.println("instance2: " + instance2.hashCode());
        } catch (Exception e) {
            System.out.println("反射攻击被阻止: " + e.getCause().getMessage());
        }
    }
}

4.2 防止序列化破坏

java 复制代码
import java.io.*;

/**
 * 防止序列化破坏的单例
 */
public class SerializationSafeSingleton implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final SerializationSafeSingleton INSTANCE =
        new SerializationSafeSingleton();

    private SerializationSafeSingleton() {
        System.out.println("[SerializationSafeSingleton] 实例被创建");
    }

    public static SerializationSafeSingleton getInstance() {
        return INSTANCE;
    }

    /**
     * 防止序列化破坏单例
     * 反序列化时,JVM会调用此方法返回实例
     */
    private Object readResolve() {
        return INSTANCE;
    }
}

/**
 * 测试序列化攻击
 */
class SerializationAttackDemo {
    public static void main(String[] args) throws Exception {
        SerializationSafeSingleton instance1 = SerializationSafeSingleton.getInstance();

        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance1);

        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        SerializationSafeSingleton instance2 =
            (SerializationSafeSingleton) ois.readObject();

        System.out.println("instance1: " + instance1.hashCode());
        System.out.println("instance2: " + instance2.hashCode());
        System.out.println("instance1 == instance2: " + (instance1 == instance2));
    }
}

五、实际生产场景应用

5.1 场景:配置管理器

应用程序的全局配置,应该只有一个实例。

java 复制代码
import java.util.*;

/**
 * 配置管理器单例
 */
public class ConfigManager {

    private static volatile ConfigManager instance;
    private Map<String, String> configs;

    private ConfigManager() {
        System.out.println("[ConfigManager] 加载配置文件...");
        configs = new HashMap<>();
        loadConfigs();
    }

    public static ConfigManager getInstance() {
        if (instance == null) {
            synchronized (ConfigManager.class) {
                if (instance == null) {
                    instance = new ConfigManager();
                }
            }
        }
        return instance;
    }

    private void loadConfigs() {
        // 模拟从配置文件加载
        configs.put("db.url", "jdbc:mysql://localhost:3306/mydb");
        configs.put("db.username", "root");
        configs.put("db.password", "password");
        configs.put("server.port", "8080");
        configs.put("log.level", "INFO");
        System.out.println("[ConfigManager] 配置加载完成");
    }

    public String getConfig(String key) {
        return configs.get(key);
    }

    public String getConfig(String key, String defaultValue) {
        return configs.getOrDefault(key, defaultValue);
    }

    public void setConfig(String key, String value) {
        configs.put(key, value);
    }

    public void display() {
        System.out.println("\n当前配置:");
        configs.forEach((k, v) -> System.out.println("  " + k + " = " + v));
    }
}

/**
 * 测试配置管理器
 */
class ConfigManagerDemo {
    public static void main(String[] args) {
        System.out.println("========== 配置管理器单例 ==========\n");

        // 多个模块获取配置
        ConfigManager config1 = ConfigManager.getInstance();
        ConfigManager config2 = ConfigManager.getInstance();
        ConfigManager config3 = ConfigManager.getInstance();

        System.out.println("\n验证单例: " + (config1 == config2 && config2 == config3));

        // 读取配置
        System.out.println("\n数据库URL: " + config1.getConfig("db.url"));
        System.out.println("服务端口: " + config2.getConfig("server.port"));

        // 修改配置(所有引用都能看到变化)
        config1.setConfig("log.level", "DEBUG");
        System.out.println("修改后的日志级别: " + config3.getConfig("log.level"));

        config1.display();
    }
}

5.2 场景:数据库连接池

数据库连接池应该是全局唯一的。

java 复制代码
import java.util.*;
import java.util.concurrent.*;

/**
 * 简单的数据库连接池单例
 */
public class ConnectionPool {

    private static volatile ConnectionPool instance;

    private BlockingQueue<Connection> pool;
    private int maxSize;
    private int currentSize;

    private ConnectionPool(int maxSize) {
        this.maxSize = maxSize;
        this.currentSize = 0;
        this.pool = new LinkedBlockingQueue<>(maxSize);

        System.out.println("[ConnectionPool] 初始化连接池,最大连接数: " + maxSize);

        // 预创建一些连接
        for (int i = 0; i < 3; i++) {
            pool.offer(createConnection());
        }
    }

    public static ConnectionPool getInstance() {
        if (instance == null) {
            synchronized (ConnectionPool.class) {
                if (instance == null) {
                    instance = new ConnectionPool(10);
                }
            }
        }
        return instance;
    }

    /**
     * 获取连接
     */
    public Connection getConnection() throws InterruptedException {
        Connection conn = pool.poll();

        if (conn == null) {
            if (currentSize < maxSize) {
                conn = createConnection();
            } else {
                // 等待可用连接
                conn = pool.take();
            }
        }

        System.out.println("[ConnectionPool] 获取连接: " + conn.getId() +
                         ",当前池中: " + pool.size());
        return conn;
    }

    /**
     * 归还连接
     */
    public void releaseConnection(Connection conn) {
        if (conn != null) {
            pool.offer(conn);
            System.out.println("[ConnectionPool] 归还连接: " + conn.getId() +
                             ",当前池中: " + pool.size());
        }
    }

    private Connection createConnection() {
        currentSize++;
        Connection conn = new Connection("CONN-" + currentSize);
        System.out.println("[ConnectionPool] 创建新连接: " + conn.getId());
        return conn;
    }

    /**
     * 连接类
     */
    public static class Connection {
        private String id;

        public Connection(String id) {
            this.id = id;
        }

        public String getId() {
            return id;
        }

        public void execute(String sql) {
            System.out.println("  [" + id + "] 执行: " + sql);
        }
    }
}

/**
 * 测试连接池
 */
class ConnectionPoolDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("========== 连接池单例 ==========\n");

        ConnectionPool pool = ConnectionPool.getInstance();

        // 获取连接并执行
        ConnectionPool.Connection conn1 = pool.getConnection();
        conn1.execute("SELECT * FROM users");

        ConnectionPool.Connection conn2 = pool.getConnection();
        conn2.execute("INSERT INTO logs VALUES(...)");

        // 归还连接
        pool.releaseConnection(conn1);
        pool.releaseConnection(conn2);

        // 再次获取(会复用之前的连接)
        ConnectionPool.Connection conn3 = pool.getConnection();
        conn3.execute("UPDATE users SET status = 1");
        pool.releaseConnection(conn3);
    }
}

5.3 场景:日志记录器

全局日志记录器应该是唯一的。

java 复制代码
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 日志记录器单例
 */
public class Logger {

    private static class LoggerHolder {
        private static final Logger INSTANCE = new Logger();
    }

    public enum Level {
        DEBUG, INFO, WARN, ERROR
    }

    private Level currentLevel = Level.INFO;
    private DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private Logger() {
        System.out.println("[Logger] 日志记录器初始化");
    }

    public static Logger getInstance() {
        return LoggerHolder.INSTANCE;
    }

    public void setLevel(Level level) {
        this.currentLevel = level;
    }

    public void debug(String message) {
        log(Level.DEBUG, message);
    }

    public void info(String message) {
        log(Level.INFO, message);
    }

    public void warn(String message) {
        log(Level.WARN, message);
    }

    public void error(String message) {
        log(Level.ERROR, message);
    }

    private void log(Level level, String message) {
        if (level.ordinal() >= currentLevel.ordinal()) {
            String time = LocalDateTime.now().format(formatter);
            System.out.printf("[%s] [%s] %s%n", time, level, message);
        }
    }
}

/**
 * 测试日志记录器
 */
class LoggerDemo {
    public static void main(String[] args) {
        System.out.println("========== 日志记录器单例 ==========\n");

        Logger logger = Logger.getInstance();

        logger.setLevel(Logger.Level.DEBUG);

        logger.debug("这是调试信息");
        logger.info("这是普通信息");
        logger.warn("这是警告信息");
        logger.error("这是错误信息");

        System.out.println("\n修改日志级别为WARN:");
        logger.setLevel(Logger.Level.WARN);

        logger.debug("这条不会显示");
        logger.info("这条不会显示");
        logger.warn("这条会显示");
        logger.error("这条会显示");
    }
}

六、开源框架中的应用

6.1 JDK Runtime

Runtime类是典型的饿汉式单例。

java 复制代码
/**
 * JDK Runtime单例示例
 *
 * Runtime源码:
 * public class Runtime {
 *     private static Runtime currentRuntime = new Runtime();
 *
 *     public static Runtime getRuntime() {
 *         return currentRuntime;
 *     }
 *
 *     private Runtime() {}
 * }
 */
public class RuntimeDemo {
    public static void main(String[] args) {
        System.out.println("========== JDK Runtime单例 ==========\n");

        Runtime runtime1 = Runtime.getRuntime();
        Runtime runtime2 = Runtime.getRuntime();

        System.out.println("runtime1 == runtime2: " + (runtime1 == runtime2));

        // 获取系统信息
        System.out.println("\n系统信息:");
        System.out.println("可用处理器数: " + runtime1.availableProcessors());
        System.out.println("最大内存: " + runtime1.maxMemory() / 1024 / 1024 + " MB");
        System.out.println("总内存: " + runtime1.totalMemory() / 1024 / 1024 + " MB");
        System.out.println("空闲内存: " + runtime1.freeMemory() / 1024 / 1024 + " MB");
    }
}

6.2 Spring单例Bean

Spring容器默认使用单例作用域管理Bean。

java 复制代码
/**
 * Spring单例Bean示例
 *
 * Spring的单例实现(简化版)
 */
class SimpleSpringContainer {

    // 单例Bean缓存
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 获取Bean(单例)
     */
    @SuppressWarnings("unchecked")
    public <T> T getBean(String beanName, Class<T> beanClass) {
        // 先从缓存获取
        Object bean = singletonObjects.get(beanName);

        if (bean == null) {
            synchronized (this.singletonObjects) {
                bean = singletonObjects.get(beanName);
                if (bean == null) {
                    // 创建Bean
                    try {
                        bean = beanClass.getDeclaredConstructor().newInstance();
                        System.out.println("[Spring] 创建单例Bean: " + beanName);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    // 放入缓存
                    singletonObjects.put(beanName, bean);
                }
            }
        }

        return (T) bean;
    }
}

/**
 * 用户Service
 */
class UserService {
    public UserService() {
        System.out.println("[UserService] 构造函数被调用");
    }

    public void save() {
        System.out.println("保存用户");
    }
}

/**
 * 测试Spring单例
 */
class SpringSingletonDemo {
    public static void main(String[] args) {
        System.out.println("========== Spring单例Bean ==========\n");

        SimpleSpringContainer container = new SimpleSpringContainer();

        // 多次获取同一个Bean
        UserService service1 = container.getBean("userService", UserService.class);
        UserService service2 = container.getBean("userService", UserService.class);
        UserService service3 = container.getBean("userService", UserService.class);

        System.out.println("\nservice1 == service2: " + (service1 == service2));
        System.out.println("service2 == service3: " + (service2 == service3));
        System.out.println("构造函数只被调用一次(单例)");
    }
}

/**
 * 真实的Spring使用:
 *
 * @Service  // 默认是单例
 * public class UserService {
 *     // ...
 * }
 *
 * @Service
 * @Scope("prototype")  // 原型作用域,每次获取都是新实例
 * public class RequestHandler {
 *     // ...
 * }
 */

七、单例模式的优缺点

7.1 优点

1. 节省资源

复制代码
只创建一个实例,避免频繁创建销毁
特别适合资源密集型对象(如数据库连接)

2. 全局访问点

复制代码
提供统一的访问入口
避免传递对象引用

3. 避免数据不一致

复制代码
全局只有一份数据
避免多个实例之间的数据冲突

7.2 缺点

1. 违反单一职责

复制代码
单例类既负责业务逻辑,又负责管理自己的实例

2. 隐藏依赖关系

复制代码
通过静态方法获取,不通过构造函数注入
依赖关系不明显,难以测试

3. 并发问题

复制代码
多线程环境下需要考虑线程安全
共享状态可能导致并发问题

4. 难以扩展

arduino 复制代码
单例类通常是final的
难以继承和扩展

八、最佳实践

8.1 选择合适的实现方式

java 复制代码
/**
 * 实现方式选择指南
 */

// 1. 简单场景,不需要延迟加载 → 饿汉式
public class SimpleSingleton {
    private static final SimpleSingleton INSTANCE = new SimpleSingleton();
    public static SimpleSingleton getInstance() { return INSTANCE; }
}

// 2. 需要延迟加载 → 静态内部类(推荐)
public class LazyLoadSingleton {
    private static class Holder {
        private static final LazyLoadSingleton INSTANCE = new LazyLoadSingleton();
    }
    public static LazyLoadSingleton getInstance() { return Holder.INSTANCE; }
}

// 3. 需要防止反射和序列化攻击 → 枚举(最安全)
public enum SafestSingleton {
    INSTANCE;
}

// 4. 需要传递参数 → 双重检查锁
public class ParameterizedSingleton {
    private static volatile ParameterizedSingleton instance;
    private String param;

    private ParameterizedSingleton(String param) {
        this.param = param;
    }

    public static ParameterizedSingleton getInstance(String param) {
        if (instance == null) {
            synchronized (ParameterizedSingleton.class) {
                if (instance == null) {
                    instance = new ParameterizedSingleton(param);
                }
            }
        }
        return instance;
    }
}

8.2 考虑使用依赖注入

java 复制代码
/**
 * 使用依赖注入代替直接获取单例
 */

// 不推荐:直接获取单例
class BadService {
    public void doSomething() {
        ConfigManager config = ConfigManager.getInstance();
        String value = config.getConfig("key");
    }
}

// 推荐:通过构造函数注入
class GoodService {
    private final ConfigManager config;

    public GoodService(ConfigManager config) {
        this.config = config;
    }

    public void doSomething() {
        String value = config.getConfig("key");
    }
}

九、总结

9.1 核心要点

  1. 单例模式的本质:确保类只有一个实例,并提供全局访问点
  2. 实现方式:饿汉式、懒汉式、双重检查锁、静态内部类、枚举
  3. 线程安全:除懒汉式(不安全版)外,其他都是线程安全的
  4. 推荐实现:静态内部类(延迟加载)或枚举(最安全)

9.2 使用场景

复制代码
适合使用单例的场景:
✓ 配置管理器
✓ 数据库连接池
✓ 线程池
✓ 日志记录器
✓ 缓存

9.3 实现方式选择

markdown 复制代码
推荐优先级:
1. 枚举(最安全,防反射/序列化)
2. 静态内部类(延迟加载,简单)
3. 双重检查锁(需要参数化)
4. 饿汉式(简单场景)

相关推荐
程序员西西1 小时前
Spring Boot整合MyBatis调用存储过程?
java·后端
2501_941879811 小时前
Python在微服务高并发异步API网关请求处理与智能路由架构中的实践
java·开发语言
AAA简单玩转程序设计1 小时前
Java进阶小白手册:基础玩法升级,告别青铜套路
java
whltaoin1 小时前
【 手撕Java源码专栏 】Spirng篇之手撕SpringBean:(包含Bean扫描、注册、实例化、获取)
java·后端·spring·bean生命周期·手撕源码
用户3721574261351 小时前
使用 Java 删除 Word 文档中的水印
java
艾斯比的日常1 小时前
Java 三色标记算法:并发垃圾回收的核心技术解析
java·开发语言·算法
空空kkk2 小时前
MyBatis——代理Dao方式的增删改查操作
java·数据库·mybatis
Seven972 小时前
线性数据结构
java
带刺的坐椅2 小时前
Solon 不依赖 Java EE 是其最有价值的设计!
java·spring·web·solon·javaee