单例模式 (Singleton Pattern)
什么是单例模式?
单例模式是设计模式中最简单、最基础的一种模式。它的核心思想是:确保一个类在整个程序运行期间只有一个实例,并提供一个全局访问点来访问这个实例。
生活中的例子
想象一下:
- 数据库连接池:整个应用程序只需要一个连接池来管理数据库连接
- 配置管理器:应用程序的配置信息只需要加载一次,全局共享
- 日志记录器:通常只需要一个日志记录器来统一管理日志输出
- Windows任务管理器:无论你打开多少次任务管理器,都只会有一个窗口
为什么需要单例模式?
- 节省资源:避免重复创建对象,减少内存和CPU开销
- 数据一致性:确保所有地方访问的都是同一个对象,数据保持一致
- 便于控制:可以统一管理对象的创建和销毁
单例模式的实现要点
- 私有构造方法 :防止外部通过
new关键字创建对象 - 私有静态实例:在类内部保存唯一的实例
- 公共静态访问方法 :提供全局访问点,如
getInstance()
代码示例
单例模式实现:数据库连接池
使用静态内部类方式实现,线程安全且延迟加载。这是单例模式最推荐的实现方式,因为:
- 线程安全:由JVM保证类的加载机制
- 延迟加载:只有在调用getInstance()时才会加载Holder类
- 代码简洁:不需要同步代码块
java
/**
* 单例模式实现:数据库连接池
* 使用静态内部类方式实现,线程安全且延迟加载
*
* 这是单例模式最推荐的实现方式,因为:
* 1. 线程安全:由JVM保证类的加载机制
* 2. 延迟加载:只有在调用getInstance()时才会加载Holder类
* 3. 代码简洁:不需要同步代码块
*/
public class DatabaseConnection {
// 私有构造方法,防止外部通过new创建实例
private DatabaseConnection() {
System.out.println("数据库连接池初始化...");
// 模拟初始化操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据库连接池初始化完成");
}
/**
* 静态内部类,负责持有单例实例
* 只有在第一次调用getInstance()时,JVM才会加载Holder类并初始化INSTANCE
*/
private static class Holder {
private static final DatabaseConnection INSTANCE = new DatabaseConnection();
}
/**
* 获取单例实例的公共方法
* @return 数据库连接池的唯一实例
*/
public static DatabaseConnection getInstance() {
return Holder.INSTANCE;
}
/**
* 模拟数据库查询操作
*/
public void query(String sql) {
System.out.println("执行SQL查询: " + sql);
}
/**
* 模拟数据库插入操作
*/
public void insert(String sql) {
System.out.println("执行SQL插入: " + sql);
}
/**
* 模拟数据库更新操作
*/
public void update(String sql) {
System.out.println("执行SQL更新: " + sql);
}
/**
* 模拟数据库删除操作
*/
public void delete(String sql) {
System.out.println("执行SQL删除: " + sql);
}
}
使用单例
java
/**
* 单例模式测试类
* 演示单例模式如何确保只有一个实例
*/
public class SingletonTest {
public static void main(String[] args) {
System.out.println("=== 单例模式测试 ===\n");
// 获取第一个实例
DatabaseConnection connection1 = DatabaseConnection.getInstance();
connection1.query("SELECT * FROM users");
System.out.println();
// 获取第二个实例
DatabaseConnection connection2 = DatabaseConnection.getInstance();
connection2.insert("INSERT INTO users VALUES (1, '张三')");
System.out.println();
// 获取第三个实例
DatabaseConnection connection3 = DatabaseConnection.getInstance();
connection3.update("UPDATE users SET name='李四' WHERE id=1");
System.out.println("\n=== 验证是否为同一个实例 ===");
System.out.println("connection1 == connection2: " + (connection1 == connection2));
System.out.println("connection2 == connection3: " + (connection2 == connection3));
System.out.println("connection1 == connection3: " + (connection1 == connection3));
System.out.println("\n=== 验证hashCode是否相同 ===");
System.out.println("connection1.hashCode(): " + connection1.hashCode());
System.out.println("connection2.hashCode(): " + connection2.hashCode());
System.out.println("connection3.hashCode(): " + connection3.hashCode());
}
}
常见的实现方式
1. 饿汉式(立即加载)
java
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
优点 :线程安全,简单
缺点:类加载时就创建实例,可能造成资源浪费
2. 懒汉式(延迟加载)
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点 :延迟加载,节省资源
缺点:线程不安全
3. 双重检查锁(推荐)
java
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优点 :线程安全,延迟加载,性能好
缺点:代码稍复杂
4. 静态内部类(推荐)
java
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
优点 :线程安全,延迟加载,代码简洁
缺点:无
适用场景
- 需要频繁创建和销毁的对象
- 创建对象耗时或耗费资源较多
- 工具类对象
- 频繁访问数据库或文件的对象
- 全局共享的配置信息
使用建议
- 优先使用静态内部类方式,它是最优雅的实现
- 如果需要支持序列化,要注意防止反序列化创建新对象
- 如果需要支持反射,要防止反射创建新对象
- 在Android开发中,可以使用
Application类作为单例
注意事项
⚠️ 单例模式虽然简单,但不要滥用!过度使用单例会导致:
- 代码难以测试(全局状态难以隔离)
- 隐藏类之间的依赖关系
- 违反单一职责原则(单例类承担过多责任)