代码简洁 这是迄今为止最大的优点, 如果你曾经在 Java5 之前写过单例模式代码, 那么你会知道即使是使用双检锁你有时候也会返回不止一个实例对象.
虽然这种问题通过改善 java 内存模型和使用 volatile 变量可以解决, 但是这种方法对于很多初学者来说写起来还是很棘手.
相比用 synchronization 的双检锁实现方式来说, 枚举单例就简单多了.
你不相信? 比较一下下面的双检锁实现代码和枚举实现代码就知道了.
注: 文末有福利!
用枚举实现的单例:
这是我们通常写枚举单例的方式, 它可能包含实例变量和实例方法, 但是简单来说我什么都没用, 需要注意的是如果你使用实例方法, 你就需要确保方法的线程安全性, 避免它会影响对象的状态. 通常情况下枚举里面创建实例是线程安全的, 但是其它的方法就需要编程者自己去考虑了.
public enum EasySingleton{INSTANCE;}
代码就这么简单, 你可以使用 EasySingleton.INSTANCE 调用它, 比起你在单例中调用 getInstance() 方法容易多了.
用双检索实现单例:
下面的代码是用双检索实现单例模式的例子, 在这里 getInstance() 方法检查了两次来判断 INSTANCE 是否为 null, 这就是为什么叫双检索的原因, 记住双检索在 java5 之前是有问题的, 但是 java5 在内存模型中有了 volatile 变量之后就没问题了.
- public class DoubleCheckedLockingSingleton{
- private volatile DoubleCheckedLockingSingleton INSTANCE;
- private DoubleCheckedLockingSingleton(){}
- public DoubleCheckedLockingSingleton getInstance(){
- if(INSTANCE == null){
- synchronized(DoubleCheckedLockingSingleton.class){
- //double checking Singleton instance
- if(INSTANCE == null){
- INSTANCE = new DoubleCheckedLockingSingleton();
- }
- }
- }
- return INSTANCE;
- }
- }
你可以访问 DoubleCheckedLockingSingleTon.getInstance() 来获得实例对象.
现在看看二者创建一个懒加载线程安全的单例需要的代码数量.
使用枚举单例模式你只需要一行代码搞定因为枚举实例的创建是线程安全的.
你可能会说比起使用双检索方法还有更好的方法实现单例模式, 但是任何一种方法都有它的利和弊, 就像我下面例子中展示的我很喜欢的一种在类加载期间初始化静态域的单例实现方式, 但是要记住这不是一种懒加载单例方式.
用静态工厂方法实现单例:
这是 java 中我比较喜欢的一种实现单例模式的方法, 由于单例实例是 static 和 final 的, 当类第一次被加载到内存它就实例化了, 所以这种实例的创建方式是线程安全的.
- public class Singleton{
- //initailzed during class loading
- private static final Singleton INSTANCE = new Singleton();
- //to prevent creating another instance of Singleton
- private Singleton(){}
- public static Singleton getSingleton(){
- return INSTANCE;
- }
- }
你可以调用 Singleton.getInstance() 方法来获得实例对象.
2) 枚举单例可以自己处理序列化
传统的单例模式的另外一个问题是一旦你实现了 serializable 接口, 他们就不再是单例的了, 因为 readObject() 方法总是返回一个 新的实例对象, 就像 java 中的构造器一样. 你可以使用 readResolve() 方法来避免这种情况, 通过像下面的例子中这样用单例来替换新创建的实 例:
- private Object readResolve(){
- return INSTANCE;
- }
如果你的单例类包含状态的话就变的更复杂了, 你需要把他们置为 transient 状态, 但是用枚举单例的话, 序列化就不要考虑了.
3) 枚举单例是线程安全的
就像第一点提到的, 由于枚举实例的创建默认就是线程安全的, 你不需要担心双检锁问题.
总结: 通过提供序列化和线程安全并且几行代码搞定, 说明枚举单例模式是 java5 之后创建单例最好的方法. 你仍然可以使用其它你感觉很流行的方式来创建单例, 但是我还是要找一个能够使我信服的观点让我不去使用枚举作为单例, 如果你有, 请告诉我!
来源: https://www.cnblogs.com/Lovebugs/p/8746851.html