面试官看完我手写的单例直接惊呆了! (3)

面试官看完我手写的单例直接惊呆了!

面试官看完我手写的单例直接惊呆了!

然后,判断 rep 是否和 obj 相等 。 obj 是刚才我们通过构造函数创建出来的新对象,而由于我们重写了 readResolve 方法,直接返回了单例对象,因此 rep 就是原来的单例对象,和 obj 不相等。

于是,把 rep 赋值给 obj ,然后返回 obj。

所以,最终得到这个 obj 对象,就是我们原来的单例对象。

至此,我们就明白了是怎么一回事。

一句话总结就是:当从对象流 ObjectInputStream 中读取对象时,会检查对象的类否定义了 readResolve 方法。如果定义了,则调用它返回我们想指定的对象(这里就指定了返回单例对象)。

总结

因此,完整的 DCL 就可以这样写,

public class Singleton implements Serializable { //注意,此变量需要用volatile修饰以防止指令重排序 private static volatile Singleton singleton = null; private Singleton(){ if(singleton != null){ throw new RuntimeException("Can not do this"); } } public static Singleton getInstance(){ //进入方法内,先判断实例是否为空,以确定是否需要进入同步代码块 if(singleton == null){ synchronized (Singleton.class){ //进入同步代码块时再次判断实例是否为空 if(singleton == null){ singleton = new Singleton(); } } } return singleton; } // 定义readResolve方法,防止反序列化返回不同的对象 private Object readResolve(){ return singleton; } }

另外,不知道细心的读者有没有发现,在看源码中 switch 分支有一个 case TC_ENUM 分支。这里,是对枚举类型进行的处理。

感兴趣的小伙伴可以去研读一下,最终的效果就是,我们通过枚举去定义单例,就可以防止序列化破坏单例。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpspfp.html