完整教程:【设计模式笔记17】:单例模式1-模式分析

六、 单例模式

1. 模式动机
  • 模式名称: 单例模式 (Singleton)
  • 核心动机: 在整个软件系统中,对于某些类来说,有且只能有一个实例。保证这个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
  • 现实场景举例:
    • 窗口管理器: 一个系统通常只有一个窗口管理器,负责管理所有窗口的层级、位置等。
    • 打印机假脱机服务: 系统中可以连接许多打印机,但只能有一个打印任务队列(假脱机服务)来统一管理和调度打印工作。
    • 配置文件读取类: 一个应用的配置信息通常是唯一的,用一个单例对象来读取和提供配置,可以避免重复读取和数据不一致。
  • 需要解决的问题: 我们怎样才能保证一个类只有一个实例,并且让系统中的任何部分都能方便地访问到这个唯一的实例呢?
  • 核心解决办法: 让类自身负责保存它的唯一实例。这个类必须保证没有其它实例被创建,并且它可以提供一个方法来访问该实例。
2. 模式定义
  • 定义: 单例模式(Singleton Pattern)确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类被称为单例类,它提供全局访问的方法。
  • 三大要点:
    1. 唯一实例: 某个类只能有一个实例。这是单例模式的根本。
    2. 自行创建: 它必须自行创建这个唯一的实例。创建的控制权在类的内部,而不是外部。
    3. 提供实例: 它必须自行向整个系统提供这个实例。这意味着它需要提供一个全局访问点,并且在单例类的外部无法使用 new 关键字创建
  • 分类: 单例模式是一种对象创建型模式
3. 结构及角色分析

单例模式的结构非常简单,只包含一个角色,即单例类 (Singleton)

在这里插入图片描述

图片描述:一个名为Singleton的类图。其中包含:

  • 一个私有的、静态的、自身类型的属性 instance: Singleton
  • 一个私有的构造方法 Singleton()
  • 一个公有的、静态的、返回自身类型的方法 getInstance(): Singleton
    图中还有一个从类指向自身属性的菱形聚合符号,以及一个从 getInstance 方法指向 instance 属性的箭头,表示该方法会返回这个实例。
4. 模式实现步骤分解

为了实现单例模式的三个要点,我们需要一步步地构建这个类。

  • 第一步:私有化构造方法

    • 目的: 防止外部通过 new 关键字随意创建实例。
    • 分析: 要想在运行期间控制一个类的实例只有一个,首要任务就是要控制创建实例的地方。如果构造方法是公有的,那么在类的外部就可以随处 new,实例的数量就失控了。
    • 实现: 将构造方法声明为 private
    private Singleton() {}
  • 第二步:提供获取实例的方法

    • 问题: 构造方法被私有化了,外部创建不了实例,也就无法调用对象的方法,这不行。
    • 解决: 单例类必须自己提供一个公有的方法,用来返回它内部创建好的那个唯一实例。
    // 初始设想,但有问题
    public Singleton getInstance() {}
  • 第三步:将获取实例的方法静态化

    • 问题: 上一步定义的是一个实例方法。要调用实例方法,必须先得有类的实例。这就陷入了一个“鸡生蛋,蛋生鸡”的死循环:为了获取实例,你得先有一个实例。
    • 解决: 在方法上加上 static 关键字,使其成为一个类方法。这样就可以直接通过 类名.方法名() 的方式来调用,而不需要先得到类实例。
    public static Singleton getInstance() {}
  • 第四步:定义存储实例的静态属性

    • 问题: getInstance() 方法的内部该如何实现?如果直接 return new Singleton();,那么每次调用都会创建一个新的实例,这违背了单例的初衷。
    • 解决: 在类内部用一个属性来记录(存储)已经创建好的那个唯一实例。当第一次创建后,就把实例保存在这个属性里,以后就直接复用这个实例,而不是重新创建。
    • 为什么要静态化: 因为这个属性需要在静态方法getInstance() 里面使用,而静态方法只能访问静态成员。所以这个存储实例的属性也必须是 static 的。
    private static Singleton instance = null;
  • 第五步:实现控制实例创建的逻辑

    • 目的: 在 getInstance() 方法里实现对实例创建的控制。
    • 逻辑:
      1. 先判断存储实例的静态属性 instance 是否有值(是否为 null)。
      2. 如果为 null,说明这是第一次调用,还没有创建过实例。此时,就 new 一个实例,并把它赋值给 instance 属性。
      3. 如果 instance 不为 null,说明之前已经创建过实例了。
      4. 最后,无论是否新建,都返回 instance 属性中存储的那个实例。
    public static Singleton getInstance() {
    // 先判断instance是否有值
    if (instance == null) {
    // 如果没有值,说明还没有创建过实例,那就创建一个
    // 并把这个实例设置给instance
    instance = new Singleton();
    }
    // 如果有值,或者是创建了值,那就直接使用
    return instance;
    }
5. 完整代码示例与验证

将以上所有步骤组合起来,就得到了一个基础的单例模式实现。

这种实现方式被称为“懒汉式”,因为它是等到第一次需要时才创建实例

  • 完整实现

在这里插入图片描述

  • 验证代码
// 验证逻辑
public static void main(String[] args) {
Singleton singletonOne = Singleton.getInstance();
Singleton singletonTwo = Singleton.getInstance();
if (singletonOne.equals(singletonTwo)) {
// 在Java中,对于引用类型,== 和 .equals() 在未重写时行为一致,都比较地址
System.out.println("singletonOne 和 singletonTwo 代表的是同一个实例");
} else {
System.out.println("singletonOne 和 singletonTwo 代表的是不同实例");
}
}
  • 预期结果: 程序会输出“singletonOne 和 singletonTwo 代表的是同一个实例”,从而证明了单例模式的正确性。
posted @ 2025-12-03 13:05  gccbuaa  阅读(30)  评论(0)    收藏  举报