曾经有老师们教过一种方法能够很好的去避免多线程申请单例模式创建一个实例对象的时候创建出多个不同的对象。而包括阿里巴巴的代码业务处理方式推荐清单也明码标价是这样。但是辩证法却无处不在,正所谓魔高一尺道高一丈,总有一些杠精喜欢钻牛角尖,打官司里叫做钻法律空子,互联网里叫做黑客。从比较深层的机制来讲,我们往往可以通过反射机制的一系列“反人类”操作实现对代码安全度的破坏!
相信大家的老师们曾经都是这样教学的,对吧?
没错这样子确实能够规避错误某一方面不需要领略synchronized那种烂番茄低下的效率,一方面帮我们维护了单例模式的英雄本色。好,下面就是破坏!
首先,无论如何,new DCLSingleton()这个操作不是原子性的,也就是说new方法的三要素并非不可拆分,因此就留给反射以可趁之机。这三要素分别是:
1.分配内存空间
2.执行构造方法,初始化对象
3.把对象指向这个空间
那么让我们看看下面的手段:
这段代码说明了些什么,我们利用这个类对象的反射方法可以直接无视构造方法的私有权限直接访问获取到一个鲜活的对象(declared构造器可以访问到私有)。同样地通过爆破手段设置为可见并重新实例化依旧可以创造出新的不同于源代码静态方法getInstance()得到的实例对象从而破坏懒汉式单例原则。
有人说好!既然这样,那我就再构造方法里面在加写代码来预防就好,比如:
这样子的话就不能够通过“偷渡”反射构造的手段来“偷对象”了,是吧?呵呵,你又错了!我只需要把main里面的第一种取值方式取消掉,那么就不能够导致lazyman不为空。不信试试!
有人会认为直接在属性中添加boolean值使构造器一旦被执行,就让boolean值取反,在取反条件下直接抛出一个我们编写好的异常就行(或者默认异常)。那么你又错了!请看:
你以为黑客难道就不可以使用爆破域的方式来修正boolean值了吗?结局这不还是一样!
种种这一切都是单例不安全的体现,所以在使用的时候大家要小心哦!至于什么是最安全的单例模式,请看这篇文章:最安全的单例模式-枚举_素净小凡人的博客-CSDN博客_绝对安全的单例模式