一、什么是单例模式?——"朕即国家"
想象你玩《星际争霸》:
- 普通类:狂造兵营,爆一堆机枪兵(费钱费资源)
- 单例类:整个地图就一个指挥中心(省钱又高效)
单例模式就是这个"指挥中心",保证整个程序"江山一统",绝不出现"军阀割据"!
二、五种实现方式——"登基大典的不同姿势"
1. 饿汉式:出生就当皇帝 👶
public class Emperor {
private static final Emperor INSTANCE = new Emperor(); // 出生就是皇帝
private Emperor() {} // 禁止民间私自称帝
public static Emperor getInstance() {
return INSTANCE; // 朕在这里!
}
}
特点:简单粗暴,但可能"占着茅坑不拉屎"
2. 懒汉式:临时竞选皇帝 🗳️
public class LazyEmperor {
private static LazyEmperor instance;
private LazyEmperor() {}
public static synchronized LazyEmperor getInstance() {
if (instance == null) { // 还没皇帝?
instance = new LazyEmperor(); // 现选一个
}
return instance;
}
}
缺点:每次上朝都要"投票"(同步锁),效率低下
3. 双重检查锁:智能选举 🤖
public class SmartEmperor {
private volatile static SmartEmperor instance;
public static SmartEmperor getInstance() {
if (instance == null) { // 第一次微服私访
synchronized(SmartEmperor.class) {
if (instance == null) { // 第二次确认
instance = new SmartEmperor();
}
}
}
return instance;
}
}
妙处:既保证唯一性,又不用每次都锁整个皇宫
4. 静态内部类:世袭制 👑
public class InheritEmperor {
private static class Heir {
private static final InheritEmperor INSTANCE = new InheritEmperor();
}
public static InheritEmperor getInstance() {
return Heir.INSTANCE; // 太子自动继位
}
}
优势:天然线程安全,用时才登基
5. 枚举式:天道法则 🌌
public enum UniverseEmperor {
INSTANCE; // 宇宙法则规定的唯一存在
public void rule() {
System.out.println("我就是天道!");
}
}
最强王者:
- 防反射(禁止"造反")
- 防克隆(禁止"复制人")
- 自动序列化("龙气"永存)
三、使用场景——什么时候需要"独裁"?
- 玉玺(配置信息):全国只能有一个
- 国库(缓存):必须统一管理
- 禁军(线程池):调兵遣令要集中
- 史官(日志):历史记录必须一致
四、三大翻车现场 🚨
1. 反射攻击——"有人伪造圣旨"
Constructor<Emperor> constructor = Emperor.class.getDeclaredConstructor();
constructor.setAccessible(true);
Emperor fakeEmperor = constructor.newInstance(); // 伪帝!
防御措施:
private Emperor() {
if (INSTANCE != null) {
throw new RuntimeException("大胆!竟敢谋反!");
}
}
2. 序列化漏洞——"玉玺被复制"
ObjectOutputStream oos = new ObjectOutputStream(...);
oos.writeObject(emperor); // 把皇帝序列化了
ObjectInputStream ois = new ObjectInputStream(...);
Emperor cloned = (Emperor) ois.readObject(); // 克隆皇帝!
解决之道:
// 添加readResolve方法
private Object readResolve() {
return getInstance(); // 永远返回真命天子
}
3. 多线程叛乱——"八王之乱"
// 如果懒汉式没加锁...
Thread t1 = new Thread(() -> Emperor.getInstance());
Thread t2 = new Thread(() -> Emperor.getInstance());
// 可能产生两个皇帝!
五、单例模式 vs 静态类 —— "皇帝"vs"议会制"
| 单例模式(皇帝) | 静态类(议会) | |
|---|---|---|
| 继承 | 可以传位给太子 | 没有继承关系 |
| 状态 | 有国库可以存钱 | 只有法律条文(无状态) |
| 灵活 | 可以微服私访(实现接口) | 死板的法律条文 |
| 测试 | 容易假装皇帝(Mock) | 很难修改法律 |
六、最佳实践——"明君养成手册"
- 优先用枚举:天道最大,省心省力
- Spring环境下:直接用
@Service,让IOC容器当"内阁" - 需要参数时:选静态内部类实现
- 切记:单例不是万能药,滥用会导致:
- 代码难以测试(皇帝太多管不过来)
- 内存泄漏(国库只进不出)
结语
单例模式就像程序世界的"帝王术":
- 用得好:四海升平,资源统一
- 用不好:民怨沸腾,bug丛生
记住编程界的"君主立宪制": 该独裁时独裁(如配置管理),该民主时民主(普通对象)