设计模式-单例(Singleton)

2018-1-12 by Atlas


  • 设计思想

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

  • 应用场景

(1)如果有1个以上的对象实例时,由于对象实例彼此之间的影响,可能会发展成出乎意料的BUG。
(2)需要控制类的对象实例的数量,以降低资源的使用时。

  • UML 类图

Singleton UML

  • UML中加“-”表示私有的(private);
  • UML中加“+”表示公有的(public);
  • UML中加“_”表示静态的(static)。

Singleton模式基本构成:

  • 静态的私有的类成员变量singleton,私有的表示只有成员所生存的对象可以访问,静态的表示类加载准备阶段分配初始值、解析阶段字符引用转化为直接引用;
  • 私有的类构造方法Singleton,私有的构造方法表示禁止非Singleton类调用实例构造器创建对象实例;
  • 静态的公有的类方法getInstance,静态的类方法表示类加载解析阶段就从字符引用转化为直接引用,即内存中方法的实际地址,可以通过类.方法直接调用。
  • 标准示例
public class Singleton {
    private static Singleton singleton = new Singleton();
    private Singleton() {
        System.out.println("已产生对象实例。");
    }
    public static Singleton getInstance() {
        return singleton;
    }
}
public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    System.out.println(s1 == s2);
}
  • 拓展示例

延迟对象实例化

  • 优点:
    类加载时不进行目标对象的实例化操作,提高了类加载的速度,实际访问需要时才进行目标对象的实例化。
  • 缺点:
    相比类加载时就进行目标对象实例化,延迟实例化可能导致多线程并发时产生线程安全问题,需要同步访问入口方法以达到线程安全的目的,理论上降低了多线程并发访问的效率。
public class Singleton {
    private static Singleton singleton;
    private Singleton() {
            System.out.println("已产生对象实例。");
    }
    public static synchronized Singleton getInstance() {
            if(singleton == null){
                singleton = new Singleton();
            }
            return singleton;
    }
}
public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    System.out.println(s1 == s2);
}

那么问题来了,有没有一种两全其美的方式呢,答案是有的。
静态内部类实例化

  • 优点:
    (1)外部类加载时无须进行目标对象的实例化操作,提高了类加载的速度,实际访问需要时加载内部类并进行目标对象的实例化。
    (2)静态内部类只会进行一次内部类的类变量的初始化,不会产生线程安全问题,无须同步,不会降低多线程并发访问效率。
public class Singleton {
    private Singleton() {
        System.out.println("已产生对象实例。");
    }
    private static class SingletonHolder {
        private static Singleton singleton = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.singleton;
    }
}
  • 案例鉴赏

java.lang.Runtime#getRuntime()

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    // 忽略其他
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章