**单例模式定义:**单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式通常在需要共享资源、控制实例数量或管理全局状态的情况下使用。它可以防止多个实例的创建,并提供一种方便的方式来访问该实例。单例模式在软件开发中广泛应用,例如数据库连接池、线程池、日志记录器等。
编写单例模式思路: 将构造函数设置成私有的,这样就不能从外部实例化改类。类内部提供一个全局访问的点获取该实例,提供一个函数,获取该类的实例。
单例模式分类:
1.程序初始化的时候初始化相应类的实例。(饿汉式)
例如:
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return instance;
}
}
2.需要使用的时候才触发生成类的实例对象(懒汉式)。
// 低性能版本
public class Singleton {
private static Singleton instance;
private Singleton() {
// 私有构造函数
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
//高性能 双通检查版本
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;
}
}
优点:
- 单实例:单例模式确保只创建一个类的一个实例,并提供对它的全局访问点。当整个系统中只有一个类对象时,这可能很有用。
- 全局访问:由于单例实例是全局可访问的,因此系统的多个部分可以轻松访问和使用它。这样就无需在不同对象或组件之间传递实例。
- 资源共享:单例对象可用于管理和共享数据库连接、线程池或文件系统等资源。通过拥有单个实例,可以更轻松地控制和协调对这些共享资源的访问。
- 延迟初始化:单例对象可以延迟初始化,这意味着仅在首次请求实例时才创建实例。这有助于提高性能和内存使用率,尤其是在创建对象成本高昂或占用大量资源的情况下。
缺点:
- 全局状态:由于单例模式只允许存在一个实例,该实例在整个应用程序中是可见的,因此它可以被任何部分访问和修改。这可能导致多个部分之间共享状态,使得状态的变化变得不可预测和难以控制。在并发环境下,全局状态可能引发竞态条件和线程安全问题。
- 紧密耦合:使用单例模式时,各个部分都直接依赖于单例实例,而不是通过接口或抽象类进行交互。这种紧密耦合会导致代码的可测试性和可维护性下降。因为无法轻松地替换或模拟单例实例,测试单个部分变得困难。任何对单例实例的更改都可能会影响到其他依赖于它的部分,使得维护和修改变得复杂。
- 难以模拟和测试:由于单例实例通常在应用程序的各个部分中直接访问,很难在测试环境中模拟或替换单例实例。这使得编写单元测试变得困难,因为无法轻松地控制和隔离单例实例的行为。这可能导致测试结果不可预测,增加了调试和故障排除的难度。
常见使用场景:
- 资源共享:当需要在系统中共享资源时,例如数据库连接池、线程池、日志对象等,可以使用单例模式确保只有一个实例被创建和共享。
- 控制实例数量:在某些情况下,系统需要限制某个类只能有一个实例存在,例如系统中只能有一个窗口管理器、只能有一个文件系统对象等。
- 配置信息:当需要在整个系统中共享配置信息时,可以使用单例模式来保存这些信息,并确保只有一个实例被访问和修改。
- 缓存:在需要缓存一些对象或数据时,可以使用单例模式来管理缓存,以确保只有一个缓存实例存在。
- 日志记录器:在记录系统日志时,为了避免多个日志记录器同时写入日志文件导致混乱,可以使用单例模式来管理日志记录器的实例。