单例模式

2019/05/04 设计模式

单例模式

1.饿汉

public class Singleton {
    /**
     * 饿汉
     */
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

2.懒汉

public class Singleton {
    /**
     * 懒汉
     */
    private static Singleton INSTANCE = null;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (null == INSTANCE) {
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}

3.静态内部类

public class Singleton {
    /**
     * 静态内部类
     */
    private Singleton(){}
    private static class InternalDemo{
        private final static Singleton INSTANCE = new Singleton();
    }
    public Singleton getInstance(){
        return InternalDemo.INSTANCE;
    }
}

4.双重加锁

public class Singleton {
    /**
     * 双重加锁
     */
    private volatile static Singleton INSTANCE;
    private Singleton(){};
    public static Singleton getInstance(){
        if(null == INSTANCE){
            synchronized(Singleton.class){
                if(null == INSTANCE){
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

5.枚举

public enum Singleton {
    /**
     * 枚举单例
     */
    INSTANCE;
    public Singleton getInstance(){
        return INSTANCE;
    }
}

6.总结

实现单例模式的三个特点:构造方法私有化,实例化的变量引用私有化,获取实例的方法共有。

除了枚举外,别的方法都存在序列化问题和被反射攻击的问题。

序列化问题: 单例类实现了java.io.Serializable接口,就可能被序列化和反序列化,比如用Stream的方式来读取,readObject()方法每次都会新建一个实例对象,这个新建的实例不同于该类初始化时创建的实例。

反射问题: 单例类由不同的类装载器装入,就有可能存在多个单例类的实例。

JDK源码对枚举做了特殊处理,反射在通过newInstance()创建对象时,会检查该类是否EMUM修饰,如果是则抛出异常,反射失败。

获取类的Class对象的方法:

getClass(): 作用于对象,在编译期加载;

Object obj = new Object();
obj.getClass();

.class: 作用于类,在编译期加载;

Object obj = Object.class;

Class.forName(“”): 在运行时动态加载,在loadClass后必须初始化,目标对象的static代码块会被初始化;

Class.forName("java.lang.Object");

ClassLoader.loadClass(“”): 目标对象被装载后不会进行链接,不会去执行该类的静态代码块。

ClassLoader.getSystemClassLoader().loadClass("java.lang.Object");

参考:

单例模式5种写法

为什么要用枚举实现单例模式(避免反射、序列化问题)

ClassLoader.loadClass()与Class.forName()的区别

Search

    Table of Contents