来源于书籍:Spring5核心原理与30个类手写实战
使用静态内部类实现的单例模式,利用了JVM的运行机制,外部类初始化的时候并不会初始化静态的内部类,只有在使用到了内部类的时候才会去初始化他,然后就会使单例对象初始化,这种方式是Effective Java中比较推荐的一种方式。
问题在于,反射能破坏这种单例模式,因为反射能调用其构造函数创建一个新的对象,那么怎么办?就需要在构造函数上进行处理了,就得到了书上这样的方式
public class StaticInnerObjectSingleton {
private StaticInnerObjectSingleton() {
if (LazyHolder.SINGLETON != null) {
throw new IllegalStateException("不允许创建多个实例");
}
}
public static StaticInnerObjectSingleton getInstance() {
return LazyHolder.SINGLETON;
}
private static class LazyHolder {
static final StaticInnerObjectSingleton SINGLETON = new StaticInnerObjectSingleton();
}
}
为什么要判断为空?而不是直接抛出异常。因为自己第一次初始化的时候还需要去调用此方法,所以当然不能直接抛出异常。
如果反射调用构造函数,执行到if的时候就会导致静态内部类的初始化,然后在初始化的过程中会导致静态内部类里面的静态代码块执行,然后静态代码快又会调用构造函数,这个时候其内部的SINGLETON肯定是空,所以能初始化成功,初始化成功之后SINGLETON就不为空了,就导致执行失败,成功避免了反射的破坏单例