单例设计模式 (2)

150 阅读2分钟

1.静态内部类实现

在上一版的时候用的是懒汉和饿汉来做单例模式,如果我们采用静态内部类的话,就可以通过classloader来懒加载单例 用静态内部类实现单例模式:

public class Singleton {
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static Singleton getInstance() {
        return LazyHolder.INSTANCE;
    }
}

这里有几个需要注意的点:

1.从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到单例对象INSTANCE。

2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。因此这种实现方式是利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。

2.反射机制打破单例

//获得构造器
Constructor con = Singleton.class.getDeclaredConstructor();
//设置为可访问
con.setAccessible(true);
//构造两个不同的对象
Singleton singleton1 = (Singleton)con.newInstance();
Singleton singleton2 = (Singleton)con.newInstance();
//验证是否是不同对象
System.out.println(singleton1.equals(singleton2));

代码可以简单归纳为三个步骤:

第一步,获得单例类的构造器。

第二步,把构造器设置为可访问。

第三步,使用newInstance方法构造对象。

最后为了确认这两个对象是否真的是不同的对象,我们使用equals方法进行比较。毫无疑问,比较结果是false。

3.用枚举实现单例模式(最佳):

JVM的enum语法糖会阻止枚举类的私有构造方法,缺点是不能懒加载

    enum Color {
        RED(1),GREEN(2),BLUE(3);
        private int code;
        Color(int code){
            this.code=code;
        }
        public int getCode(){
            return code;
        }
    }

####4. 总结:

image.png

####5. 补充:

使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。

对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve方法。

转自:https://mp.weixin.qq.com/s/AdJI5a4w515SPPI_4gVImA