2025/9/23日 每日总结 设计模式实践:单例模式实现学号唯一性管理

设计模式实践:单例模式实现学号唯一性管理

在软件开发中,经常会遇到“某个对象在系统中只能存在一个实例”的需求——比如配置管理器、数据库连接池、日志工具等。单例模式正是为解决这类问题而生,它能确保一个类仅有一个实例,并提供一个全局访问点。本文将以“学生学号唯一性管理”为例,分享单例模式的设计思想与实践实现。

一、单例模式核心思想

单例模式的核心目标是控制实例数量,其设计遵循三大原则:

  1. 私有构造方法:禁止外部通过new关键字创建实例;

  2. 静态私有实例:在类内部维护唯一的实例对象;

  3. 静态公有访问方法:提供全局统一的实例获取入口,确保每次获取的都是同一个实例。
    本次实践场景中,每个学生应仅有一个学号,因此用单例模式封装StudentID类,确保系统中只存在一个学号实例,所有学生对象共享该实例,从而保证学号唯一性。

二、类图设计

| StudentID | // 单例类
+-------------------+


- instance: StudentID // 静态私有实例(唯一实例)

- id: String // 学号字段
+-------------------+

- StudentID() // 私有构造方法(禁止外部实例化)


- getInstance(): StudentID // 静态公有方法(获取实例)


- setStudentID(id: String): void // 私有方法(设置学号)


- getStudentID(): String // 公有方法(获取学号)
+-------------------+

三、完整代码实现

1. 单例类:StudentID

通过私有构造方法、静态实例和全局访问方法,实现学号的唯一性管理。采用“懒汉式”加载策略,即第一次获取实例时才创建对象,节省系统资源。

public class StudentID {
// 1. 静态私有实例:维护唯一实例,初始为null
private static StudentID instance = null;
// 学号字段
private String id;
// 2. 私有构造方法:禁止外部通过new创建实例
private StudentID() {}
// 3. 静态公有访问方法:全局唯一获取实例的入口
public static StudentID getInstance() {
// 懒汉式加载:第一次调用时创建实例
if (instance == null) {
System.out.println("学生信息注册,分配学号!");
instance = new StudentID();
// 初始化学号(实际场景中可从数据库或配置文件获取)
instance.setStudentID("20224083");
} else {
System.out.println("学生信息已注册,获取已存在的学号!");
}
return instance;
}
// 私有方法:设置学号(仅内部可调用,保证学号不可随意修改)
private void setStudentID(String id) {
this.id = id;
}
// 公有方法:获取学号(提供外部访问接口)
public String getStudentID() {
return this.id;
}
}

2. 客户端测试:Client

通过多次获取StudentID实例,验证单例模式的唯一性——无论调用多少次getInstance(),返回的都是同一个实例,学号始终一致。

public class Client {
public static void main(String[] args) {
StudentID stu1, stu2;
String id1, id2;
// 第一次获取实例:创建对象并分配学号
System.out.println("第一次生成学生");
stu1 = StudentID.getInstance();
// 第二次获取实例:直接返回已存在的实例
System.out.println("第二次生成学生");
stu2 = StudentID.getInstance();
// 验证两个引用是否指向同一个实例(对象地址比较)
System.out.println("两学生学号实例是否一致:" + (stu1 == stu2));
// 获取学号并验证内容一致性
id1 = stu1.getStudentID();
id2 = stu2.getStudentID();
System.out.println("第一次获取的学号:" + id1);
System.out.println("第二次获取的学号:" + id2);
System.out.println("学号内容是否相等:" + id1.equalsIgnoreCase(id2));
System.out.println("学号对象是否相同(地址比较):" + (id1 == id2));
}
}

四、运行结果与验证

第一次生成学生
学生信息注册,分配学号!
第二次生成学生
学生信息已注册,获取已存在的学号!
两学生学号实例是否一致:true
第一次获取的学号:20224083
第二次获取的学号:20224083
学号内容是否相等:true
学号对象是否相同(地址比较):true

结果分析:

  1. 实例唯一性:stu1 == stu2 返回 true,证明两次获取的是同一个StudentID实例;

  2. 学号唯一性:id1id2 内容与地址均相同,说明学号未被重复创建,始终唯一;

  3. 懒汉式加载:仅第一次调用getInstance()时执行实例创建逻辑,后续直接复用已有实例。

五、单例模式的关键细节

1. 实现方式对比

本次采用的“懒汉式”是单例模式的常用实现,其特点与其他方式对比:

实现方式 核心特点 线程安全 优点 缺点
懒汉式(无锁) 延迟加载,第一次调用才创建实例 节省资源,按需创建 多线程环境下可能创建多个实例
饿汉式 类加载时直接创建实例 实现简单,线程安全 提前占用资源,无论是否使用
双重检查锁定 懒加载+同步锁,双重判断实例是否为空 兼顾性能与线程安全 实现稍复杂(需volatile关键字)
静态内部类 利用类加载机制实现延迟加载和线程安全 简洁高效,无锁设计 无法传参初始化

2. 线程安全问题

本次实现的懒汉式在单线程环境下完全可靠,但在多线程环境中可能出现线程安全问题:当多个线程同时调用getInstance(),且此时instancenull,可能导致多个线程同时创建实例,破坏单例特性。

线程安全优化方案(双重检查锁定):

public static StudentID getInstance() {
// 第一次检查:避免不必要的同步(提高性能)
if (instance == null) {
synchronized (StudentID.class) { // 同步锁:确保同一时间只有一个线程进入
// 第二次检查:防止多个线程等待锁后重复创建
if (instance == null) {
instance = new StudentID();
instance.setStudentID("20224083");
}
}
}
return instance;
}

3. 禁止序列化破坏单例

如果单例类实现了Serializable接口,序列化后再反序列化可能会创建新实例。解决方案是添加readResolve()方法,强制返回已有实例:

private Object readResolve() {
return getInstance();
}

六、单例模式的应用场景

单例模式适用于“全局唯一对象”的场景,典型应用包括:

  1. 配置管理器:系统配置全局唯一,避免重复加载配置文件;

  2. 数据库连接池:减少连接创建开销,统一管理连接资源;

  3. 日志工具:确保日志写入的原子性,避免日志混乱;

  4. 计数器:全局共享计数器,保证计数准确性;

  5. 硬件设备管理器:如打印机、摄像头等,同一时间只能被一个实例控制。

七、实践总结

  1. 单例模式的核心是“控制实例数量”,通过私有构造、静态实例、全局访问方法三大要素实现;

  2. 懒汉式适合单线程或对性能要求不高的场景,多线程环境需通过同步锁优化;

  3. 单例模式的优势在于节省系统资源、确保对象唯一性、提供全局统一访问点;

  4. 注意避免单例模式的过度使用——如果对象不需要全局唯一,强行使用单例会导致代码耦合度升高,灵活性降低。
    通过本次学号唯一性管理的实践,我对单例模式的设计思想和实际应用有了更深刻的理解。在实际开发中,需根据业务场景选择合适的单例实现方式,兼顾唯一性、性能和线程安全。

posted @ 2025-12-29 14:30  Moonbeamsc  阅读(14)  评论(0)    收藏  举报
返回顶端