原文地址: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. java之struts框架入门教程

    本教程主要讲述struts的简单入门操作 使用的是myeclipse工具 1.创建web项目 2.复制struts必要的jar包到 WebRoot/WEB-INF/lib 下 jar包列表如下: as ...

  2. Ngnix服务器详解(Windows版本)(非原创)

    文章大纲 一.Ngnix简介二.Ngnix安装三.Ngnix之静态资源访问四.Ngnix正向代理与反向代理五.Ngnix之虚拟主机配置六.Ngnix之负载均衡七.Ngnix之访问控制八.Ngnix日志 ...

  3. springboot+Jsp部署linux

    这个springboot部署到linux,我之前一直都是在linux上使用tomcat部署,但是这样部署容易出现EL表达式无法使用导致项目报错:后来发现了一种更简单的方法,就是将项目打成war包,注册 ...

  4. https微信分享看不到图片的坑

    最近在做一个活动项目的时候一开始走的http,发现网络被劫持的特别严重,没办法,只能改走https,但是修改为https后发现在使用微信js-sdk分享的时候看不到缩略图,直接通过地址打开是可以找开图 ...

  5. IIS发布网站出错解决方案

    1.第一类错误(Web服务器被配置为不列出此目录的内容) 问题所在没有为请求的URL设置默认文档,在IIS“默认文档”添加一个你要访问的默认文档名字,如:Default.aspx. 2.第二类错误(请 ...

  6. koa2实现文件上传服务

    使用方法 方法一: 使用中间介 koa-body 方法二: 自己写个借口去接收数据流并保存 方法三: 使用 koa-body 接受文件,自己写个接口做文件保存或处理等操作 这里简单记录方法三 app. ...

  7. 获得session中的用户信息

    由于每个系统都有往session中放入用户信息以及把用户信息取出来的模块,而且在session中取出用户信息的地方非常之多,所以有必要把session中对用户的操作封装成为一个工具类,以便在以后的使用 ...

  8. 3203 数组做函数参数----排序函数--C语言版

    3203: 数组做函数参数----排序函数--C语言版 时间限制: 1 Sec  内存限制: 128 MB提交: 253  解决: 151[提交][状态][讨论版][命题人:smallgyy] 题目描 ...

  9. 欠采样(undersampling)和过采样(oversampling)会对模型带来怎样的影响

    项目中出现了二分类数据不平横问题,研究总结下对于类别不平横问题的处理经验: 为什么类别不平横会影响模型的输出? 许多模型的输出类别是基于阈值的,例如逻辑回归中小于0.5的为反例,大于则为正例.在数据不 ...

  10. python_68_迭代器

    ''' 我们已经知道,可以直接作用于for循环的数据类型有以下几种: 一类是集合数据类型,如list.tuple.dict.set.str等: 一类是generator,包括生成器和带yield的ge ...