• 06-单例模式


    6.1模式定义

      单例设计模式(Singleton Pattern),顾名思义,是指确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。需要注意的是,在系统中只有真正有“单一实例”的需求时才可使用。

      使用单例模式时,有三个要点:

      1)某个类只能有一个实例;

      2)该类必须自行创建这个实例;

      3)该类必须自行向整个系统提供这个实例。

      满足了上面三个特点,才能保证正确使用单例设计模式。

    6.2模式分析

      我们知道,一个正常的是可以通过new操作符来创建对象的。为什么可以任意创建实例对象呢?因为在类中,即使不显示定义构造方法,也存在一个默认的构造方法,供外部生成对象使用。这样在外部就可以随着创建实例对象。单例设计模式只允许存在一个实例对象,也就是说,在外部是不能随意生成对象实例的,那么如何设计单例模式呢?答案就在于我们需要将类的构造方法显式的声明为private方式,然后定义一个获得实例对象的方法获得单例对象。来看如下单例设计模式的结构图:

      在单例设计模式中,在类中存在一个全局共享的对象实例,并且类的构造方法是private声明方式的,外界无法访问,也就是不能使用new操作符来创建对象了。在整个系统中我们获得的Singleton实例对象始终都是一个。

    6.3模式实现

    6.3.1实现一:使用同步线程安全创建单例对象

      在Singleton类中需要做三点处理:

      1)含有一个静态私有的共享实例对象;

      2)构造方法显式声明为私有方式,即外部不能创建实例对象;

      3)含有一个公有获取单例对象的方法,即该类自行向整个系统提供这个实例。

    package com.demo.singleton;
    
    /**
     * Created by lsq on 2018/3/15.
     * 单例设计模式
     */
    public class Singleton {
    
        //类共享实例对象
        private static Singleton singleton = null;
    
        //私有构造方法
        private Singleton(){
            System.out.println("-- this is Singleton constructor!!!");
        }
    
        //获得单例对象的方法
        public synchronized static Singleton getInstance(){
            //判断共享对象是否为null,如果为null,则new一个新的对象
            if (singleton==null){
                singleton = new Singleton();
            }
            return singleton;
        }
    
    }

    创建客户端程序,首先来看看是否能使用new操作符创建Singleton对象,如下图所示:

    上面的尝试会报编译错误,所以单例模式是不能使用new操作符创建实例对象的。我们要通过类中获得单例对象的方法来获得单例对象,如下所示:

    import com.demo.singleton.Singleton;
    
    /**
     * Created by lsq on 2018/3/15.
     *
     */
    public class MainApp {
    
        public static void main(String[] args) {
            //通过提供的外部获得单例对象的接口获得Singleton对象实例
            Singleton singleton = Singleton.getInstance();
            //获得另外一个对象实例
            Singleton singleton2 = Singleton.getInstance();
            //比较两个对象是不是同一个对象
            if (singleton==singleton2){
                System.out.println("--这是同一个对象!");
            }else {
                System.out.println("--这是不同的对象!");
            }
        }
    
    }

    运行结果如下所示:

      注意:这里在获得实例对象的方法前添加了synchronized关键字,这样程序就变成了线程安全的。

    6.3.2实现二:创建一个类全局对象实例作为单例对象

      如果不想使用synchronized关键字,还有另外一种方法供你选择。

      这里与上面一种方法不同的是,首先将全局共享对象实例化,在获得单例对象的方法中,直接返回全局共享对象,而不使用synchronized关键字。

    package com.demo.singleton;
    
    /**
     * Created by lsq on 2018/3/15.
     * 单例设计模式
     */
    public class Singleton2 {
    
        //类共享实例对象实例化
        private static Singleton2 singleton = new Singleton2();
    
        //私有构造方法
        private Singleton2(){
            System.out.println("-- this is Singleton2 constructor!!!");
        }
    
        //获得单例方法
        public static Singleton2 getInstance(){
            //直接返回共享对象
            return singleton;
        }
    
    }

      两种实现方式的比较:

      以上两种方式都能产生单例对象实例,两者有各自的优缺点:

      方式一:优点是,不会产生内存浪费,因为共享实例对象开始没有被初始化,而是在获得共享对象的方法中动态生成实例的;缺点,也在获得共享对象的方法上,使用synchronized线程同步关键字,执行效率会有所降低。

      方式二:缺点,会产生内存浪费,因为在加载Singleton2类时就已经初始化共享对象实例;优点,由于没有synchronized线程同步,执行效率高。

    6.3.3提高:多例模式实现

    1.多例模式分析方法

      在前面,我们已经掌握了单例设计模式的设计方法,其实,在我们的程序设计中,还存在另外一种模式——多例模式。所谓多例模式就是存在多个对象实例,供外部应用调用,比如数据库连接池。下面给出一种比较常用的设计思路,供大家参考。

      在多例模式中,使用ArrayList存储多个实例对象,然后使用第二种方式,首先在类中添加N个对象实例,在获得实例对象的方法中产生一个0~N之间的随机数,然后返回ArrayList中指定的随机数位置的对象实例。

      静态类图如下所示:

      

    2.多例模式实现——Multipleton

    package com.demo.singleton;
    
    import java.util.ArrayList;
    
    /**
     * Created by lsq on 2018/3/15.
     * 多例模式
     */
    public class Multipleton {
    
        //多例数量
        private static final int N=10;
    
        //存放N个实例对象的容器
        private static ArrayList<Multipleton> list = new ArrayList<>(N);
    
        //每个对象的序号、标识
        private int no;
    
        //私有构造方法,防止外界应用程序实例化
        private Multipleton(int no){
            this.no = no;
            System.out.println("-- Create Multipleton Object["+no+"]!");
        }
    
        //实例化N个对象实例
        static {
            //添加Multipleton对象实例
            for (int i=0; i<N;i++){
                list.add(new Multipleton(i));
            }
        }
    
        /**
         * 随机获得实例对象
         */
        public static Multipleton getRandomInstance(){
            //获得随机数字
            int num = (int)(Math.random() * N);
            //获得list中的对象实例
            return list.get(num);
        }
    
        public int getNo() {
            return no;
        }
    
        public void setNo(int no) {
            this.no = no;
        }
    }

      注:使用static关键字向list数组中放入N个Multipleton对象实例,使创建对象实例过程静态化,在类加载的时候执行一次,而创建对象实例的时候则不会执行。

      私有构造方法中,增加一个传入的参数,用来标识对象实例。

      getRandomInstance方法中,首先产生一个0~N的随机数,然后返回list数组指定位置的实例对象。

    3.创建MultipletonClient客户端类

    package com.demo.singleton;
    
    /**
     * Created by lsq on 2018/3/15.
     * 客户端应用程序
     */
    public class MultipletonClient {
    
        public static void main(String[] args) {
            //获得Multipleton对象实例
            Multipleton multipleton = Multipleton.getRandomInstance();
            System.out.println("multipleton:"+multipleton.getNo());
    
            //再次获得Multipleton对象实例
            Multipleton multipleton2 = Multipleton.getRandomInstance();
            System.out.println("multipleton2:"+multipleton2.getNo());
    
            //比较两个对象是否是同一个对象实例
            if (multipleton==multipleton2){
                System.out.println("--这是同一个对象!");
            }else {
                System.out.println("--这是不同的对象!");
            }
        }
    
    }

    运行结果如下:

      从上面的执行结果,可以得出两个结论:

      1)实例化N个实例对象的过程只执行了一次。

      2)随机获得了Multipleton对象。

    6.4使用场合

      1)当在系统中某个特定的类对象实例只需要一个的时候,可以使用单例设计模式。需要注意的是,只有真正有“单一实例”的需求时才可使用。

      2)多例模式的实现原理:首先,在类中设置一个存储多个实例的容器,如ArrayList或HashMap;然后,静态初始化实例对象并添加到容器中;最后根据条件返回容器中的实例对象。

  • 相关阅读:
    digitalpersona 开发
    Task 暂停与继续
    IQueryable 和 IEnumerable(二)
    SpringBoot Redis 订阅发布
    @Formula
    Aop 简单实例
    幂等 zuul的Filter实现
    C# async await 举个栗子
    Integer 类和 int 的区别
    TCP和UDP的区别以及各自应用
  • 原文地址:https://www.cnblogs.com/danielleee/p/8579308.html
Copyright © 2020-2023  润新知