最好的单例模式?


饿汉

在这里插入图片描述
1.为什么要加final?

防止子类继承父类,子类中的一些不适当操作破坏单例。

2.如果实现了序列化接口,还要做什么来防止反序列化破坏单例?

private Object readResolve() {return instance;}
这个方法在反序列化创建对象时会直接return当前的对象,而不会将字节码生成对象当成结果返回而破坏单例。

3.为什么要设置私有?是否能反射创建新的实例?

防止其它类通过构造方法直接创建对象
不能,任然可以通过暴力反射的方式创建对象

4.这样初始化能否保证单例对象创建时的线程安全?

能,静态成员变量的初始化是在类加载阶段完成的,jvm会保证类加载阶段代码的线程安全性

5.为什么提供静态方法而不是直接将instanse设置为public?

扫描二维码关注公众号,回复: 11586539 查看本文章

使用方法会方便更好的改进,而使用变量暴露则无法进行改进


枚举

在这里插入图片描述
1.枚举单例在创建时是否有并发问题?

没有,因为枚举类中的实例也是静态成员变量,其初始化也是在类加载阶段完成的

2.枚举单例是否会被反射破坏?

不会,反射在通过newInstance()创建对象时会检查这个类是否是枚举类,如果是枚举类就会抛出异常

3.枚举单例是否会被反序列化破坏?

不会,java序列化对枚举的序列化做了特殊规定,在序列化时只是将枚举对象的那么属性输出到结果,在反序列化时通过Enum.valuOf()方法根据名字查找对象,而不是新建一个对象。

4.枚举单例属于懒汉式还是饿汉式?

饿汉式,实例在类加载阶段创建


懒汉

在这里插入图片描述

双重检测

在这里插入图片描述
1.为什么要加volatile?

synchronized并未完全将单例对象的操作全部锁住,所以此时不能保证该共享变量操作的有序性,所以需要通过volatile关键字防止指令重排,来保证有序性。

2.为什么还要在这里加空判断?

防止出现多线程获取单例对象时,多次创建对象,破坏单例。
例如:线程A和线程B都执行getInstance()方法,由于未加互斥控制,都执行到图示synchronized这行代码,线程A获取到类对象锁,线程B进入类对象的所关联的Monitor的EntryList阻塞。线程A执行完后,线程B竞争到类对象锁,此时如果没有该空判断,线程B会new一个新的实例返回,从而破坏单例。


静态内部类

在这里插入图片描述
1.属于懒汉式还是饿汉式?

懒汉式 ,因为类加载本身是懒惰的,类只有在被使用到时才会被加载。

2.在创建时是否有并发问题?

否,静态成员变量的初始化都是线程安全的。


猜你喜欢

转载自blog.csdn.net/weixin_44017425/article/details/107414572