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;然后,静态初始化实例对象并添加到容器中;最后根据条件返回容器中的实例对象。

 

posted @ 2018-03-16 10:44  shanquan  阅读(139)  评论(0编辑  收藏  举报