原文地址:http://www.cnblogs.com/AprilCal/p/5426007.html

  • 理由一:无需再考虑可序列化的情况

      《effective java》第77条:对于实例控制,枚举类型优先于readResolve

      说到readResolve,有的人可能会不甚清楚其作用,简单来说,readResolve的作用是这样的:readResolve特性允许你用readObject创建的实例代替另一个实例。
      对于一个正在被反序列化的对象,如果他的类定义了一个readResolve方法,并具备正确的声明,那么在反序列化之后,新建对象上的readResolve方法就会被调用。然后,该方法返回的对象引用将会被返回,取代新建的对象。

      《effective java》中 第三条提到,如果在这个类的声明中加上了‘inplments
    Serializable’的字样,它就不再是一个Singleton。无论该类使用了默认的序列化形式,还是自定义的序列化形式,都没有关系;也跟它是否提供了显示的readObject方法无关。任何一个readObject方法,不管是显示的还是默认的,它都会返回一个新建的实例。这个新建的实例不同于该类初始化时创建的实例。这也是为什么要提供readResolve方法的原因。

    《effective java》第3条中:
      To make a singleton class that is implemented using either of the
    previous approaches serializable (Chapter 11), it is not sufficient
    merely to add implements Serializable to its declaration. To maintain
    the singleton guarantee, you have to declare all instance fields
    transient and provide a readResolve method (Item 77). Otherwise, each
    time a serialized instance is deserialized, a new instance will be
    created, leading, in the case of our example, to spurious Elvis
    sightings. To prevent this, add this readResolve method to the Elvis
    class:
    // readResolve method to preserve singleton property
    private Object readResolve() {
    // Return the one true Elvis and let the garbage collector
    // take care of the Elvis impersonator.
    return INSTANCE;
    }
      由于中文版中概念稍有混淆,特意去查阅英文原文。大体上的意思如下:
      为了使之前的方法实现的Singleton类可序列化,仅仅在声明中加上"implements
    Serializable"是不够的。为了保持Singleton,你需要声明所有实例字段为transient并提供readResolve方法。否则,每次一个序列化的实例被反序列化时,都会创建一个新的实例。比如在我们的例子中,会导致假冒的Elvis情况。为防止此种情况,要在Elvis类中加入下面这个readResolve方法:
    看到这里应该明白,对于一个需要序列化的Singleton来说,我们需要手动为其添加readResolve方法,在某些情况下,这样做会尤为复杂。
      而用枚举来实现Singleton则完全不必考虑,因为jvm可以保证这一点。

  • 理由二:无需再考虑通过反射调用私有构造函数的情况

      《effective java》第三条中:享有特权的客户端可以借助AccessiableObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让他在被要求第二次创建第二个实例的时候抛出异常。

       先看一个例子


    package singleton;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    //AprilCal on 2016.4.23
    class Singleton//最普通的一种Singleton实现
    {
    private static Singleton INSTANCE=new Singleton();
    private Singleton()
    {
    System.out.println("私有构造函数被调用");
    }
    public static Singleton getInstance()
    {
    return INSTANCE;
    }
    }
    enum EnumSingleton2//枚举实现的Singleton
    {
    INSTANCE;
    private EnumSingleton2()
    {
    System.out.println("enum 私有构造函数被调用");
    }
    }
    public class SingletonTest
    {
    public static void main(String[] args)
    throws ClassNotFoundException, IllegalAccessException,
    IllegalArgumentException, InvocationTargetException,
    NoSuchMethodException, SecurityException, InstantiationException
    {
    Class <?> cls1 = Class.forName("singleton.Singleton");
    Class <?> cls2 = Class.forName("singleton.EnumSingleton2");
    //通过反射调用Singleton的构造函数
    Constructor <?> c0=cls1.getDeclaredConstructor();
    c0.setAccessible(true);
    Singleton s=(Singleton)c0.newInstance();
    //通过反射调用EnumSingleton的构造函数
    Constructor &lt?> c1=cls2.getDeclaredConstructor();
    c1.setAccessible(true);
    EnumSingleton es=(EnumSingleton)c1.newInstance();
    }
    }

这段代码的执行结果如下:

私有构造函数被调用
enum 私有构造函数被调用
私有构造函数被调用
Exception in thread "main" java.lang.NoSuchMethodException: singleton.EnumSingleton2.()
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
at singleton.SingletonTest.main(SingletonTest.java:46)
>
在普通的Singleton中,我们通过反射机制调用了其私有的构造函数,而在通过反射调用enmuSingleton的私有构造函数时,则直接抛出了异常。可见,在使用枚举实现Singleton时,jvm会替我们完成防止通过反射调用私有构造函数的工作。

    • 理由三:枚举实例创建是线程安全的,无需再考虑Double checked locking

      我们都知道,在延迟初始化的情况下,为了保证线程安全,通常在实现Singleton的时候使用Double checked locking,而在枚举的情况下,我们则可以完全不必考虑这些。
      关于Double checked locking实现Singleton详见http://www.cnblogs.com/techyc/p/3529983.html
      并且此连接中有处细节需要纠正一下,用到Double checked locking时,必须要用volatile修饰单例的实例,否则将毫无意义。
      至于为什么要用volatile修饰,我不说了,嘻嘻。

    • 总结

      总结起来使用枚举类型实现Singleton主要有以下三大优势:

      1:无需再考虑可序列化的情况

      2:无需再考虑通过反射调用私有构造函数的情况

      3:枚举实例创建是线程安全的

      最后,千言万语一个字,使用枚举实现单例情况会好的多,但不排除某些情况用特殊的方法实现单例也同样很高效。

为什么要用枚举实现Singleton--Java的更多相关文章

  1. Singleton.java.ft not found 相关错误的解决办法

    Entry fileTemplates//Singleton.java.ft not found in C:/Users/admin/Desktop/android-studio/lib/resour ...

  2. 单例模式的七种实现-Singleton(Java实现)

    1. 饿汉式 实现代码: public class Singleton { private Singleton() { } private static Singleton singleton = n ...

  3. 用枚举来处理java自定义异常

    在系统开发过程中,总少不免要自己处理一些异常信息,然后将异常信息变成友好的提示返回到客户端的这样一个过程,之前都是new一个自定义的异常,当然这个所谓的自定义异常也是继承RuntimeExceptio ...

  4. Singleton(Java)

    1.定义私有静态易变的类变量2.定义getInstance静态方法 2.1.若静态变量为null,则在同步类类型的同时判断静态实例是否为null, 是null则创建新实例赋给静态变量 2.2.不为nu ...

  5. 单元素枚举类型singleton模块

    public enum Elvis { INSTANCE; public void leaveTheBuilding() { System.out.println("Whoa baby, I ...

  6. Java中的GOF23(23中设计模式)--------- 单例模式(Singleton)

    Java中的GOF23(23中设计模式)--------- 单例模式(Singleton) 在Java这这门语言里面,它的优点在于它本身的可移植性上面,而要做到可移植的话,本身就需要一个中介作为翻译工 ...

  7. Java枚举enum以及应用:枚举实现单例模式

    枚举作为一个常规的语言概念,一直到Java5才诞生不得不说有点奇怪,以至于到现在为止很多程序员仍然更喜欢用static final的形式去命名常量而不使用,一般情况下,Java程序员用这种方式去实现枚 ...

  8. Java 单例(Singleton)模式

    一.什么是单例模式: 单例模式是一种确保了一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.被实例化的类称为单例类. 二.单例模式的特点: 单例类只有一个实例. 单例类必须自行创建自己唯一的 ...

  9. 理解Java枚举类型

    (参考资料:深入理解java enum) 1.原理:对编译后的class文件javap反编译可以看出,定义的枚举类继承自java.lang.Enum抽象类且通过public static final定 ...

  10. 用私有构造器或枚举类型强化Singleton

    Singleton指只有一个实例的类,只能被创建一次. 在Java1.5之前实现Singleton有两种方式,都是将构造器设为private并导出公有的静态成员实例. 第一种方式将公有的静态成员实例设 ...

随机推荐

  1. 禁止Asp.Net WebService 的Test页面功能

    只需要Web.Config里面添加: <system.web> <webServices> <protocols> <remove name="Ht ...

  2. KBEngine warring项目源码阅读(三) 实体文件与Account处理

    上一篇开始,我们就提到了一个概念,并且进行了初步的运用,这个概念就是实体. KBE中的实体是一个很重要的概念,可以说,有了实体就有了一切. 我们首先接着上一章的内容,来看Account.def对应的实 ...

  3. 人工智能之必须会的Python基础

    Python 号称是最接近人工智能的语言,因为它的动态便捷性和灵活的三方扩展,成就了它在人工智能领域的丰碑 走进Python,靠近人工智能 一.编程语言Python的基础 之 "浅入浅出&q ...

  4. Vue.js(2.x)之条件渲染

    1.v-if:这里的官网文档看完后赶脚v-if就是用来判断元素是显示还是隐藏. 2.template这个包装元素感觉挺好用,以后把需要某些特定操作才出现的元素存放进去挺好. 3.前面看的网友写的还可以 ...

  5. ${fn:} 函数

    调用这样一个头文件<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions " ...

  6. 报错:无法打开"cocos-ext.h" /添加第三方库

    参考原文:http://lin-jianlong.diandian.com/post/2012-11-05/40042951271 1.项目属性->配置属性->C/C++->常规-& ...

  7. Python3基础12(collections、struct、itertools、chardet等的使用)

    import struct import base64import itertoolsimport chardet from collections import namedtuple,default ...

  8. PHP数组排序方法总结

    随着PHP的快速发展,用它的人越来越多,在PHP数组学习摘录部分了解到最基本的PHP数组的建立和数组元素的显示.需要深入学习下PHP数组的相关操作.首先接触的就是PHP数组排序.降序的排序问题. so ...

  9. Java中ArrayList的对象引用问题

    前言事件起因是由于同事使用ArrayList的带参构造方法进行ArrayList对象复制,修改新的ArrayList对象中的元素(对象)的成员变量时也会修改原ArrayList中的元素(对象)的成员变 ...

  10. 漫谈Ajax在.Net中的使用

    引用地址:http://birdshover.cnblogs.com/archive/2006/07/03/441439.html AJAX出来的时间也不短了.虽然它在某些方面很受争议,但是瑕不掩瑜. ...