设计模式之单例模式

使用场景

  • 只拥有一个全局对象,或者某种类型对象只应该存在一个
  • 避免产生多个对象耗费过多的资源

关键点

  • 构造函数不对外开放,一般是private
  • 通过一个静态方法或者枚举返回单例类对象
  • 确保单例类的对象有且只有一个,尤其在多线程环境下
  • 确保单例类对象在反序列化时不会重新构建对象

UML

说明:
  1. Singleton只有一个实例化对象,内部自己实现
  2. Client通过Singleton的getInstance方法获取实例对象

单例模型实例

公司里面的CEO为例,一个公司可以有几个VP,无数的员工,但是CEO只有一个

  • 基类,普通员工

    public class Staff {
    
        public void work(){
            //干活
        }
    
    }
    
  • 副总裁,继承自员工

    public class VP extends Staff {
    
        @Override
        public void work() {
            //管理下面的经理
        }
    }
    
  • 单例方式的恶汉模式,在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快

    public class CEO extends Staff {
    
        private static final CEO mCeo = new CEO();
    
        //构造函数私有
        public CEO() {
        }
    
        //共有的静态函数,对外暴露湖区单例对象的接口
        public static CEO getCeo(){
            return mCeo;
        }
    
        @Override
        public void work() {
            //管理VP
        }
    }
    
  • 公司类

    public class Company {
    
        private List<Staff> allStaffs = new ArrayList<Staff>();
    
        public void addStaff(Staff per) {
            allStaffs.add(per);
        }
    
        public void showAllStaffs() {
            for (Staff per : allStaffs) {
                System.out.println("Obj : " + per.toString());
            }
        }
    
    }
    
  • 测试函数

    public class Main {
    
        public static void main(String[] args) {
    
            Company company = new Company();
    
            //CEO对象只能通过getCeo函数获取
            CEO ceo1 = CEO.getCeo();
            CEO ceo2 = CEO.getCeo();
            company.addStaff(ceo1);
            company.addStaff(ceo2);
    
            //通过new创建VP对象
            VP vp1 = new VP();
            VP vp2 = new VP();
            company.addStaff(vp1);
            company.addStaff(vp2);
    
            //通过new创建Staff对象
            Staff staff1 = new Staff();
            Staff staff2 = new Staff();
            company.addStaff(staff1);
            company.addStaff(staff2);
    
            //通过打印,判断对象是否统一
            company.showAllStaffs();
        }
    }
    
  • 懒汉模式实现单例,在类加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢

public class CEO1 extends Staff {  

    private static CEO1 ceo1 = null;

    public CEO1() {
    }

    public static synchronized CEO1 getCeo1() {
        if (ceo1 == null) {
            ceo1 = new CEO1();
        }
        return ceo1;
    }

    @Override
    public void work() {
        //管理VP
    }
}

在一定程度上节约了资源,但在第一次加载时需要及时进行实例化,反应稍慢,最大的问题是在每次调用getCeo时候都进行同步,造成不必要的开支

  • Double CheckLock(DCL)–双重检查锁定,实现单例

    public class CEO2 extends Staff {
    
        private volatile static CEO2 mCeo2 = null;
    
        public CEO2() {}
    
        public static CEO2 getmCeo2(){
            if (mCeo2 == null){
                synchronized (CEO2.class){
                    if (mCeo2 == null){
                        mCeo2 = new CEO2();
                    }
                }
            }
            return mCeo2;
        }
    }
    

DCL优点是资源利用率高,缺点是由于Java编译器允许处理器乱序执行,以及JDK1.5之前的Java内存模型回写顺序规定,会造成DCL小几率失效问题

  • 静态内部类实现单例模式

    public class CEO3 extends Staff{
        public CEO3() {}
    
        public static CEO3 getCeo3(){
            return CEO3Holder.mCeo3;
        }
    
        /**
         * 静态内部类
         */
        private static class CEO3Holder{
            private static final CEO3 mCeo3 = new CEO3();
        }
    }
    

静态内部类不仅能够保证线程安全,也能够保证对象的唯一性

  • 枚举实例的创建是线程安全的,并且在任何情况下都是一个单例,即使反序列化(不用重写readResolve方法)也不会重新生成实例

    public enum CEO4Enum {
    
        CEO4;
    
        public void doSomething(){
            System.out.println("do sth.");
        }
    
    }
    
  • 使用容器实现单例模式

    public class CEOManager {
    
        private static Map<String, Object> mCeo = new HashMap<>();
    
        public static void registerService(String key, Object ceo){
            if (!mCeo.containsKey(key)){
                mCeo.put(key,ceo);
            }
        }
    
        public static Object getService(String key){
            return mCeo.get(key);
        }
    
    }
    

在程序的初始化中,将多种单例类型注入到统一的管理类中,通过key获取对象对应类型的对象

坚持原创技术分享,您的支持将鼓励我继续创作!