确保一个类在任何情况下都只有一个实例,并提供一个全局访问点
结构
单例类:只能创建一个实例的类
访问类:使用单例类的类
优缺点
优点:
1、在内存里只有一个实例,减少了内存的开销
2、避免对资源的多重占用
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
特点
1.单例类只能有一个实例
2.单例类必须自己创建自己的唯一实例
3.单例类必须给所有其他对象提供这一实例
使用场景
1.有频繁的实例化后又销毁的情况,适合考虑使用单例模式,如记录日志的log对象
2.创建对象需要消耗过多的系统资源,但又经常用到的资源,如数据库连接
创建模式
饿汉式
在该类初始化的时候就创建实例对象,线程是安全的
java
public class Singleton {
// 在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
private static Singleton instance = new Singleton();
// 私有化构造函数,防止外部创建对象
private Singleton(){}
// 提供一个全局访问点
public static Singleton getInstance(){
return instance;
}
}
饿汉式单例模式的优点是线程安全且效率高。由于实例在类加载时就创建了,所以获取对象的速度快。此外,由于构造函数是私有的,外部无法创建实例,因此可以保证只有一个实例
缺点:
饿汉式单例模式的缺点是可能会造成内存空间的浪费。如果该实例需要消耗大量的内存,且在程序的生命周期内可能不会使用到这个实例,那么就会造成内存空间的浪费
懒汉式
首次使用单例实例的时候创建,之后使用时再判断单例实例是否已创建,如果没有则创建实例
java
public class Singleton {
// 私有静态变量,用于保存单例对象
private static Singleton instance;
// 私有化构造函数,防止外部创建对象
private Singleton(){}
// 在第一次使用时,通过静态代码块创建实例
private static void initializeInstance() {
instance = new Singleton();
}
// 提供一个全局访问点
public static Singleton getInstance(){
if (instance == null) {
initializeInstance();
}
return instance;
}
}
instance变量在第一次调用getInstance()方法时才创建,因此线程是不安全的。为了实现线程安全,可以使用双重检查锁定、静态初始化块、枚举方式
优点:
只有在需要使用实例时才创建。这可以节省内存空间,特别是对于那些在程序生命周期中不一定需要的实例
双重检查锁定
java
public class Singleton {
// 使用volatile关键字,确保该变量在多线程环境下保持可见性
private volatile static Singleton instance;
// 私有化构造函数,防止外部创建对象
private Singleton(){}
// 在第一次使用时,通过静态代码块创建实例
private static void initializeInstance() {
instance = new Singleton();
}
// 提供一个全局访问点
public static Singleton getInstance(){
// 第一次检查,如果instance不为null,说明实例已经被初始化,直接返回实例即可
if (instance == null) {
synchronized (Singleton.class) {
// 第二次检查,如果instance为null,说明实例还没有被初始化,需要进入同步块进行初始化
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
instance
变量使用volatile
关键字修饰,以确保在多线程环境下保持可见性。双重检查锁定通过两次检查instance
变量的值是否为null
来实现线程安全
静态初始化块
java
public class Singleton {
// 私有静态变量,用于保存单例对象
private static Singleton instance;
// 私有化构造函数,防止外部创建对象
private Singleton(){}
// 使用静态初始化块来创建实例
static {
instance = new Singleton();
}
// 提供一个全局访问点
public static Singleton getInstance(){
return instance;
}
}
instance
变量在类加载时创建实例,因此它是线程安全的。
优点:避免了在第一次调用getInstance()
方法时进行同步的开销;线程安全的,可以确保只创建一个实例
缺点:会造成类加载的开销
枚举方式
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举是线程安全的,并且只会装载一次,枚举类是所有单例类实现中唯一不会被破坏的单例模式
java
public enum Singleton {
INSTANCE;
// 单例对象
private Singleton instance;
// 私有方法,用于创建实例
private void createInstance() {
instance = new Singleton();
}
// 公共方法,用于获取实例
public Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
createInstance();
}
}
}
return instance;
}
}
instance
变量在枚举类型被加载时创建实例,因此它是线程安全的。枚举类型在Java中是线程安全的,并且只有一个实例。getInstance()
方法用于获取实例,它首先检查instance
变量是否为null
,如果是null
,则进入同步块,再次检查instance
变量是否为null
,如果是null
,则创建一个新的实例
优点:具有更高的性能和更少的内存占用;使用枚举实现的单例模式更加简单和安全
缺点:只能创建单一的实例,无法实现多个实例的创建和管理