单例模式 (Singleton)
📖 通俗理解
想象一下,一个公司只能有一个CEO。不管你问谁"你们CEO是谁",得到的都是同一个人。
单例模式就是这样:保证一个类只有一个实例,并提供一个全局访问点。
🎯 解决什么问题?
有些对象我们只需要一个,比如:
- 数据库连接池
- 线程池
- 配置文件对象
- 日志对象
如果创建多个实例,会造成资源浪费或者数据不一致的问题。
🌰 生活中的例子
- 太阳:地球只有一个太阳
- 班长:一个班只有一个班长
- 打印机:办公室共用一台打印机
- 任务管理器:Windows 只能打开一个任务管理器
💻 Java 代码实现
方式一:饿汉式(推荐)
类加载时就创建实例,简单安全。
/**
* 饿汉式单例
* 优点:简单,线程安全
* 缺点:类加载就创建,可能造成资源浪费
*/
public class Singleton {
// 1. 私有静态实例,类加载时就创建
private static final Singleton INSTANCE = new Singleton();
// 2. 私有构造方法,防止外部 new
private Singleton() {
System.out.println("单例对象被创建了!");
}
// 3. 公共静态方法,返回唯一实例
public static Singleton getInstance() {
return INSTANCE;
}
public void doSomething() {
System.out.println("单例对象在工作...");
}
}
测试代码:
public class Test {
public static void main(String[] args) {
// 获取单例对象
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
// 验证是同一个对象
System.out.println(s1 == s2); // true
System.out.println(s1.hashCode());
System.out.println(s2.hashCode()); // hashCode 相同
}
}
方式二:懒汉式(双重检查锁)
第一次使用时才创建实例,节省资源。
/**
* 懒汉式单例(双重检查锁)
* 优点:延迟加载,节省资源
* 注意:必须用 volatile 修饰,防止指令重排序
*/
public class LazySingleton {
// volatile 防止指令重排序
private static volatile LazySingleton instance;
private LazySingleton() {
System.out.println("懒汉式单例被创建了!");
}
public static LazySingleton getInstance() {
// 第一次检查,避免不必要的同步
if (instance == null) {
synchronized (LazySingleton.class) {
// 第二次检查,防止多线程问题
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
方式三:静态内部类(最推荐)⭐
结合了饿汉式和懒汉式的优点!
/**
* 静态内部类单例(推荐使用)
* 优点:
* 1. 延迟加载(调用 getInstance 时才加载内部类)
* 2. 线程安全(JVM 保证类加载的线程安全)
* 3. 代码简洁
*/
public class BestSingleton {
private BestSingleton() {
System.out.println("静态内部类单例被创建了!");
}
// 静态内部类
private static class SingletonHolder {
private static final BestSingleton INSTANCE = new BestSingleton();
}
public static BestSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
方式四:枚举(最简洁)
《Effective Java》作者推荐的方式!
/**
* 枚举单例
* 优点:
* 1. 代码最简洁
* 2. 天然防止反射和序列化破坏单例
*/
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("枚举单例在工作...");
}
}
// 使用方式
// EnumSingleton.INSTANCE.doSomething();
🔥 实战案例:配置文件管理器
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 配置文件管理器(单例模式)
* 整个应用只需要一个配置对象
*/
public class ConfigManager {
private Properties properties;
private static class Holder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
private ConfigManager() {
properties = new Properties();
try {
InputStream is = getClass().getClassLoader()
.getResourceAsStream("config.properties");
if (is != null) {
properties.load(is);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static ConfigManager getInstance() {
return Holder.INSTANCE;
}
public String getProperty(String key) {
return properties.getProperty(key);
}
public String getProperty(String key, String defaultValue) {
return properties.getProperty(key, defaultValue);
}
}
使用方式:
public class Test {
public static void main(String[] args) {
// 在任何地方获取配置
ConfigManager config = ConfigManager.getInstance();
String dbUrl = config.getProperty("db.url");
String dbUser = config.getProperty("db.user", "root");
System.out.println("数据库地址:" + dbUrl);
System.out.println("数据库用户:" + dbUser);
}
}
📊 对比总结
| 实现方式 | 线程安全 | 延迟加载 | 防反射破坏 | 推荐指数 |
|---|---|---|---|---|
| 饿汉式 | ✅ | ❌ | ❌ | ⭐⭐⭐ |
| 懒汉式(双重检查锁) | ✅ | ✅ | ❌ | ⭐⭐⭐ |
| 静态内部类 | ✅ | ✅ | ❌ | ⭐⭐⭐⭐ |
| 枚举 | ✅ | ❌ | ✅ | ⭐⭐⭐⭐⭐ |
✅ 适用场景
- 需要频繁创建销毁的对象
- 创建对象耗时过长或耗资源过多
- 工具类对象
- 频繁访问数据库或文件的对象
⚠️ 注意事项
- 私有化构造方法:防止外部 new 对象
- 线程安全:多线程环境下要保证只创建一个实例
- 反射和序列化:可能会破坏单例,枚举方式可以解决
小结
单例模式是最简单的设计模式,核心就是:私有构造 + 静态方法返回唯一实例。
推荐使用 静态内部类 或 枚举 方式实现。
👉 下一篇:工厂方法模式
