• Java读书笔记(8)-单例模式


          今天在阅读《Effective Java 2》第3条时,获知一种使用枚举enum实现单例模式的新方法,然而书上并没有就此展开深入说明,于是上网查阅了一些资料,现将收集的资料整理于下:

          转自:枚举类型的单例模式:http://callmegod.iteye.com/blog/1474441

    Singleton 模式是在编程实践中应用最广泛的几种设计模式之一。以前知道的,实现单例的方法有两种(下面的A、B)。刚刚在读《Effective Java的时候》学到一种新的更好的方法(E):单元素的枚举类型。同时通过网上资料也知道了其他两种方法(C、D)。最后一种在Java中从1.5版本 开始支持,其他语言在验证后说明。

    A.饿汉式(类加载的时候就创建实例)。
    代码如下:

    public class MaYun {
    public static final Mayun instance = new Mayun(); //静态的final的MaYun
    private MaYun() {
    //MaYun诞生要做的事情
    }
    public void splitAlipay() {
    System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
    }
    }
    Call:MaYun.instance.splitAlipay();

    Feature:可以通过反射机制攻击;线程安全[多个类加载器除外]。

    A+.饿汉变种[推荐]

    public class MaYun {
    private static Mayun instance = new Mayun();
    private static getInstance() {
    return instance;
    }
    private MaYun() {
    //MaYun诞生要做的事情
    }
    public void splitAlipay() {
    System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
    }
    }

    A++.饿汉变种(类初始化的时候实例化instance):

    public class MaYun {
    private MaYun instance = null;
    static {
    instance = new MaYun();
    }
    private MaYun() {
    //MaYun诞生要做的事情
    }
    public static MaYun getInstance() {
    return this.instance;
    }
    public void splitAlipay() {
    System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
    }
    }

    B.懒汉式。
    代码如下:

    public class MaYun {
    private static MaYun instance = null;
    private MaYun() {
    //MaYun诞生要做的事情
    }
    public static MaYun getInstance() {
    if (instance == null) {
    instance = new MaYun();
    }
    return instance;
    }
    public void splitAlipay() {
    System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
    }
    }
    Call:MaYun.getInstance().splitAlipay();

    Feature:延时加载;线程不安全,多线程下不能正常工作;需要额外的工作(Serializable、transient、readResolve())来实现序列化。

    B+.懒汉式变种。

    public class MaYun {
    private static MaYun instance = null;
    private MaYun() {
    //MaYun诞生要做的事情
    }
    public static synchronized MaYun getInstance() {
    if (instance == null) {
    instance = new MaYun();
    }
    return instance;
    }
    public void splitAlipay() {
    System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
    }
    }

    Feature:线程安全;效率比较低,因为需要线程同步的时候比较少。

    C.静态内部类[推荐]。
    代码如下:

    public class MaYun {
    private static class SingletonHolder {
    private static final instance = new MaYun();
    }
    public static final getInstance() {
    return SingletonHolder.instance;
    }
    private MaYun() {
    //MaYun诞生要做的事情
    }
    public void splitAlipay() {
    System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
    }
    Call:MaYun.getInstance().splitAlipay();

    Feature:线程安全;延迟加载。

    D.双重校验锁[不推荐]。
    代码如下:

    public class MaYun {
    private volatile static MaYun instance;
    private MaYun (){}
    public static MaYun getInstance() {
    if (instance == null) {
    synchronized (MaYun.class) {
    if (instance == null) {
    instance = new MaYun();
    }
    }
    }
    return instance;
    }
    }

    Feature:jdk1.5之后才能正常达到单例效果。

    E.编写一个包含单个元素的枚举类型[极推荐]。
    代码如下:

    public enum MaYun {
    himself; //定义一个枚举的元素,就代表MaYun的一个实例
    private String anotherField;
    MaYun() {
    //MaYun诞生要做的事情
    //这个方法也可以去掉。将构造时候需要做的事情放在instance赋值的时候:
    /** himself = MaYun() {
    * //MaYun诞生要做的事情
    * }
    **/
    }
    public void splitAlipay() {
    System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
    }
    }
    Call:MaYun.himself.splitAlipay();

    Feature:从Java1.5开始支持;无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。

    总之,五类:懒汉,饿汉,双重校验锁,静态内部类,枚举。
    饿汉:因为加载类的时候就创建实例,所以线程安全(多个ClassLoader存在时例外)。缺点是不能延时加载。
    懒汉:需要加锁才能实现多线程同步,但是效率会降低。优点是延时加载。
    双重校验锁:麻烦,在当前Java内存模型中不一定都管用,某些平台和编译器甚至是错误的,因为instance = new MaYun()这种代码在不同编译器上的行为和实现方式不可预知。
    静态内部类:延迟加载,减少内存开销。因为用到的时候才加载,避免了静态field在单例类加载时即进入到堆内存的permanent代而永远得不到回收的缺点(大多数垃圾回收算法是这样)。
    枚举:很好,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。但是失去了类的一些特性,没有延迟加载,用的人也太少了~~

        比较了这么多中单例实现模式,发现使用枚举enum除了没有延迟加载这一点好处之外,无论是编程方便程度和安全性都是最后的,以后尝试在自己开发中使用这种新的方法。

  • 相关阅读:
    JAVA消息对话框
    stringbuffer capacity()的疑问
    JAVA确认对话框
    c/c++实现获取NOD32升级账号密码
    复制构造函数(拷贝构造函数)
    使用VC将sqlite3.def转化为sqlite3.lib
    Windows下安装OpenSSL
    java中io与nio的使用
    使用 XStream 把 Java 对象序列化为 XML
    使用 XStream 把 Java 对象序列化为 XML
  • 原文地址:https://www.cnblogs.com/hust_wsh/p/5127773.html
Copyright © 2020-2023  润新知