设计模式之单例模式

63 阅读3分钟

image.png

特点

  • 在Java应用中,单例模式能保证在一个JVM中,该对象只有一个实例存在
  • 构造器必须是私有的,外部类无法通过调用构造器方法创建该实例
  • 没有公开的set方法,外部类无法调用set方法创建该实例
  • 提供一个公开的get方法获取唯一的这个实例

好处

  • 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销
  • 省去了new操作符,降低了系统内存的使用频率,减轻GC压力
  • 系统中某些类,如spring里的controller,控制着处理流程,如果该类可以创建多个的话,系统完全乱了
  • 避免了对资源的重复占用

实现

饿汉式

// 1、为什么使用final:防止子类覆盖父类方法破坏单例  
// 2、如果实现了序列化,如何防止反序列化破坏单例:加入readResolve方法,在反序列化时就会采用readResolve返回的对象,而不是反序列化生成的对象  
public final class SingleEhan implements Serializable {  
  
//3、构造方法为什么为私有:防止使用者使用构造器创建对象,破坏单例。  
//4、能否防止反射:不能,设置accessable为true通过构造方法反射依然会获得对象  
private SingleEhan() {  
  
}  
  
//5、这样初始化单例能否保证单例对象创建时的线程安全:可以,静态成员变量,在类初始化的时候创建。由jvm保证线程安全,会造成内存浪费  
private static final SingleEhan singleEhan = new SingleEhan();  
  
// 6、为什么使用方法而不是直接将singleEhan设置为public:为了更好的封装,可以改写成懒惰初始化。也可以支持泛型。方法中可以写一些别的逻辑  
public static SingleEhan getSingleEhan() {  
return singleEhan;  
}  
  
public Object readResolve() {  
return singleEhan;  
}  
}

懒汉式

public final class SingleLanHan {  
private static SingleLanHan singleLanHan = null;  
  
private SingleLanHan() {  
}  
  
//此处能否保证线程安全:可以,因为创建对象或者获得对象会加锁。但是,锁的范围较大,就算对象创建好了,以后获得对象也要加锁,性能较低!  
public static synchronized SingleLanHan getSingleLanhan() {  
if (singleLanHan != null) {  
return singleLanHan;  
}  
singleLanHan = new SingleLanHan();  
return singleLanHan;  
}  
}

双重检查(DCL)

public final class SingleDcl {  
// 1、此处为何要用volatile:防止singleDcl 在实例化的时候指令重排,导致别的线程获得实例的时候虽不为空,但还未赋值。  
private static volatile SingleDcl singleDcl = null;  
  
private SingleDcl() {  
  
}  
// 2、对比单纯的懒汉式,这里有什么优点:初始化成功后,都不用上锁  
public static SingleDcl getSingleDcl() {  
if(singleDcl!=null) {  
return singleDcl;  
}  
synchronized (SingleDcl.class) {  
// 3、此处为什么还要为空判断:因为首次初始化时都会进入到同步块。  
if(singleDcl!=null) {  
return singleDcl;  
}  
singleDcl = new SingleDcl();  
}  
return singleDcl;  
}  
  
}

静态内部类

public final class SingleInnerClass {  
  
private SingleInnerClass() {  
  
}  
  
// 1、属于懒汉式还是饿汉式:懒汉式创建,类加载是懒惰的,在调用时静态变量才会初始化。  
private static class LazyHolder {  
static final SingleInnerClass singleInnerClass = new SingleInnerClass();  
}  
  
// 2、在创建单例时是否有并发问题:没有,初始化属于静态变量初始化,由jvm保证线程。  
public static SingleInnerClass getSingleInnerClass() {  
return LazyHolder.singleInnerClass;  
}  
  
}

枚举

public enum SingleEnum {  
INSTANCE;  
  
public void doSomething() {  
  
}  
}