基本概念
单件模式:一种创建型设计模式,确保一个类在系统中只有一个实例,并提供全局访问点。主要用于解决全局资源共享的问题,常用于需要唯一控制的场景(如配置管理、日志记录、线程池等)。
核心结构:
- 类自身负责创建唯一实例
- 提供全局访问该实例的方法
- 确保实例只能被创建一次
示例说明
场景:设计一个全局日志记录器,确保系统中只有一个日志实例
- 饿汉式单例(线程安全,立即加载)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class LoggerHungry { private static final LoggerHungry instance = new LoggerHungry(); private LoggerHungry() { System.out.println("日志记录器初始化"); } public static LoggerHungry getInstance() { return instance; } public void log(String message) { System.out.println("日志记录: " + message); } }
|
- 懒汉式单例(延迟加载,需手动处理线程安全)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class LoggerLazy { private static LoggerLazy instance; private LoggerLazy() {} public static synchronized LoggerLazy getInstance() { if (instance == null) { instance = new LoggerLazy(); } return instance; } public static LoggerLazy getInstanceDCL() { if (instance == null) { synchronized (LoggerLazy.class) { if (instance == null) { instance = new LoggerLazy(); } } } return instance; } public void log(String message) { System.out.println("日志记录: " + message); } }
|
- 静态内部类单例(推荐,兼顾延迟加载和线程安全)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class LoggerStaticInner { private LoggerStaticInner() {} private static class InstanceHolder { private static final LoggerStaticInner INSTANCE = new LoggerStaticInner(); } public static LoggerStaticInner getInstance() { return InstanceHolder.INSTANCE; } public void log(String message) { System.out.println("日志记录: " + message); } }
|
- 枚举单例(最简捷,天然支持序列化和反序列化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public enum LoggerEnum { INSTANCE; public void log(String message) { System.out.println("日志记录: " + message); } }
public class Client { public static void main(String[] args) { LoggerHungry hungry1 = LoggerHungry.getInstance(); LoggerHungry hungry2 = LoggerHungry.getInstance(); System.out.println("饿汉式实例是否相同: " + (hungry1 == hungry2)); LoggerStaticInner inner1 = LoggerStaticInner.getInstance(); LoggerStaticInner inner2 = LoggerStaticInner.getInstance(); System.out.println("静态内部类实例是否相同: " + (inner1 == inner2)); LoggerEnum enum1 = LoggerEnum.INSTANCE; LoggerEnum enum2 = LoggerEnum.INSTANCE; System.out.println("枚举式实例是否相同: " + (enum1 == enum2)); } }
|
- 执行结果
1 2 3 4 5 6
| 日志记录器初始化 饿汉式实例是否相同: true 静态内部类实例是否相同: true 枚举式实例是否相同: true
|
类图如下:

总结
优点:
- 全局唯一:确保系统中只有一个实例,避免资源重复创建
- 方便访问:提供全局访问点,简化代码设计
- 节约资源:适合管理共享资源(如数据库连接池、线程池)
缺点:
- 违反单一职责:单例类可能承担创建实例和业务逻辑的双重职责
- 不利于测试:单例模式难以模拟不同实例场景,增加单元测试复杂度
- 潜在线程安全问题:非线程安全的实现需要额外处理同步
- 可能导致内存泄漏:长期持有资源可能引发内存问题
应用场景
- 全局资源管理:日志记录器(
java.util.LogManager
),配置管理器(读取系统配置),线程池(统一管理线程资源)
- 系统控制:Windows任务管理器(确保只有一个实例),打印机后台处理程序,数据库连接池(避免创建过多连接)
- 框架设计:
Spring
框架中的ApplicationContext
(单例作用域),Hibernate
的SessionFactory
- JDK中的应用:
java.lang.Runtime
:获取当前运行时环境,全局唯一,java.util.LogManager
:日志系统的管理类,javax.servlet.ServletContext
:Web应用的全局上下文
注意
- 序列化问题:单例类若实现
Serializable
接口,需添加readResolve()
方法防止反序列化时创建新实例。
- 反射攻击:私有构造函数无法完全阻止反射创建实例,需在构造函数中添加判断
- 多类加载器:不同类加载器可能创建多个实例,需特殊处理
- 推荐实现:优先使用静态内部类单例或枚举单例,兼顾线程安全和性能
- 与工厂模式相比:关注对象创建,单例模式关注对象唯一性