单例模式的实现主要可以通过饿汉式、懒汉式(DCL)、以及枚举实现。
其中饿汉式、懒汉式皆可以通过反射的方式破坏单例,而枚举可以有效的防止反射破坏单例。
注意:单例中的构造是私有的,只有私有构造器才能防止外部类轻易通过构造方法来创建实例,从而破坏单例。
- 饿汉式单例模式
所谓饿汉式就是直接在实例初始化时,便调用构造方法来创建单例。
public class Hungry {
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry GetInstance(){
return HUNGRY;
}
}
2.懒汉式
懒汉式,顾名思义,就是在使用的时候才会创建实例。懒汉式通过双重检测(即在加锁前加锁后判断当前单例是否为null),外加关键字volatile实现(主要是因为构造方法若涉及到指令重排,会导致返回无效的单例)。
public class Lazy {
private Lazy(){
}
private volatile static Lazy LAZY;
public static Lazy GetInstance(){
if(LAZY == null){
// 加锁,双层检测
synchronized (Lazy.class){
if(LAZY == null){
LAZY = new Lazy(); // 在操作系统内部并非原子操作
// 1.分配内存空间 2.调用构造方法 3.将创建的对象执行分配的内存空间
// 若发生指令重排,可能导致执行顺序变为 132,此时若不加关键字 volatile ,会导致下一个线程,发现当前对象不为空,直接返回LAZY,但实际上该对象还未进行构造,执行了1 3步
}
}
}
return LAZY;
}
}
补充:前面讲到了通过反射可以破坏这两种单例模式,下面给出具体的破坏方法!
这里以懒汉式为例:
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Lazy lazy1 = Lazy.GetInstance();
System.out.println(lazy1);
// 采用反射,可以破坏懒汉式单例
Constructor constructor = Lazy.class.getDeclaredConstructor(null);
// 设置构造器可以外部访问
constructor.setAccessible(true);
Lazy lazy = constructor.newInstance();
System.out.println(lazy);
}
实验结果如下:
可以通过反射可以创建出不相同的实例。
3.枚举实现单例
枚举为什么能够防止反射破坏单例呢,很简单,源码中说的。。。。
即,不能够通过反射来创建枚举对象。
枚举实现单例的方式如下:
public enum SinglePattern {
INSTANCE;
private SinglePattern() {
}
public static SinglePattern GetInstance() {
return INSTANCE;
}
}



