什么是单例模式
单例模式是最简单也是最常用的设计模式之一,它确保一个类在整个应用中只有一个实例,并提供一个全局访问点来获取这个实例。就像一个国家只能有一位总统,一个班级只能有一位班长一样,单例模式保证某些特定类的对象全局唯一。
为什么需要单例模式
想象一下,如果我们的应用程序中有一个负责管理系统配置的类。如果这个类的实例到处都在创建,不仅浪费内存资源,更重要的是可能导致配置信息的不一致。单例模式就是为了解决这类问题而生的。
单例模式的核心实现
public class Singleton {
// 私有静态实例变量
private static Singleton instance;
// 私有构造函数,防止外部直接创建实例
private Singleton() {
// 初始化代码
}
// 公共静态方法,提供全局访问点
public static synchronized Singleton getInstance() {
// 懒加载:只有在第一次调用时才创建实例
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 业务方法
public void doSomething() {
System.out.println("单例执行某些操作");
}
}
单例模式的关键点
- 私有构造函数:防止外部通过
new关键字创建实例 - 私有静态变量:持有唯一实例
- 公共静态方法:提供获取实例的唯一途径
- 懒加载:实例在首次使用时才被创建
实际应用示例:配置管理器
下面通过一个配置管理器的例子来展示单例模式的实际应用:
import java.util.HashMap;
public class ConfigManager {
// 私有静态实例
private static ConfigManager instance;
// 存储配置信息
private HashMap<String, String> configMap;
// 私有构造函数
private ConfigManager() {
System.out.println("配置管理器初始化中...");
configMap = new HashMap<>();
// 加载配置(实际应用中可能从文件或数据库加载)
configMap.put("数据库地址", "jdbc:mysql://localhost:3306/mydb");
configMap.put("最大连接数", "100");
configMap.put("超时时间", "3000");
}
// 获取实例的方法
public static synchronized ConfigManager getInstance() {
if (instance == null) {
instance = new ConfigManager();
}
return instance;
}
// 获取配置项
public String getConfig(String key) {
return configMap.get(key);
}
// 更新配置项
public void updateConfig(String key, String value) {
configMap.put(key, value);
}
}
如何使用单例模式
public class SingletonDemo {
public static void main(String[] args) {
// 应用启动
System.out.println("应用程序启动");
// 在应用的不同部分获取配置
// 用户管理模块获取配置
ConfigManager userConfig = ConfigManager.getInstance();
System.out.println("用户模块获取数据库地址: " + userConfig.getConfig("数据库地址"));
// 更新配置值
userConfig.updateConfig("数据库地址", "jdbc:mysql://192.168.1.100:3306/userdb");
// 订单管理模块获取配置
ConfigManager orderConfig = ConfigManager.getInstance();
System.out.println("订单模块获取数据库地址: " + orderConfig.getConfig("数据库地址"));
// 验证是否同一个实例
System.out.println("是否为同一个配置管理器实例: " + (userConfig == orderConfig));
}
}
运行结果
应用程序启动
配置管理器初始化中...
用户模块获取数据库地址: jdbc:mysql://localhost:3306/mydb
订单模块获取数据库地址: jdbc:mysql://192.168.1.100:3306/userdb
是否为同一个配置管理器实例: true
单例模式的常见应用场景
- 配置管理器:存储和管理应用配置信息
- 数据库连接池:管理数据库连接资源
- 线程池:集中管理线程资源
- 日志管理器:统一日志记录
- 缓存:应用级缓存管理
- 对话框管理器:管理UI组件
- 打印机后台处理程序:管理打印任务队列
单例模式的优点
- 资源节约:避免重复创建对象,节省内存资源
- 保证一致性:确保所有代码访问的是同一个实例,数据保持一致
- 方便访问:提供全局访问点,无需在应用中传递对象
- 延迟初始化:第一次使用时才创建实例,提高启动性能
单例模式的缺点
- 线程安全问题:在多线程环境下需要特别注意实现方式
- 单一职责违背:单例类可能承担过多职责
- 测试困难:全局状态使单元测试变得复杂
- 扩展性受限:不易扩展为多个实例
进阶:线程安全的单例实现
以下是几种更加安全和高效的单例实现方式:
1. 饿汉式(静态初始化)
public class EagerSingleton {
// 在类加载时就创建实例
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
2. 双重检查锁定
public class DCLSingleton {
// volatile关键字确保多线程可见性
private static volatile DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
3. 静态内部类方式
public class StaticInnerSingleton {
private StaticInnerSingleton() {}
// 静态内部类,延迟加载,线程安全
private static class SingletonHolder {
private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
}
public static StaticInnerSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
单例模式小结
单例模式虽然简单,但应用广泛。它通过限制实例化和提供全局访问点,帮助我们管理全局状态和共享资源。在选择单例模式时,需要权衡其优缺点,并根据实际需求选择合适的实现方式。掌握单例模式,是理解更复杂设计模式的基础,也是编写高质量代码的重要步骤。

1155

被折叠的 条评论
为什么被折叠?



