Java 设计模式——单例模式6种写法:从原理到 SpringBoot 落地
Java 设计模式——单例模式6种写法:从原理到 SpringBoot 落地
单例模式是 Java 中最基础却最易踩坑的设计模式 —— 看似简单的 “保证一个实例”,背后藏着线程安全、懒加载、反序列化破坏等诸多细节。本文不仅拆解 6 种单例写法的核心代码,更会通过优缺点对比、场景适配、SpringBoot 实战案例,帮你彻底搞懂 “哪种写法适合你的项目”,以及 “如何避免单例被破坏”。
文章目录
- Java 设计模式——单例模式6种写法:从原理到 SpringBoot 落地
- 一、单例模式核心认知:为什么必须掌握?
- 二、6 种单例写法深度解析(附代码 + 原理 + 场景)
- 1. 静态内部类(推荐首选)
- 核心原理
- 代码实现
- 核心特点
- 适用场景
- 2. 双重校验锁(DCL,高并发场景首选)
- 核心原理
- 代码实现(JDK1.5+)
- 关键问题解析
- 核心特点
- 适用场景
- 3. 枚举(防破坏场景首选)
- 核心原理
- 代码实现(极简)
- 核心优势:天然防破坏
- 核心特点
- 适用场景
- 4. 饿汉式(简单场景可用)
- 核心原理
- 代码实现(两种写法)
- 核心特点
- 适用场景
- 5. 懒汉式(不推荐,仅作对比)
- 核心原理
- 代码实现
- 核心特点
- 适用场景
- 6. 非 synchronized 加锁(特殊场景可用)
- (1)CAS 实现(无锁并发)
- (2)ThreadLocal 实现(线程内单例)
- 三、6 种单例写法对比表(实战选型指南)
- 四、单例模式进阶:如何防止被破坏?
- 1. 防反射破坏:在私有构造器中添加校验
- 2. 防反序列化破坏:重写`readResolve()`方法
- 五、SpringBoot 实战:单例模式的实际应用
- 1. Spring 的单例 Bean:底层原理与配置
- 2. 实战场景 1:自定义单例工具类(与 Spring 整合)
- 3. 实战场景 2:单例 Bean 的线程安全问题(避坑)
- 六、单例模式常见误区与最佳实践
- 1. 常见误区(避坑指南)
- 2. 最佳实践(选型口诀)
- 七、总结:单例模式的本质是 “平衡”
一、单例模式核心认知:为什么必须掌握?
在开始之前,先明确单例模式的 “不可替代性”—— 它解决的是 “资源独占” 和 “全局访问” 的核心问题:
-
资源独占场景:工具类(如
RedisUtils)、配置类(如AppConfig)、线程池对象,如果重复创建会导致资源浪费或逻辑混乱; -
全局访问场景:系统中需要一个统一入口的组件(如日志工厂、缓存管理器),单例模式可避免到处传递实例;
-
框架底层依赖:Spring 中
Bean默认单例、MyBatis 的SqlSessionFactory,本质都是单例模式的应用。
实现单例模式的 3 个核心要素(缺一不可):
-
私有构造器:防止外部通过
new关键字创建实例; -
静态全局访问点:提供
getInstance()等静态方法,供外部获取唯一实例; -
线程安全:多线程环境下,确保实例只被创建一次(线程不安全的单例都是 “伪单例”)。
二、6 种单例写法深度解析(附代码 + 原理 + 场景)
以下按 “实战优先级” 排序,从最常用到特殊场景写法,逐一拆解:
1. 静态内部类(推荐首选)
核心原理
利用 Java 的类加载机制实现线程安全:静态内部类SingletonHolder只有在调用getInstance()时才会被加载,加载时创建外部类实例,且类加载过程由 JVM 保证线程安全。
代码实现
package com.fastech.designpattern.singleton;public class InnerClassSingleton {// 1. 私有构造器:禁止外部newprivate InnerClassSingleton() {// 可选:防止反射破坏(下文会详细讲)if (SingletonHolder.instance != null) {throw new IllegalStateException("单例已被实例化,禁止重复创建");}}// 2. 静态内部类:延迟加载,仅在调用getInstance时加载private static class SingletonHolder {// 类加载时创建实例,JVM保证线程安全private static final InnerClassSingleton instance = new InnerClassSingleton();}// 3. 静态全局访问点public static InnerClassSingleton getInstance() {return SingletonHolder.instance;}
}
核心特点
-
✅ 线程安全:依赖 JVM 类加载的 “初始化锁”,无需手动加锁;
-
✅ 完美懒加载:类加载时不创建实例,只有调用
getInstance()才初始化; -
✅ 高性能:无同步锁开销,获取实例时直接返回,效率极高;
-
❌ 不防反序列化 / 反射:需额外处理(下文有解决方案)。
适用场景
90% 的业务场景(如工具类、配置类、普通服务组件),尤其是对 “懒加载” 和 “性能” 有要求的场景,是日常开发的首选写法。
2. 双重校验锁(DCL,高并发场景首选)
核心原理
“懒汉式” 的升级版 —— 通过 “两次if判断 +volatile禁止指令重排”,既保证线程安全,又减少同步锁的开销(仅首次创建实例时加锁)。
代码实现(JDK1.5+)
package com.fastech.designpattern.singleton;public class DoubleCheckedLockSingleton {// 1. 私有构造器private DoubleCheckedLockSingleton() {}// 2. volatile修饰实例:禁止指令重排+保证可见性(关键!)private static volatile DoubleCheckedLockSingleton instance;// 3. 双重校验锁的全局访问点public static DoubleCheckedLockSingleton getInstance() {// 第一次校验:避免已创建实例后,每次调用都进入同步块(提高性能)if (instance == null) {// 同步锁:保证多线程下只有一个线程进入创建逻辑synchronized (DoubleCheckedLockSingleton.class) {// 第二次校验:防止多个线程等待锁时,重复创建实例(关键!)if (instance == null) {// 若不加volatile,可能发生指令重排(1-3-2),导致其他线程拿到未初始化的实例instance = new DoubleCheckedLockSingleton();}}}return instance;}
}
关键问题解析
- 为什么加
volatile?
instance = new DoubleCheckedLockSingleton()在 JVM 中会拆分为 3 步:1. 分配内存空间;2. 初始化实例;3. 将instance指向内存空间。
若不加volatile,JVM 可能进行 “指令重排”(如 1→3→2),导致线程 A 执行到 3 时,instance已非null,线程 B 直接返回未初始化的instance,引发空指针异常。
volatile的作用:禁止指令重排,确保实例初始化完成后,才对外可见。
- 为什么需要两次
if校验?
假设线程 A、B 同时进入第一次if(instance为null):
线程 A 先获取锁,进入第二次if,创建实例后释放锁;
线程 B 再获取锁,若没有第二次if,会重复创建实例,破坏单例。
核心特点
-
✅ 线程安全:双重校验 +
volatile确保多线程下实例唯一; -
✅ 懒加载:仅首次调用时创建实例;
-
✅ 高性能:首次创建后无锁开销,适合高并发场景;
-
❌ 代码稍复杂:需注意
volatile和双重校验的细节; -
❌ 不防反序列化 / 反射:需额外处理。
适用场景
高并发场景(如秒杀系统的库存管理器、分布式锁客户端),或需要 “懒加载 + 低锁开销” 的核心组件。
3. 枚举(防破坏场景首选)
核心原理
利用 Java 枚举的天然特性实现单例:枚举类的实例在类加载时创建,且 JVM 禁止通过反射、反序列化创建新实例,是《Effective Java》作者 Josh Bloch 推荐的 “最安全单例写法”。
代码实现(极简)
package com.fastech.designpattern.singleton.enums;// 枚举单例:3行代码实现最安全的单例
public enum EnumSingleton {// 唯一实例(枚举类的每个枚举值都是单例)INSTANCE;// 可选:添加业务方法public void doBusiness() {System.out.println("枚举单例执行业务逻辑");}
}// 调用方式:直接通过枚举值获取实例
EnumSingleton.INSTANCE.doBusiness();
核心优势:天然防破坏
枚举单例的最大价值,是无需额外代码即可抵御 “反射” 和 “反序列化” 的破坏:
- 防反射破坏:JVM 在
Constructor.newInstance()中明确禁止反射创建枚举实例,若强行调用会抛出IllegalArgumentException;
// 测试反射破坏枚举单例(会报错)
public class ReflectTest {public static void main(String[] args) throws Exception {// 枚举类的构造器默认有String(枚举名)和int(序号)两个参数Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);constructor.setAccessible(true);// 报错:Cannot reflectively create enum objectsEnumSingleton instance = constructor.newInstance("TEST", 1);}
}
具体可以查看newInstance()方法的源码

- 防反序列化破坏:枚举类默认实现
Serializable,反序列化时会调用Enum.valueOf()方法,根据枚举名获取已存在的实例,而非创建新实例;
// 测试反序列化枚举单例(结果为true,实例唯一)
public class SerializeTest {public static void main(String[] args) throws Exception {// 序列化ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enum-singleton.txt"));oos.writeObject(EnumSingleton.INSTANCE);// 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enum-singleton.txt"));EnumSingleton deserializedInstance = (EnumSingleton) ois.readObject();// 输出true:反序列化后仍是原实例System.out.println(deserializedInstance == EnumSingleton.INSTANCE);}
}
核心特点
-
✅ 绝对线程安全:枚举实例由 JVM 保证唯一;
-
✅ 天然防破坏:抵御反射、反序列化攻击;
-
✅ 代码极简:无需处理锁、volatile 等细节;
-
❌ 不支持懒加载:枚举类加载时即创建实例(若实例初始化开销大,会影响启动速度);
-
❌ 灵活性低:枚举类不能继承其他类(已默认继承
Enum)。
适用场景
对 “单例安全性” 要求极高的场景(如权限管理器、加密密钥生成器),或需要防止反序列化 / 反射破坏的场景。
4. 饿汉式(简单场景可用)
核心原理
利用 “类加载时初始化实例” 实现单例:类加载的 “准备→初始化” 阶段由 JVM 保证线程安全,实例在类加载时就创建,无需懒加载。
代码实现(两种写法)
// 写法1:静态变量直接初始化
public class HungrySingleton1 {// 类加载时创建实例(线程安全)private static final HungrySingleton1 instance = new HungrySingleton1();private HungrySingleton1() {}public static HungrySingleton1 getInstance() {return instance;}
}// 写法2:静态代码块初始化(与写法1原理一致)
public class HungrySingleton2 {private static final HungrySingleton2 instance;// 静态代码块在类加载时执行,仅执行一次static {instance = new HungrySingleton2();// 可在此处添加实例初始化逻辑(如读取配置)}private HungrySingleton2() {}public static HungrySingleton2 getInstance() {return instance;}
}
核心特点
-
✅ 线程安全:依赖 JVM 类加载机制,无需加锁;
-
✅ 高性能:获取实例时直接返回,无任何开销;
-
✅ 代码简单:易于理解和实现;
-
❌ 不支持懒加载:类加载时即创建实例,若实例初始化开销大(如加载大文件),会导致系统启动变慢;
-
❌ 不防反序列化 / 反射:需额外处理。
适用场景
实例初始化开销小、启动时就需要用到的组件(如简单的工具类StringUtils),或小型项目中对 “懒加载” 无要求的场景。
5. 懒汉式(不推荐,仅作对比)
核心原理
“懒加载” 的基础实现:只有调用getInstance()时才创建实例,通过synchronized关键字保证线程安全,但同步锁粒度太大,性能极低。
代码实现
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}// 整个方法加锁:每次调用都要竞争锁,性能差public static synchronized LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
核心特点
-
✅ 线程安全:
synchronized保证多线程下实例唯一; -
✅ 支持懒加载:仅首次调用时创建实例;
-
❌ 性能极差:每次调用
getInstance()都要加锁,即使实例已创建,仍需竞争锁(对比双重校验锁的 “细粒度锁”,差距明显); -
❌ 不防反序列化 / 反射:需额外处理。
适用场景
几乎不推荐在实际项目中使用,仅作为 “单例模式演进” 的学习案例(理解从懒汉式到双重校验锁的优化思路)。
6. 非 synchronized 加锁(特殊场景可用)
这类写法不依赖synchronized,而是通过 CAS 或 ThreadLocal 实现线程安全,属于 “小众但有特定价值” 的写法。
(1)CAS 实现(无锁并发)
核心原理:利用AtomicReference的compareAndSet()(CAS 操作),在无锁情况下保证实例创建的原子性,适合并发量极高但实例初始化频率低的场景。
package com.fastech.designpattern.singleton;import java.util.concurrent.atomic.AtomicReference;public class CasSingleton {// 原子引用:存储单例实例private static final AtomicReference<CasSingleton> INSTANCE = new AtomicReference<>();private CasSingleton() {}public static CasSingleton getInstance() {while (true) {// 1. 尝试获取已存在的实例CasSingleton instance = INSTANCE.get();if (instance != null) {return instance;}// 2. 若实例不存在,创建新实例并通过CAS尝试设置instance = new CasSingleton();// CAS操作:若当前值为null(无其他线程修改),则设置为新实例,返回trueif (INSTANCE.compareAndSet(null, instance)) {return instance;}// 3. 若CAS失败(其他线程已创建实例),则循环重试}}
}
特点:无锁开销,并发性能高;但循环重试可能消耗 CPU,且不防反射 / 反序列化,适合底层框架或极致性能场景。
(2)ThreadLocal 实现(线程内单例)
核心原理:ThreadLocal保证 “每个线程拥有独立实例”,并非 “全局唯一实例”,属于 “线程级单例”,适合线程内需要复用实例但无需全局唯一的场景(如多线程处理任务时,每个线程一个日志器)。
package com.fastech.designpattern.singleton;public class ThreadLocalSingleton {// ThreadLocal:每个线程存储自己的实例private static final ThreadLocal<ThreadLocalSingleton> INSTANCE = ThreadLocal.withInitial(ThreadLocalSingleton::new);private ThreadLocalSingleton() {}// 每个线程调用时,获取自己线程内的实例public static ThreadLocalSingleton getInstance() {return INSTANCE.get();}
}
特点:线程内实例唯一,无并发竞争;但全局不唯一,需明确场景后使用(如 Spring 的RequestContextHolder就是类似思想)。
三、6 种单例写法对比表(实战选型指南)
| 写法 | 线程安全 | 懒加载 | 防反射 / 反序列化 | 性能 | 代码复杂度 | 适用场景 |
|---|---|---|---|---|---|---|
| 静态内部类 | ✅ | ✅ | ❌(需额外处理) | 极高 | 低 | 日常开发首选(工具类、配置类) |
| 双重校验锁 | ✅ | ✅ | ❌(需额外处理) | 高 | 中 | 高并发场景(秒杀、分布式锁) |
| 枚举 | ✅ | ❌ | ✅(天然支持) | 极高 | 极低 | 安全优先场景(权限、加密) |
| 饿汉式 | ✅ | ❌ | ❌(需额外处理) | 极高 | 极低 | 简单场景(轻量级工具类) |
| 懒汉式 | ✅ | ✅ | ❌(需额外处理) | 差 | 低 | 仅学习使用,不推荐项目用 |
| CAS 实现 | ✅ | ✅ | ❌(需额外处理) | 极高 | 中 | 极致性能场景(底层框架) |
| ThreadLocal | ✅(线程内) | ✅ | ❌(需额外处理) | 极高 | 中 | 线程级单例(如线程内日志) |
四、单例模式进阶:如何防止被破坏?
除了枚举单例,其他写法都可能被 “反射” 或 “反序列化” 破坏,以下是完整的防护方案:
1. 防反射破坏:在私有构造器中添加校验
核心思路:在私有构造器中判断实例是否已存在,若已存在则抛出异常,阻止反射通过newInstance()重复创建实例。
以 “静态内部类单例” 为例,完善防反射代码:
public class InnerClassSingleton {private InnerClassSingleton() {// 防反射关键代码:若实例已存在,直接抛异常if (SingletonHolder.instance != null) {throw new IllegalStateException("单例对象已存在,禁止通过反射重复创建");}}private static class SingletonHolder {private static final InnerClassSingleton instance = new InnerClassSingleton();}public static InnerClassSingleton getInstance() {return SingletonHolder.instance;}
}
测试反射破坏(此时会报错):
public class ReflectTest {public static void main(String[] args) throws Exception {// 获取私有构造器Constructor<InnerClassSingleton> constructor = InnerClassSingleton.class.getDeclaredConstructor();constructor.setAccessible(true); // 强行访问私有构造器// 第一次创建:正常(此时SingletonHolder.instance为null)InnerClassSingleton instance1 = constructor.newInstance();// 第二次创建:抛出IllegalStateException,阻止重复创建InnerClassSingleton instance2 = constructor.newInstance();}
}
注意:这种方案仅对 “静态内部类”“饿汉式” 等 “实例在类加载时创建” 的写法有效;对 “双重校验锁”“懒汉式” 等 “延迟创建实例” 的写法,需结合volatile和原子变量优化,否则在多线程反射场景下仍有漏洞(实际项目中,反射破坏多为恶意操作,此方案已能应对大部分场景)。
2. 防反序列化破坏:重写readResolve()方法
核心原理:当单例类实现Serializable接口时,反序列化会通过readObject()创建新实例;重写readResolve()方法后,反序列化会直接返回该方法指定的实例(即已存在的单例实例),而非创建新实例。
以 “双重校验锁单例” 为例,完善防反序列化代码:
package com.fastech.designpattern.singleton;import java.io.Serializable;public class DoubleCheckedLockSingleton implements Serializable {private static volatile DoubleCheckedLockSingleton instance;private DoubleCheckedLockSingleton() {}public static DoubleCheckedLockSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockSingleton();}}}return instance;}// 防反序列化关键方法:反序列化时返回已存在的单例实例private Object readResolve() {return getInstance();}
}
测试反序列化破坏(此时结果为true,实例唯一):
public class SerializeTest {public static void main(String[] args) throws Exception {// 1. 序列化单例实例ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dcl-singleton.txt"));oos.writeObject(DoubleCheckedLockSingleton.getInstance());oos.close();// 2. 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dcl-singleton.txt"));DoubleCheckedLockSingleton deserializedInstance = (DoubleCheckedLockSingleton) ois.readObject();ois.close();// 3. 对比:反序列化实例与原实例是否一致(结果为true)System.out.println(deserializedInstance == DoubleCheckedLockSingleton.getInstance());}
}
原理补充:readResolve()是 Java 序列化机制的 “钩子方法”,在readObject()之后执行;若类中存在该方法,序列化框架会用其返回值替换readObject()创建的新实例,从而保证单例唯一性。
五、SpringBoot 实战:单例模式的实际应用
在 SpringBoot 项目中,我们很少手动写单例(因为 Spring 默认将Bean创建为单例),但理解单例模式的原理,能帮你更好地解决 “Bean 的作用域”“线程安全” 等核心问题。
1. Spring 的单例 Bean:底层原理与配置
Spring 中Bean默认是 “单例作用域”(singleton),其实现逻辑与 “饿汉式”“静态内部类” 类似,依赖容器启动时创建实例和线程安全的 Bean 工厂:
-
创建时机:默认在容器启动(
refresh()方法)时创建单例 Bean(可通过@Lazy注解改为 “懒加载”,对应 “静态内部类” 的延迟创建逻辑); -
线程安全:Spring 通过
DefaultSingletonBeanRegistry中的 “单例缓存” 和 “同步锁” 保证线程安全,核心代码如下(简化版):
public class DefaultSingletonBeanRegistry {// 单例Bean缓存:key=Bean名称,value=Bean实例private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 创建Bean的同步锁private final Object singletonMutex = new Object();public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {synchronized (singletonMutex) { // 同步锁保证线程安全Object singletonObject = singletonObjects.get(beanName);if (singletonObject == null) {// 调用ObjectFactory创建Bean实例(如反射创建Bean)singletonObject = singletonFactory.getObject();// 将创建好的Bean放入缓存singletonObjects.put(beanName, singletonObject);}return singletonObject;}}
}
2. 实战场景 1:自定义单例工具类(与 Spring 整合)
在 SpringBoot 中,若需自定义单例工具类(如RedisUtils),推荐结合Spring的依赖注入,避免手动管理单例实例:
// 1. 自定义单例工具类(通过Spring管理,确保单例)
@Component // 交给Spring管理,默认单例
public class RedisUtils {// 依赖注入RedisTemplate(Spring管理的单例Bean)@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 私有构造器:防止外部new(可选,因为Spring会通过反射创建实例)private RedisUtils() {}// 业务方法:封装Redis操作public void set(String key, Object value, long timeout) {redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);}public Object get(String key) {return redisTemplate.opsForValue().get(key);}
}// 2. 其他组件中使用:直接注入,无需手动调用getInstance()
@Service
public class UserService {@Autowiredprivate RedisUtils redisUtils; // 注入的是Spring管理的单例实例public void cacheUser(User user) {redisUtils.set("user:" + user.getId(), user, 3600);}
}
3. 实战场景 2:单例 Bean 的线程安全问题(避坑)
Spring 的单例 Bean 是 “实例唯一”,但不保证线程安全—— 若 Bean 中存在 “可变成员变量”,多线程并发修改时会出现数据混乱。
反例:线程不安全的单例 Bean
@Component
public class ThreadUnsafeSingletonBean {// 可变成员变量:多线程共享会导致线程安全问题private int count = 0;// 多线程并发调用此方法,会出现count计算错误public void increment() {count++;System.out.println("当前计数:" + count);}
}
正例:线程安全的单例 Bean(3 种解决方案)
@Component
public class ThreadSafeSingletonBean {// 方案1:使用ThreadLocal(每个线程有独立变量副本)private ThreadLocal<Integer> count = ThreadLocal.withInitial(() -> 0);// 方案2:使用原子变量(CAS操作保证线程安全)private AtomicInteger atomicCount = new AtomicInteger(0);// 方案3:加同步锁(适合复杂逻辑)private int syncCount = 0;public void increment() {// ThreadLocal方案count.set(count.get() + 1);System.out.println("ThreadLocal计数:" + count.get());// 原子变量方案System.out.println("原子变量计数:" + atomicCount.incrementAndGet());// 同步锁方案synchronized (this) {syncCount++;System.out.println("同步锁计数:" + syncCount);}}
}
六、单例模式常见误区与最佳实践
1. 常见误区(避坑指南)
-
误区 1:认为 “单例 = 线程安全”
单例仅保证 “实例唯一”,不保证 “业务逻辑线程安全”(如上述
ThreadUnsafeSingletonBean),需额外通过ThreadLocal、原子变量、同步锁保证线程安全。 -
误区 2:过度依赖单例,导致 “全局状态污染”
若单例 Bean 中存储过多全局状态(如用户登录信息),会导致多用户共享数据混乱,应使用
RequestScope(请求作用域)或SessionScope(会话作用域)的 Bean 替代。 -
误区 3:手动写单例时忽略 “防破坏”
若单例类实现
Serializable或可能被反射调用,必须添加readResolve()方法和构造器校验,否则会被破坏单例。
2. 最佳实践(选型口诀)
-
日常开发选 “静态内部类”:兼顾懒加载、线程安全、高性能,适配 90% 的业务场景;
-
高并发选 “双重校验锁”:细粒度锁减少开销,适合秒杀、分布式锁等核心场景;
-
安全优先选 “枚举”:天然防反射、反序列化,适合权限、加密等敏感场景;
-
Spring 项目少写 “手动单例”:优先用
@Component让 Spring 管理 Bean,利用容器的单例机制和依赖注入简化开发; -
线程内复用选 “ThreadLocal”:如多线程处理任务时,每个线程一个日志器、数据库连接。
七、总结:单例模式的本质是 “平衡”
单例模式的核心不是 “写出唯一实例”,而是 “在资源开销、性能、安全性之间找到平衡”:
-
若追求 “简单高效”,选饿汉式、静态内部类;
-
若追求 “高并发适配”,选双重校验锁、CAS 实现;
-
若追求 “绝对安全”,选枚举;
-
若在 Spring 生态中,优先依赖容器的单例 Bean 机制,避免重复造轮子。
掌握单例模式的 6 种写法,不仅能应对面试中的 “单例实现” 问题,更能理解框架底层的设计逻辑(如 Spring Bean、MyBatis SqlSessionFactory),为后续学习更复杂的设计模式(如工厂模式、代理模式)打下基础。
