反射破坏单例模式

132 阅读2分钟

反射破坏单例模式的方式及解决方案

我们现在使用静态内部类方式来创建一个单例类(其他模式同理,除了枚举不能被破坏):

//Singleton.class
//私有构造方法
private Singleton() {}
​
//定义一个静态内部类
private static class SingletonHolder {
    //在内部类中声明并初始化外部类对象
    private static final Singleton INSTANCE = new Singleton();
}
​
//提供getter
public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
}

现在我们在测试类中来尝试使用反射的方式破坏这个单例模式👇

//Client.class
//1、获取Singleton的字节码对象
Class Singleton = Singleton.class;
//2、通过getDeclareConstructor方法获取无参构造方法对象
Constructor cons = Singleton.getDeclareConstructor();
//3、取消访问检查
cons.setAccessible(true);
//4、创建两个Singleton实例
Singleton s1 = (Singleton)cons.newInstance();
​
Singleton s2 = (Singleton)cons.newInstance();
​
//观察两个实例是否相同
//如果返回true,则说明没有破坏单例模式
//如果返回false,则说明破坏了单例模式
System.out.println(s1 == s2);

最后运行,我们可以得到结果:false

反射破解的解决方案

思路:在匿名构造函数内添加判断,若instance != null,则说明内存中已经存在该实例,也就说明之前就调用过构造函数,获取过该单例实例,这时我们就能明确知道有程序要破坏我们的单例模式,就可以抛出异常提示错误。

我们重写该单例类,解决反射破坏问题:

//Singleton.class
//私有构造方法
private Singleton() {
    //反射破解单例模式需要添加的代码
    if(instance != null) {
        //内存中已经存在实例,非法调用构造方法,抛出异常
        throw new RuntimeException("不能创建多个对象!!");
    }
}
​
//定义一个静态内部类
private static class SingletonHolder {
    //在内部类中声明并初始化外部类对象
    private static final Singleton INSTANCE = new Singleton();
}
​
//提供getter
public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
}

这时我们再进行client测试类调用,创建两个单例类实例时,程序就会报错,至此完美解决反射破解单例模式的问题!