什么是单例模式?
单例模式确保一个类只有一个实例,并提供一个全局访问点。就像公司里的CEO,整个公司只有一个,任何需要CEO的地方都访问同一个对象。
简单来说,单例模式:
- 控制实例数量(只允许创建一个)
- 提供全局访问点
- 通常自行管理自己的实例
主要解决什么问题?
单例模式主要解决以下问题:
- 资源共用问题:如数据库连接池、线程池、缓存等只需要一个实例
- 全局状态管理:配置信息、计数器等需要全局唯一的状态
- 避免重复创建:某些对象创建成本高,需要复用
何时使用单例模式?
当你遇到以下场景时,考虑使用单例模式:
- 当类只能有一个实例且客户端需要从众所周知的访问点访问它时
- 当唯一实例应该通过子类化可扩展,且客户端无需修改代码就能使用扩展后的实例时
- 需要严格控制全局变量时(比全局变量更优雅)
单例模式的优点
- 控制实例数量:确保只有一个实例存在
- 全局访问:提供统一的访问点
- 延迟初始化:可以在需要时才创建实例
- 减少资源消耗:避免重复创建消耗资源的对象
单例模式的缺点
- 违反单一职责原则:既管理自己的创建又负责业务逻辑
- 难以测试:全局状态使得单元测试困难
- 隐藏依赖:单例的调用关系不明显
- 多线程问题:需要特殊处理以保证线程安全
- 可能成为"上帝对象":过度使用会导致设计问题
代码示例:多种实现方式
1. 饿汉式(线程安全)
public class EagerSingleton {
// 类加载时就初始化
private static final EagerSingleton instance = new EagerSingleton();
// 私有构造函数
private EagerSingleton() {}
// 全局访问点
public static EagerSingleton getInstance() {
return instance;
}
// 示例方法
public void showMessage() {
System.out.println("Hello, I'm an Eager Singleton!");
}
}
2. 懒汉式(线程不安全)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
// 非线程安全
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
public void showMessage() {
System.out.println("Hello, I'm a Lazy Singleton!");
}
}
3. 双重检查锁(线程安全)
public class ThreadSafeSingleton {
// volatile保证可见性和禁止指令重排序
private static volatile ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static ThreadSafeSingleton getInstance() {
// 第一次检查
if (instance == null) {
synchronized (ThreadSafeSingleton.class) {
// 第二次检查
if (instance == null) {
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
public void showMessage() {
System.out.println("Hello, I'm a Thread-Safe Singleton!");
}
}
4. 静态内部类实现(推荐)
public class InnerClassSingleton {
private InnerClassSingleton() {}
// 静态内部类在第一次使用时加载
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
public void showMessage() {
System.out.println("Hello, I'm an Inner Class Singleton!");
}
}
5. 枚举实现(最佳实践,防反射攻击)
public enum EnumSingleton {
INSTANCE;
public void showMessage() {
System.out.println("Hello, I'm an Enum Singleton!");
}
}
客户端使用示例
public class SingletonDemo {
public static void main(String[] args) {
// 饿汉式
EagerSingleton eager1 = EagerSingleton.getInstance();
EagerSingleton eager2 = EagerSingleton.getInstance();
System.out.println("EagerSingleton same instance? " + (eager1 == eager2));
eager1.showMessage();
// 枚举式
EnumSingleton enum1 = EnumSingleton.INSTANCE;
EnumSingleton enum2 = EnumSingleton.INSTANCE;
System.out.println("EnumSingleton same instance? " + (enum1 == enum2));
enum1.showMessage();
}
}
实际应用场景
单例模式在Java中有许多实际应用:
- 日志记录器:整个应用共享一个日志实例
- 配置管理:全局配置信息
- 数据库连接池:管理数据库连接资源
- 设备管理器:如打印机管理器
- Spring中的Bean:默认scope就是单例