设计模式-单例模式(Singleton Pattern)

一、概念

1.1 定义

它是一种创建类的对象的模式,能够确保系统中只产生该类的一个对象。

1.2 作用

  1. 可以省略那些被频繁使用的对象的创建时间,节省系统开销。
  2. 降低内存使用频率,减轻GC压力,缩短GC停顿时间。

二、种类

2.1 饿汉式单例

public class Singleton1 {
    /**
     * 必须有一个private修饰的构造器
     */
    private Singleton1() {
        System.out.println("Singleton instance is create!!");
    }

    //该成员变量必须用static修饰
    private static Singleton1 instance = new Singleton1();

    /**
     * 创建实例的方法必须用static修饰
     */
    public static Singleton1 getInstance() {
        return instance;
    }
}

优点:实现方式简单,可靠。
缺点:无法实现延迟加载,由于instance成员变量是static的,在jvm加载类时单例对象就会被创建,而不管该类是否能被用到。

2.2 懒汉式单例

public class Singleton2 {

    private Singleton2() {
        System.out.println("LazySingleton instance is create!!");
    }

    /**
     * instance赋值null,确保系统加载时没有额外负载
     */
    private static Singleton2 instance = null;

    /**
     * 注意:该方法必须是同步的,假如去掉同步关键字,在多线程环境下,加入线程正在新建单例,
     * 完成赋值前,线程2进行instance是否为null判断,可能被认为是null,从而会导致多个实例被创建
     * @return
     */
    public static synchronized Singleton2 getInstance() {
        if(instance == null){
            instance = new Singleton2();
        }
        return instance;
    }
}

优点:实现了延迟加载。
缺点:由于引入了synchronized 关键字,再多线程的环境下它的耗时要远远大于饿汉式单例。

2.3 静态内部类实现

public class Singleton3 {
    private Singleton3() {
        System.out.println("StaticSingleton instance is create!!");
    }

    private static class SingletonHolder{
        private static Singleton3 instance = new Singleton3();
    }

    public static Singleton3 getInstance() {
        return SingletonHolder.instance;
    }
}

优点:使用内部类来维护单例的实例,当该类被jvm加载时,内部类不会被实例化,当要获取单例实例时才会加载SingletonHolder,对instance进行初始化。同时,实例的建立实在类加载时完成,所以是多线程安全的。因此该方法即支持延迟加载又是线程安全的,算是很完美了。
缺点:有种极端情况,如果通过反射调用私有构造器依然会产生多个实例,一般不进行考虑。

2.4 能被串行化的单例

public class Singleton4 implements Serializable {
    private Singleton4() {
        System.out.println("Singleton instance is create!!");
    }

    private static Singleton4 instance = new Singleton4();

    public static Singleton4 getInstance() {
        return instance;
    }

    /**
     * 如果去掉该方法,在反序列化后依然生成多个实例。
     * 事实上,实现该方法后readObject()已经失效,返回值已经被readResolve()替代。
     * @return
     */
    private Object readResolve() {
        System.out.println("method [readResolve()] is invoked!!");
        return instance;
    }
}

测试代码

class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Singleton4 s1 = null;
        Singleton4 s = Singleton4.getInstance();
        //将实例串行化到文件
        FileOutputStream fos = new FileOutputStream("Singleton4.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(s);
        oos.flush();
        oos.close();

        //从文件读出原有的单例类
        FileInputStream fis = new FileInputStream("Singleton4.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //readObject()已经失效,已经被readResolve()。
        s1 = (Singleton4) ois.readObject();

        System.out.println(s==s1);
    }
}

注意
序列化和反序列化会破坏单例,一般来说这种场景不多见。

只有把命运掌握在自己手中,从今天起开始努力,即使暂时看不到希望,也要相信自己。因为比你牛几倍的人,依然在努力。
原文地址:https://www.cnblogs.com/freesky168/p/14358231.html