单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
单例的特点
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
单例模式的写法
可以参考这篇文章 单例模式的七种写法
破坏单例模式
反射
先来看一种常见的单例写法(饿汉模式):
1 | public class Singleton { |
测试代码:
1 | public class TestSingleton { |
运行结果:
1 | true |
结论:
通过反射可以破坏单例的属性,把它的构造方法设置成可访问的,然后去生成一个新的对象。
如何避免:
1 | public class Singleton { |
运行结果:
1 | true |
序列号和反序列化
我们对饿汉模式的单例简单改动,让它可以序列化。
1 | public class Singleton implements Serializable { |
测试代码:
1 | public class TestSingleton { |
运行结果:
1 | false |
结论:
通过对序列化后的对象进行反序列化得到的对象是一个新的对象,这就破坏了单例性。
如何避免:
1 | public class Singleton implements Serializable { |
原理就是,在 Singleton 中定义 readResolve 方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。
单元素枚举类型
这是实现单例的最佳方式,内部已经避免了反射和序列化反序列化。
1 | public enum Elvis { |
总结
- 常见的单例写法有他的弊端,存在安全性问题,如:反射,序列化的影响。
- 《Effective Java》作者 Josh Bloch 提倡使用单元素枚举类型的方式来实现单例,首先创建一个枚举很简单,其次枚举常量是线程安全的,最后有天然的可序列化机制和防反射的机制。