代码改变世界

设计模式-单例模式

2018-03-01 17:54  FelixShen  阅读(434)  评论(0编辑  收藏  举报

 

  • 前言: 设计模式是一个经常被提及的话题,无论是日常开发还是面试,都会经常被提及。让举出一些常用的设计模式,基本都会首先列举: 单例模式 工厂模式。我们这个设计模式系列,也不去追求新颖,我们也从提及率最高的单例模式开始。
  •  使用场景:顾名思义,单例就是单实例的意思,是指在整个系统中,某个类的对象都只存在一个,同时对外有一个统一的暴露方法来获取这个实例。
  •  思路要点: 既然只能有一个对象存在,那就不能让人随便new,所以构造函数必须私有。同时要提供一个静态的public方法来获取这个实例。记住这两个要点,写出来其实就不太难
  • 单例模式的写法 通常有三种 : 饿汉模式  懒汉模式  以及最优秀的枚举模式。下面分别介绍这三种写法

 

--  饿汉模式  (对象必须一直存在,不要等到我要使用的时候才给我创造,我很饿,等不及!)

 

-- 懒汉模式(我不需要你一直存在,但是当我需要你时,你得存在)

 

 

懒汉模式相比饿汉模式存在线程安全的问题,因为当多个线程同时访问 getInstance方法的时候,第一个线程 获取为null ,然后创建,同时第二个线程也正在访问,也创建了一个对象,那就和我们的初衷相违背了。所以我们在懒汉模式下,加上了 synchronized同步块来保证线程安全。这是要注意的一个点。

 

这么说,上面两种方法就应该是不错的单例模式写法了,但是实际上在面对 序列化反序列化攻击的时候, 就会出现问题。

 

 

测试这个方法,我们会发现 第一次 返回的是true,  而第二次通过序列化,反序列化折腾之后,就为false了。 饿汉模式同样存在这样的被攻击弊端。

完美的解决方案就是 枚举模式实现的 单例

 

 

是的,就是如此简单粗暴。。为什么枚举可以呢,因为枚举字节码反编译之后,Instance 也是以public static 的形式存在,同时枚举对象的 序列化只是将名字输出到结果中,而反序列化的时候,则是根据名字 查找枚举对象,因此,反序列化后的实例和被序列化的对象实例相同。

 

测试之后,发现两次都是true ,完美!

 

 

   代码地址:  https://github.com/felixmaomao/design-patterns