👑 单例模式:独一无二的王者设计模式 👑

“在我的代码王国里,只能有一个国王!” —— 单例模式宣言 👑

🌟 单例模式是什么?

想象一下:

  • 太阳系只能有一个太阳 ☀️
  • 一个国家只能有一个国王 👑
  • 一台电脑只能有一个任务管理器 💻

这就是单例模式!它确保一个类只有一个实例,并提供全局访问点。就像你永远不需要第二个任务管理器一样!

它是一种创建型的模式!

🧠 为什么要用单例模式?

场景没有单例 😱使用单例 😎
数据库连接每次操作都新建连接,资源爆炸!全局共享一个连接池
配置管理器配置被重复加载,内存泄漏所有模块共享同一配置
日志系统日志文件被多线程覆盖统一日志入口

核心价值:

  • 🚫 防止重复创建:节约系统资源
  • 🌐 全局访问点:任何地方都能获取同一实例
  • ⚡ 延迟初始化:需要时才创建(懒汉式)

💻 代码实战:五种单例实现

1. 饿汉式(饿肚子也要先做饭)

public class Sun {
    // 类加载时就创建实例
    private static final Sun instance = new Sun();
    
    // 私有构造!禁止外部new
    private Sun() {} 
    
    public static Sun getInstance() {
        return instance;
    }
}
// 使用:Sun.getInstance().shine();

✅ 优点:简单、线程安全
❌ 缺点:可能提前占用资源

2. 懒汉式(饿了才做饭)

public class King {
    private static King instance;
    
    private King() { /* 加冕仪式 */ }
    
    public static synchronized King getInstance() {
        if (instance == null) {
            instance = new King();
        }
        return instance;
    }
}
// 使用:King.getInstance().rule();

✅ 优点:延迟初始化
❌ 缺点:同步锁影响性能

3. 双重检查锁(智能管家)

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

✅ 优点:高性能+线程安全
⚠️ 注意:volatile关键字必不可少!

4. 静态内部类(优雅贵族)(最推荐使用的)

public class LogMaster {
    private LogMaster() {}
    
    private static class Holder {
        static final LogMaster INSTANCE = new LogMaster();
    }
    
    public static LogMaster getInstance() {
        return Holder.INSTANCE;
    }
}

✅ 优点:线程安全+延迟加载+无锁
🎩 最优雅的实现方式!

5. 枚举式(终极王者)

public enum Universe {
    INSTANCE;
    
    public void bigBang() {
        System.out.println("💥 宇宙大爆炸启动!");
    }
}
// 使用:Universe.INSTANCE.bigBang();

✅ 优点:绝对防止反射攻击
👑 《Effective Java》作者Josh Bloch推荐

💼 面试黄金问答宝典

Q1:什么是单例模式?使用场景?

满分回答:

"单例模式确保一个类只有一个实例,并提供全局访问点。它就像公司的CEO,整个系统只需要一个。
使用场景:

  • 需要共享资源的场景(数据库连接池、线程池)
  • 需要控制资源的场景(配置管理器、日志系统)
  • 需要唯一性的场景(任务管理器、序列号生成器)

Q2:如何实现线程安全的单例?

技术流回答:

"五种实现方式各有特点:

  1. 饿汉式:类加载时初始化,线程安全但可能浪费资源
  2. 同步懒汉式:简单但性能差
  3. 双重检查锁:高性能线程安全,注意volatile防重排序
  4. 静态内部类:利用类加载机制保证线程安全,推荐!
  5. 枚举单例:最安全,能防止反射攻击,Effective Java推荐"

Q3:单例模式有什么缺点?

辩证性回答:

"虽然单例很强大,但要注意:

  • 测试困难:全局状态难以mock
  • 违反单一职责:同时管理实例和业务逻辑
  • 内存泄漏:长期持有可能不被GC回收
  • 过度使用:不是所有’只需要一个’的场景都适用

🔍 设计挑战台

挑战1:单例 vs 静态工具类

// 静态工具类
class MathUtils {
    public static int add(int a, int b) { return a + b; }
}

// 单例类
class ConfigManager {
    private static ConfigManager instance;
    private Map<String, String> configs;
    
    private ConfigManager() { loadConfigs(); }
    
    public static ConfigManager getInstance() { /*...*/ }
    
    public String getConfig(String key) { /*...*/ }
}

思考:什么时候该用单例而不是静态方法?
💡 答案:当需要状态管理(如配置数据)时用单例,纯工具操作用静态方法。

挑战2:破坏单例的三种方式

  1. 反射攻击:通过反射调用私有构造器

    Constructor<King> constructor = King.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    King fakeKing = constructor.newInstance();
    
  2. 序列化攻击:序列化后再反序列化

    // 序列化
    ObjectOutputStream oos = new ObjectOutputStream(...);
    oos.writeObject(King.getInstance());
    
    // 反序列化
    ObjectInputStream ois = new ObjectInputStream(...);
    King clone = (King) ois.readObject(); // 新实例!
    
  3. 克隆攻击:实现Cloneable接口

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 返回新实例!
    }
    

防御策略:

  • 使用枚举单例
  • 重写clone()方法抛出异常
  • 实现readResolve()方法返回已有实例

🌈 单例模式总结图

应用场景:
  ┌──────────────┐   ┌──────────────┐   ┌──────────────┐
  │ 配置管理器   │   │ 数据库连接池 │   │ 日志系统     │
  └──────────────┘   └──────────────┘   └──────────────┘
          ▲                  ▲                  ▲
          │                  │                  │
          └─────────┬────────┴────────┬─────────┘
                    │ 单例模式核心思想 │
                    └─────────────────┘
                          ▼
         ┌───────────────────────────────┐
         │ 确保唯一实例 + 全局访问点 │
         └───────────────────────────────┘
                          ▼
           ┌───────┬───────┬───────┬───────┐
           │ 饿汉式│ 懒汉式│双重检查│ 枚举式│
           └───────┴───────┴───────┴───────┘

🎓 终极面试加分金句

“单例模式是控制欲最强的设计模式,但记住:权力越大,责任越大!
在Spring中,默认Bean作用域就是单例,但要注意线程安全问题。
在并发场景下,我首选静态内部类实现,平衡了线程安全和延迟加载的需求。”


Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐