Java 单例模式:确保一个类只有一个实例

发布时间:2026/5/20 4:20:40

Java 单例模式:确保一个类只有一个实例 Java 单例模式确保一个类只有一个实例单例模式是最常用、最重要的设计模式之一。它确保一个类在整个应用中只有一个实例并提供一个全局访问点。为什么需要单例场景数据库连接池// 如果没有单例每个地方都 new 一个连接池 DatabasePool pool1 new DatabasePool(); DatabasePool pool2 new DatabasePool(); // 结果创建了多个连接池浪费资源连接数超标 // 使用单例整个应用只有一个连接池 DatabasePool pool DatabasePool.getInstance(); // 无论哪里获取都是同一个实例单例模式的核心要求一个类只能有一个实例必须自行创建这个实例必须向整个系统提供这个实例 实现单例的 5 种方式1. 饿汉式Eager Initialization - 最简单特点类加载时就创建实例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 showMessage() { System.out.println(我是饿汉式单例); } } // 使用 Singleton s1 Singleton.getInstance(); Singleton s2 Singleton.getInstance(); System.out.println(s1 s2); // true是同一个对象优点简单线程安全缺点即使不用也会创建可能浪费内存2. 懒汉式Lazy Initialization - 延迟加载特点第一次使用时才创建实例2.1 线程不安全的懒汉式❌ 有问题public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { // 线程1判断为null instance new Singleton(); // 线程2也判断为null会创建第二个实例 } return instance; } } // ❌ 多线程环境下可能创建多个实例2.2 线程安全的懒汉式方法同步public class Singleton { private static Singleton instance; private Singleton() {} // 使用 synchronized 保证线程安全 public static synchronized Singleton getInstance() { if (instance null) { instance new Singleton(); } return instance; } } // ✅ 线程安全 // ❌ 每次获取都要同步性能差3. 双重检查锁Double-Checked Locking- 推荐特点只在第一次创建时同步兼顾性能和线程安全public class Singleton { // 必须使用 volatile 防止指令重排序 private static volatile Singleton instance; private Singleton() { System.out.println(双重检查锁单例被创建); } public static Singleton getInstance() { // 第一次检查避免不必要的同步 if (instance null) { // 同步代码块 synchronized (Singleton.class) { // 第二次检查确保只有一个线程能创建实例 if (instance null) { instance new Singleton(); } } } return instance; } public void showMessage() { System.out.println(我是双重检查锁单例); } }工作原理线程A调用 getInstance() ↓ if (instance null) // 第一次检查 ↓ 进入 synchronized 块 ↓ if (instance null) // 第二次检查 ↓ 创建实例 ↓ 返回实例 线程B调用 getInstance() ↓ if (instance null) // 第一次检查发现不为null ↓ 直接返回已有实例为什么需要 volatileinstance new Singleton(); // 这行代码分3步 // 1. 分配内存空间 // 2. 初始化对象 // 3. 将instance指向内存地址 // 没有volatileJVM可能重排序为1-3-2 // 导致其他线程拿到未初始化的对象4. 静态内部类Static Inner Class- 最佳实现特点利用类加载机制保证线程安全实现延迟加载public class Singleton { private Singleton() { System.out.println(静态内部类单例被创建); } // 静态内部类 private static class SingletonHolder { private static final Singleton INSTANCE new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } public void showMessage() { System.out.println(我是静态内部类单例); } }原理外部类加载时内部类不会加载调用getInstance()时才会加载内部类类加载是线程安全的由 JVM 保证实现了延迟加载 线程安全这是最推荐的单例实现方式5. 枚举Enum- 最安全特点JVM 保证线程安全防止反射和序列化攻击public enum Singleton { INSTANCE; // 唯一的实例 // 可以添加方法 public void showMessage() { System.out.println(我是枚举单例); } // 可以添加属性 private String data 单例数据; public String getData() { return data; } public void setData(String data) { this.data data; } } // 使用 Singleton singleton Singleton.INSTANCE; singleton.showMessage(); System.out.println(singleton.getData());优点简单线程安全防止反射攻击防止序列化破坏单例防止克隆破坏单例单例模式的应用场景场景1配置管理器public class ConfigManager { private static volatile ConfigManager instance; private Properties config; private ConfigManager() { loadConfig(); } public static ConfigManager getInstance() { if (instance null) { synchronized (ConfigManager.class) { if (instance null) { instance new ConfigManager(); } } } return instance; } private void loadConfig() { config new Properties(); try (InputStream input getClass().getClassLoader() .getResourceAsStream(config.properties)) { config.load(input); } catch (IOException e) { throw new RuntimeException(加载配置文件失败, e); } } public String getProperty(String key) { return config.getProperty(key); } public int getIntProperty(String key) { return Integer.parseInt(config.getProperty(key)); } } // 使用 String dbUrl ConfigManager.getInstance().getProperty(database.url); int timeout ConfigManager.getInstance().getIntProperty(timeout);场景2数据库连接池public class DatabasePool { private static DatabasePool instance; private ListConnection connections; private DatabasePool() { // 初始化连接池 connections new ArrayList(); for (int i 0; i 10; i) { connections.add(createConnection()); } } public static DatabasePool getInstance() { if (instance null) { synchronized (DatabasePool.class) { if (instance null) { instance new DatabasePool(); } } } return instance; } public Connection getConnection() { // 从连接池获取连接 synchronized (connections) { if (!connections.isEmpty()) { return connections.remove(0); } } // 连接池为空创建新连接 return createConnection(); } public void releaseConnection(Connection conn) { // 释放连接回到连接池 synchronized (connections) { if (connections.size() 10) { connections.add(conn); } else { // 关闭多余的连接 closeConnection(conn); } } } private Connection createConnection() { // 创建数据库连接 return null; // 实际实现 } private void closeConnection(Connection conn) { // 关闭连接 } }场景3日志记录器public class Logger { private static Logger instance; private File logFile; private FileWriter writer; private Logger() { try { logFile new File(app.log); writer new FileWriter(logFile, true); } catch (IOException e) { e.printStackTrace(); } } public static Logger getInstance() { if (instance null) { synchronized (Logger.class) { if (instance null) { instance new Logger(); } } } return instance; } public void log(String message) { try { writer.write(LocalDateTime.now() - message \n); writer.flush(); } catch (IOException e) { e.printStackTrace(); } } public void close() { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } }场景4缓存管理器public class CacheManager { private static CacheManager instance; private MapString, Object cache; private CacheManager() { cache new ConcurrentHashMap(); } public static CacheManager getInstance() { if (instance null) { synchronized (CacheManager.class) { if (instance null) { instance new CacheManager(); } } } return instance; } public void put(String key, Object value) { cache.put(key, value); } public Object get(String key) { return cache.get(key); } public void remove(String key) { cache.remove(key); } public void clear() { cache.clear(); } }单例模式的问题和解决方案问题1反射攻击反射可以调用私有构造器创建新实例public class ReflectionAttackTest { public static void main(String[] args) throws Exception { Singleton s1 Singleton.getInstance(); // 通过反射创建第二个实例 ConstructorSingleton constructor Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton s2 constructor.newInstance(); System.out.println(s1 s2); // false单例被破坏了 } }解决方案public class Singleton { private static volatile Singleton instance; private static boolean flag false; private Singleton() { // 防止反射攻击 if (flag) { throw new RuntimeException(单例模式禁止反射创建); } flag true; } public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }问题2序列化破坏序列化和反序列化会创建新实例public class SerializationAttackTest { public static void main(String[] args) throws Exception { Singleton s1 Singleton.getInstance(); // 序列化 ObjectOutputStream oos new ObjectOutputStream( new FileOutputStream(singleton.ser) ); oos.writeObject(s1); oos.close(); // 反序列化 ObjectInputStream ois new ObjectInputStream( new FileInputStream(singleton.ser) ); Singleton s2 (Singleton) ois.readObject(); ois.close(); System.out.println(s1 s2); // false单例被破坏了 } }解决方案public class Singleton implements Serializable { private static final long serialVersionUID 1L; 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; } // 防止序列化破坏 protected Object readResolve() { return getInstance(); // 返回已有实例 } }问题3克隆破坏clone() 方法会创建新实例public class Singleton implements Cloneable { // ... 单例实现 Override protected Object clone() throws CloneNotSupportedException { return super.clone(); // ❌ 这样会破坏单例 } // ✅ 正确禁止克隆 Override protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(单例模式禁止克隆); } }线程安全的单例模式对比实现方式线程安全延迟加载防止反射防止序列化性能饿汉式✅❌❌❌好懒汉式同步方法✅✅❌❌差双重检查锁✅✅❌❌好静态内部类✅✅❌❌好枚举✅❌✅✅好最佳实践建议1. 选择哪种实现// 1. 如果确定一定会用到用饿汉式 public class EarlySingleton { private static final EarlySingleton INSTANCE new EarlySingleton(); } // 2. 大多数情况用静态内部类推荐 public class InnerClassSingleton { private static class Holder { static final InnerClassSingleton INSTANCE new InnerClassSingleton(); } } // 3. 需要防止反射/序列化攻击用枚举 public enum EnumSingleton { INSTANCE; } // 4. 双重检查锁用于复杂初始化 public class ComplexSingleton { private static volatile ComplexSingleton instance; private SomeResource resource; private ComplexSingleton() { resource new SomeResource(); // 复杂的初始化 resource.init(); } }2. 在 Spring 中使用单例Component // Spring 默认就是单例 Scope(singleton) // 显式声明 public class UserService { // Spring 容器管理的单例 } // 或者使用 Bean Configuration public class AppConfig { Bean Scope(singleton) public DataSource dataSource() { return new HikariDataSource(); } }3. 单例模式的替代方案// 1. 使用依赖注入推荐 public class UserService { // 通过构造器注入 private final DatabasePool pool; public UserService(DatabasePool pool) { this.pool pool; } } // 2. 使用静态工具类 public final class StringUtils { private StringUtils() {} // 私有构造器防止实例化 public static boolean isEmpty(String str) { return str null || str.trim().isEmpty(); } }单例模式常见面试题1. 为什么要用单例模式控制资源的使用如连接池控制实例的数量全局访问点节省内存和计算资源2. 单例模式的缺点是什么难以扩展只能有一个实例违背单一职责原则控制自己业务逻辑多线程环境需要特殊处理单元测试困难全局状态3. 如何实现线程安全的单例使用 synchronized使用双重检查锁使用静态内部类使用枚举4. 单例模式 vs 静态类单例模式静态类可以有实例可以继承不能实例化不能继承可以实现接口不能实现接口可以延迟初始化类加载时就初始化可以序列化不能序列化实战练习创建一个计数器单例要求整个应用只有一个计数器实例线程安全可以获取当前计数可以增加计数可以重置计数// 你的实现 public class Counter { // TODO: 实现线程安全的单例计数器 }总结单例模式的本质控制实例数量节省资源提供全局访问。选择指南简单场景 → 饿汉式一般场景 → 静态内部类最推荐防止攻击 → 枚举复杂初始化 → 双重检查锁记住核心单例模式的核心是私有构造器 静态获取方法。根据具体需求选择合适的实现方式并注意线程安全和可能的安全攻击。

相关新闻