原文地址: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. ElasticSearch java API-使用More like this实现基于内容的推荐

    ElasticSearch java API-使用More like this实现基于内容的推荐 基于内容的推荐通常是给定一篇文档信息,然后给用户推荐与该文档相识的文档.Lucene的api中有实现查 ...

  2. 浅析System.Console.WriteLine()

    浅析System.Console.WriteLine() Writeline()函数的功能向 StreamWriter 类写入指定字符串和一行字符,共有19个重载,其与Write()函数的主要区别在于 ...

  3. 《Head First 设计模式》之单件模式

    单件模式(Singleton) ——确保一个类只有一个实例,并提供全局访问点. 有一些对象其实我们只需要一个,比如线程池.缓存.对话框.处理偏好设置和注册表的对象.日志对象.如果制造出多个实例,就会导 ...

  4. 《译》准备做一些 AR/增强现实的 翻译

    中文这方面资料实在少之又少. 准备做一些这方面翻译,关注于Vuforia, Unity3d, Hololens等方面. 如有问题.建议,随时联系.Fell free ton contact me.

  5. 判断一个点是否在多边形区域内--C算法

    /*函数的输入:(1)当前点的坐标p(2)区域顶点数组pt[]:(3)顶点数nCount 输出: 在区域内返回TRUE,否则返回FALSE.  Point类型是一个结构: struct Point { ...

  6. HDU 3351 Seinfeld 宋飞正传(水)

    题意: 给出一个串,串内只有大括号,问经过几次改变可使全部括号合法?改变指的是可以将某一方向的括号变成另一方向. 思路: 利用栈的特点,若出现成对的合法括号,直接删掉,留下那些不合法的成为一串.既然不 ...

  7. linux 命令——39 grep (转)

    Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来.grep全称是Global Regular Expression Print,表示全局正则表达 ...

  8. iptables (2) 基本配置

    iptables 基本命令使用举例 一.链的基本操作 1.清除所有的规则.1)清除预设表filter中所有规则链中的规则.# iptables -F -F, --flush [chain] Flush ...

  9. 【洛谷5358】[SDOI2019] 快速查询(模拟)

    点此看题面 大致题意: 有单点赋值.全局加法.全局乘法.全局赋值.单点求值.全局求和\(6\)种操作.现在给出操作序列,以及\(t\)对正整数\(a_i,b_i\).让你处理\(t*q\)次操作,每次 ...

  10. LigerUI的下拉框行和树的设置(表单生成)

    http://blog.csdn.net/dxnn520/article/details/8194767 // ---------------------- // [下拉树设置 -- 单选] {dis ...