- 前言: 设计模式是一个经常被提及的话题,无论是日常开发还是面试,都会经常被提及。让举出一些常用的设计模式,基本都会首先列举: 单例模式 工厂模式。我们这个设计模式系列,也不去追求新颖,我们也从提及率最高的单例模式开始。
- 使用场景:顾名思义,单例就是单实例的意思,是指在整个系统中,某个类的对象都只存在一个,同时对外有一个统一的暴露方法来获取这个实例。
- 思路要点: 既然只能有一个对象存在,那就不能让人随便new,所以构造函数必须私有。同时要提供一个静态的public方法来获取这个实例。记住这两个要点,写出来其实就不太难
- 单例模式的写法 通常有三种 : 饿汉模式 懒汉模式 以及最优秀的枚举模式。下面分别介绍这三种写法
-- 饿汉模式 (对象必须一直存在,不要等到我要使用的时候才给我创造,我很饿,等不及!)
-- 懒汉模式(我不需要你一直存在,但是当我需要你时,你得存在)
懒汉模式相比饿汉模式存在线程安全的问题,因为当多个线程同时访问 getInstance方法的时候,第一个线程 获取为null ,然后创建,同时第二个线程也正在访问,也创建了一个对象,那就和我们的初衷相违背了。所以我们在懒汉模式下,加上了 synchronized同步块来保证线程安全。这是要注意的一个点。
这么说,上面两种方法就应该是不错的单例模式写法了,但是实际上在面对 序列化反序列化攻击的时候, 就会出现问题。
测试这个方法,我们会发现 第一次 返回的是true, 而第二次通过序列化,反序列化折腾之后,就为false了。 饿汉模式同样存在这样的被攻击弊端。
完美的解决方案就是 枚举模式实现的 单例
是的,就是如此简单粗暴。。为什么枚举可以呢,因为枚举字节码反编译之后,Instance 也是以public static 的形式存在,同时枚举对象的 序列化只是将名字输出到结果中,而反序列化的时候,则是根据名字 查找枚举对象,因此,反序列化后的实例和被序列化的对象实例相同。
测试之后,发现两次都是true ,完美!