设计模式读书笔记一 单例模式

作者: grr1314 | 来源:发表于2017-02-09 02:08 被阅读0次

    1、单例模式介绍
    单例模式是应用最广泛的模式之一,也是可以说是初级工程师唯一会用的设计模式。在应用这一模式的时候,单例对象的类必须保证只有一个实例存在。许多时候整个系统只要一个全局对象,这样有利于我们协调系统整体行为。如在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader中又包含有线程池、缓存系统、网络请求等,很消耗资源,因此,没有理由让它有多个实例。这种情况就是单例模式的使用场景。
    2、单例模式的定义
    确保类只有一个实例,而且自行实例化并向整个系统提供这个实例
    3、使用场景
    确保类只有一个对象的场景
    4、UML类图

    5、单例模式的多种实现
    (1)饿汉模式
    这是单例模式的最简单的一种写法,在具体介绍之前我们写一个简单的例子,这样也方便后面介绍单例模式的其他写发。
    例如:一个公司只有一个CEO、几个VP、无数个员工,例子很简单。下面我们一点点的实现

    普通员工类:Staff.java

    package com.example.singleton;
    
    public class Staff {
           public void work()
          {
                 //干活
          }
    
    }
    
    

    副总裁类:VP.java

    package com.example.singleton;
    
    public class VP extends Staff {
           @Override
           public void work() {
                 // TODO Auto-generated method stub
                 super .work();
                 //管理下面的经理
          }
    
    }
    
    

    CEO类:CEO.java

    package com.example.singleton;
    
    public class CEO extends Staff {
    
           private static final CEO mCeo= new CEO();
    
           // 构造函数私有化(构造方法的私有化是单例模式的核心)
           private CEO() {
    
          }
    
           /**
           * 方法一
           * 饿汉单例模式
           *
           * @author HP
           *
           */
           public static CEO getCeo () {
                 return mCeo ;
          }
           @Override
           public void work() {
                 // TODO Auto-generated method stub
                 super .work();
                 // 管理 vp
          }
    
    }
    
    

    公司类:Company.java

    package com.example.singleton;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 公司类
     * @author HP
     *
     */
    public class Company {
    
        private List<Staff> allStaffs=new ArrayList<>();
    
        public void addStaff(Staff per)
        {
            allStaffs.add(per);
        }
        public void showAllStaffs()
        {
            for(Staff per:allStaffs)
            {
                System.out.println("Obj:"+per.toString());
            }
        }
    }
    
    

    最后是Main.java

    package com.example.singleton;
    
    public class Main {
    
           /**
           * @param args
           */
           public static void main(String[] args) {
                 // TODO Auto-generated method stub
                Company cp = new Company();
                 //方法一 :饿汉模式
                Staff ceo1 = CEO. getCeo();
                Staff ceo2 = CEO. getCeo();
    
                cp.addStaff(ceo1);
                cp.addStaff(ceo2);
    
                cp.showAllStaffs();
                System. out .println("--------------------------------------" );
                 //测试发现有两条数据是一样的,说明单例类只能提供一个对象
    
                Staff vp1= new VP();
                Staff vp2= new VP();
    
                Staff staff1= new Staff();
                Staff staff2= new Staff();
                Staff staff3= new Staff();
    
    
                cp.addStaff(vp1);
                cp.addStaff(vp2);
                cp.addStaff(staff1);
                cp.addStaff(staff2);
                cp.addStaff(staff3);
    
                cp.showAllStaffs();
    
          }
    
    }
    
    

    输出结果:

    Obj:com.example.singleton.CEO@1cacd5d4
    Obj:com.example.singleton.CEO@1cacd5d4
    --------------------------------------
    Obj:com.example.singleton.CEO@1cacd5d4
    Obj:com.example.singleton.CEO@1cacd5d4
    Obj:com.example.singleton.VP@170a6001
    Obj:com.example.singleton.VP@2a24ed78
    Obj:com.example.singleton.Staff@5e6276e5
    Obj:com.example.singleton.Staff@126be4cc
    Obj:com.example.singleton.Staff@697a1686
    
    

    以上就是这个例子的全部内容,其中CEO.java就是一个单例类,因为之前说了一个公司就只有一个CEO。从代码中可以看到,CEO类不能通过new关键字来构造对象,因为构造方法已经被私有化。只能通过CEO类对外开放的getCeo方法来获取对象,而这个对象是在申明的时候就已经被初始化,这就保证的对象的唯一性。以上单例模式的写法就是所谓的饿汉模式。
    (2)懒汉模式
    懒汉模式与饿汉模式的区别在于,懒汉模式的对象是在调用了getInstance方法的时候初始化的。懒汉模式的实现方式如下,将CEO类修改。

    package com.example.singleton;
    
    public class CEO extends Staff {
    
           // private static final CEO mCeo=new CEO();
           private static CEO mCeo;
    
           // 构造函数私有化(构造方法的私有化是单例模式的核心)
           private CEO() {
    
          }
    
           // /**
           // * 方法一
           // * 饿汉单例模式
           // *
           // * @author HP
           // *
           // */
           // public static CEO getCeo() {
           // return mCeo;
           // }
           /**
           * 方法二
           * 懒汉模式
           * @return
           */
           public static synchronized CEO getInstance() {
                 if (mCeo != null) {
                       mCeo = new CEO();
                }
                 return mCeo ;
          }
    
           @Override
           public void work() {
                 // TODO Auto-generated method stub
                 super .work();
                 // 管理 vp
          }
    
    }
    
    

    对应的修改Main.java

    package com.example.singleton;
    
    public class Main {
    
           /**
           * @param args
           */
           public static void main(String[] args) {
                 // TODO Auto-generated method stub
                Company cp = new Company();
    //          //方法一 :饿汉模式
    //          Staff ceo1 = CEO.getCeo();
    //          Staff ceo2 = CEO.getCeo();
    
                 //方法二:懒汉模式
                Staff ceo1 = CEO. getInstance();
                Staff ceo2 = CEO. getInstance();
    
    
    
                cp.addStaff(ceo1);
                cp.addStaff(ceo2);
    
                cp.showAllStaffs();
                System. out .println("--------------------------------------" );
                 //测试发现有两条数据是一样的,说明单例类只能提供一个对象
    
                Staff vp1= new VP();
                Staff vp2= new VP();
    
                Staff staff1= new Staff();
                Staff staff2= new Staff();
                Staff staff3= new Staff();
    
    
                cp.addStaff(vp1);
                cp.addStaff(vp2);
                cp.addStaff(staff1);
                cp.addStaff(staff2);
                cp.addStaff(staff3);
    
                cp.showAllStaffs();
    
          }
    
    }
    
    

    懒汉模式中的getInstance方法中添加了synchronized关键字,也就是说getInstance是一个同步方法,这就是在多线程情况下保证对象唯一性的手段。但是有一个问题,那就是即使mCeo已经被初始化,每次调用getInstance方法的时候还是会同步,这样就造成了不必要的资源浪费。这也是懒汉模式的最大问题所在。

    △ 懒汉模式的优缺点
    懒汉模式的优点是单例只有使用的时候才会被初始化,在一定程度上节约了资源;缺点是第一次加载的时候反应稍慢,最大了问题是每次调用getInstance方法的时候都要同步,会造成不必要的开销。

    (3)DCL实现方式
    这种方式既可以实现在需要的时候在初始化实例,又保证线程安全,并且在调用getInstance方法的时候不同不同,其实现如下:

    package com.example.singleton;
    
    public class CEO extends Staff {
    
    //     private static final CEO mCeo=new CEO();
           private static CEO mCeo;
    
           // 构造函数私有化(构造方法的私有化是单例模式的核心)
           private CEO() {
    
          }
    
    //     /**
    //     * 方法一
    //     * 饿汉单例模式
    //     *
    //     * @author HP
    //     *
    //     */
    //     public static CEO getCeo() {
    //     return mCeo;
    //     }
    //    /**
    //     * 方法二
    //     * 懒汉模式
    //     * @return
    //     */
    //    public static synchronized CEO getInstance() {
    //          if (mCeo == null) {
    //                mCeo = new CEO();
    //          }
    //          return mCeo;
    //    }
    
           /**
           * 方法三
           * DCL
           */
           public static CEO getInstance() {
                 if (mCeo == null) {
                       synchronized (CEO.class ) {
                             if (mCeo == null) {
                                   mCeo =new CEO();
                            }
                      }
                }
                 return mCeo ;
          }
    
           @Override
           public void work() {
                 // TODO Auto-generated method stub
                 super .work();
                 // 管理 vp
          }
    
    }
    
    

    这不是一种被推荐使用的单例模式写法
    (4)静态内部类实现单例模式
    其实现如下:

    package com.example.singleton;
    
    public class CEO extends Staff {
    
           // private static final CEO mCeo=new CEO();
           // private static CEO mCeo;
    
           // 构造函数私有化(构造方法的私有化是单例模式的核心)
           private CEO() {
    
          }
    
           // /**
           // * 方法一
           // * 饿汉单例模式
           // *
           // * @author HP
           // *
           // */
           // public static CEO getCeo() {
           // return mCeo;
           // }
           // /**
           // * 方法二
           // * 懒汉模式
           // * @return
           // */
           // public static synchronized CEO getInstance() {
           // if (mCeo == null) {
           // mCeo = new CEO();
           // }
           // return mCeo;
           // }
    
           // /**
           // * 方法三
           // * DCL
           // */
           // public static CEO getInstance() {
           // if (mCeo==null) {
           // synchronized (CEO.class) {
           // if (mCeo==null) {
           // mCeo=new CEO();
           // }
           // }
           // }
           // return mCeo;
           // }
    
           /**
           * 方法四 静态内部类单例模式(推荐使用的单例模式)
           *
           * @return
           */
           public static CEO getInstance() {
                 return SinletonCEO. mCeo;
          }
    
           private static class SinletonCEO {
                 private static final CEO mCeo = new CEO();
          }
    
           @Override
           public void work() {
                 // TODO Auto-generated method stub
                 super .work();
                 // 管理 vp
          }
    
    }
    
    

    当第一次加载CEO类的时候并不会初始化mCeo,只有在第一次调用CEO的getInstance方法的时候mCeo才会被初始化。因此,第一次调用getInstance方法会导致虚拟机加载SinletonCEO类,这种方式不仅能够确保线程安全,同时也能够保证单例对象的唯一性,同时也延迟了单例的实例化。所以这是一种推荐使用的单例实现方式。

    (5)其他单例实现方式
    其中还有两种单例的实现方式,他们是枚举单例和使用容器实现单例模式。这里就不再具体介绍了,想了解的同学可以翻阅《Android源码设计模式解析与实战》艺术。

    6 总结
    不管是哪种方式实现的单例模式,它们的核心就是构造函数的私有化,并且通过静态方法来获取一个唯一的实例,在这个过程中我们必须要保证线程安全、防止反序列化导致重生成实例等问题!

    相关文章

      网友评论

        本文标题:设计模式读书笔记一 单例模式

        本文链接:https://www.haomeiwen.com/subject/cqxiittx.html