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

图片描述 :一个名为
Singleton的类图。其中包含:
- 一个私有的、静态的、自身类型的属性
instance: Singleton。- 一个私有的构造方法
Singleton()。- 一个公有的、静态的、返回自身类型的方法
getInstance(): Singleton。
图中还有一个从类指向自身属性的菱形聚合符号,以及一个从getInstance方法指向instance属性的箭头,表示该方法会返回这个实例。
4. 模式实现步骤分解
为了实现单例模式的三个要点,我们需要一步步地构建这个类。
-
第一步:私有化构造方法
- 目的 : 防止外部通过
new关键字随意创建实例。 - 分析 : 要想在运行期间控制一个类的实例只有一个,首要任务就是要控制创建实例的地方。如果构造方法是公有的,那么在类的外部就可以随处
new,实例的数量就失控了。 - 实现 : 将构造方法声明为
private。
javaprivate Singleton() {} - 目的 : 防止外部通过
-
第二步:提供获取实例的方法
- 问题: 构造方法被私有化了,外部创建不了实例,也就无法调用对象的方法,这不行。
- 解决: 单例类必须自己提供一个公有的方法,用来返回它内部创建好的那个唯一实例。
java// 初始设想,但有问题 public Singleton getInstance() {} -
第三步:将获取实例的方法静态化
- 问题: 上一步定义的是一个实例方法。要调用实例方法,必须先得有类的实例。这就陷入了一个"鸡生蛋,蛋生鸡"的死循环:为了获取实例,你得先有一个实例。
- 解决 : 在方法上加上
static关键字,使其成为一个类方法。这样就可以直接通过类名.方法名()的方式来调用,而不需要先得到类实例。
javapublic static Singleton getInstance() {} -
第四步:定义存储实例的静态属性
- 问题 :
getInstance()方法的内部该如何实现?如果直接return new Singleton();,那么每次调用都会创建一个新的实例,这违背了单例的初衷。 - 解决: 在类内部用一个属性来记录(存储)已经创建好的那个唯一实例。当第一次创建后,就把实例保存在这个属性里,以后就直接复用这个实例,而不是重新创建。
- 为什么要静态化 : 因为这个属性需要在静态方法
getInstance()里面使用,而静态方法只能访问静态成员。所以这个存储实例的属性也必须是static的。
javaprivate static Singleton instance = null; - 问题 :
-
第五步:实现控制实例创建的逻辑
- 目的 : 在
getInstance()方法里实现对实例创建的控制。 - 逻辑 :
- 先判断存储实例的静态属性
instance是否有值(是否为null)。 - 如果为
null,说明这是第一次调用,还没有创建过实例。此时,就new一个实例,并把它赋值给instance属性。 - 如果
instance不为null,说明之前已经创建过实例了。 - 最后,无论是否新建,都返回
instance属性中存储的那个实例。
- 先判断存储实例的静态属性
javapublic static Singleton getInstance() { // 先判断instance是否有值 if (instance == null) { // 如果没有值,说明还没有创建过实例,那就创建一个 // 并把这个实例设置给instance instance = new Singleton(); } // 如果有值,或者是创建了值,那就直接使用 return instance; } - 目的 : 在
5. 完整代码示例与验证
将以上所有步骤组合起来,就得到了一个基础的单例模式实现。
这种实现方式被称为"懒汉式",因为它是等到第一次需要时才创建实例
- 完整实现

- 验证代码
java
// 验证逻辑
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 代表的是同一个实例",从而证明了单例模式的正确性。