设计模式——单例模式

1、单例模式定义

  单例模式可定义为:一个类只有一个实例,并且该类能够自行创建此实例的模式。在Java中,单例模式可以保证一个类在JVM中只有一个实例对象存在。

  一般情况下,我们定义一个普通的类之后,当需要该类的实例对象时只需要通过new操作符就可以获得此类的一个实例对象,这是因为Java类都包含一个或一个以上的构造方法,构造方法用于构造该类的实例对象,Java语言通过new关键字来调用构造方法,从而返回该类的实例对象。还需要明确的是,当没有在类中自定义构造方法时候,系统会为类提供一个默认的无参构造方法。

  综合以上可知,单例模式中的类,需要对构造方法进行特殊处理,不能让构造方法在外部被调用而产生类的实例对象(否则可能会破坏实例对象的唯一性),可以用private修饰符修饰构造方法来达到禁止外部访问构造方法的目的,也即在类中显示定义私有的构造方法。

2、单例模式特点

  (1)由于单例模式只生成一个实例对象,所以减小了系统的性能开销。尤其是对于创建实例对象消耗资源较多的类,此好处更加明显。

  (2)由于不是采用new操作符的方式创建类的实例对象,所以不用频繁的在系统内存中开辟内存创建实例以及进行GC操纵,也即降低了系统内存的使用频率,减小了GC压力。

  (3)单例类对外提供一个访问该单例的全局访问点,优化了共享资源的访问方式。

3、单例模式的实现和分析

  (1)饿汉式实现单例模式

    (a)饿汉式

      饿汉式实现的特点是在类加载的时候就开始创建一个实例,具体代码如下:

package com.singleton;
public class SingleTon01 {
    //类初始化的时候立即初始化instance对象
    private static SingleTon01 instance = new SingleTon01();
    private SingleTon01() {}
    //getInstance方法没有同步,所以效率较高
    public static SingleTon01 getInstace() {
        return instance;
    }
}

    (b)分析:

    (I)instance是静态私有变量,保证了只能在类内部访问,而且在类加载的时候创建实例对象

    (II)类的构造方法是私有的,这一点很重要,一般类的构造方法是共有的,从而保证在程序中可以使用new操作符来创建类的对象。但是在单例模式中不允许通过new来创建对象(否则可能会出现多个实例,违反了单例的定义),所以要把类的构造方式设置为private。

    (III)getInstance方法需要设置为public,也即对外提供一个共有的方法来创建或获取该静态私有实例

    (c)测试

package com.singleton;
public class Client {
    public static void main(String[] args) {//饿汉式单例模式实现
        //此处创建两个SingleTon01类的对象,打印输出查看是否是同一个对象,如果输出的是同一个对象,则满足单例要求
        //因为SingleTon01中的getInstance给定义为static方法,所以使用的时候通过类名来调用
        SingleTon01 s1 = SingleTon01.getInstace();
        SingleTon01 s2 = SingleTon01.getInstace();
        System.out.println(s1);
        System.out.println(s2);       
    }
}

    (d)运行结果:

com.singleton.SingleTon01@15db9742
com.singleton.SingleTon01@15db9742

  打印结果一样,证明了两个对象是同一个对象。

 

  (2)懒汉式实现单例模式

    (a)懒汉式

      懒汉式实现的特点是在类加载的时候没有创建实例,而是在实际使用(也即第一次调用getInstance方法)的时候再去创建实例,也即所谓的延迟加载。

package com.singleton;
//此案例为懒汉模式,也即在创建类的时候没有立即加载对象,而是进行延迟加载
public class SingleTon02 {
    //也是静态私有对象,但是不进行初始化
    private static SingleTon02 instance;
    private SingleTon02() {}
    //对方法进行同步,保证了唯一性,并发性降低了
    public static synchronized SingleTon02 getInstance() {
        if(instance == null) {
            instance = new SingleTon02();
        }
        return instance;
    }
}

    (b)分析

      getInstance方法被synchronized修饰符所修饰,在多线程环境下也能保证线程安全。但是会影响系统的性能。

    (c)测试

package com.singleton;
public class Client {
    public static void main(String[] args) {//懒汉式单例模式实现
        SingleTon02 s1 = SingleTon02.getInstance();
        SingleTon02 s2 = SingleTon02.getInstance();
        System.out.println(s1);
        System.out.println(s2);        
    }
}

    (d)运行结果

com.singleton.SingleTon02@15db9742
com.singleton.SingleTon02@15db9742

 

  (3)静态内部类实现单例模式

    (a)静态内部类

      单例模式使用静态内部类来维护单例的实现时,JVM内部的机制能够保证当一个类被加载时类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。

package com.singleton;
public class SingleTon03 {
    //私有的静态内部类
    private static class InnerClass{
        //静态内部类中有一个私有的静态变量instance,变量的类型是外部类的类型
        private static final SingleTon03 instance = new SingleTon03();
    }
    //私有构造方法,防止在外部被实例化
    private SingleTon03() {} 
    //公有的获得实例的方法
    public static SingleTon03  getInstance(){
        return InnerClass.instance;//可以访问此静态类内部的属性instance
    }
}

    (b)分析

      在类中定义一个私有的静态内部类,内部类中定义一个私有的外部类的变量instance,用作最终返回的实例。

    (c)测试

package com.singleton;
//单例模式,是保证在整个程序中,某个类的对象有且只能一个
public class Client {
    public static void main(String[] args) {
        SingleTon03 s1 = SingleTon03.getInstance();
        SingleTon03 s2 = SingleTon03.getInstance();
        System.out.println(s1);
        System.out.println(s2);        
    }
}

    (d)运行结果

com.singleton.SingleTon03@15db9742
com.singleton.SingleTon03@15db9742
原文地址:https://www.cnblogs.com/xwwbb/p/11094141.html