【Java+设计模式】通过反射机制/序列化机制破解单例模式,及其解决方法

news/2024/5/19 3:02:06 标签: 单例模式, 反射, 序列化, 设计模式

单例模式

关于单例模式,可以戳这篇文章:《设计模式单例模式(Singleton Pattern)

下面这是一个经典的懒汉式单例模式实现。

public class Singleton {
	// 1.在类中添加一个私有静态成员变量用于保存唯一实例
    private static Singleton instance;
    // 2.将默认构造方法设置为私有,这样它就不能被new了
    private Singleton() { }
	// 3.写一个公有静态成员方法,暴露给外部用于获取唯一实例
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
public class SingletonTest {
    public static void main(String[] args) throws Exception {
		Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);		// true
    }
}

 
 

反射机制破解单例模式

单例模式的实现中有一个核心步骤,将无参构造方法设置为私有,防止被外部访问到,从而new不出对象。

那么事情就变得非常简单——反射机制可以调用构造方法对象,且可以跳过访问权限检查,进行暴力反射

public class DestroySingletonByReflect {
    public static void main(String[] args) throws Exception {
		// 加载类
        Class.forName("Singleton");
        // 通过反射获取无参构造方法对象
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        // 跳过权限检查,进行暴力反射
        constructor.setAccessible(true);
        // 通过无参构造方法对象获得实例
        Singleton instance1 = constructor.newInstance();
        Singleton instance2 = constructor.newInstance();
        // 获取的还是同一个实例吗?
        System.out.println(instance1 == instance2);		// false
    }
}

如何避免单例模式反射机制破解?

也很好理解,反射机制破解单例模式的本质是对无参构造方法(对象)的调用。我们给无参构造方法也加上单例模式的逻辑即可。

public class Singleton {
    private static Singleton instance;
    private Singleton() {
    	// 给无参构造方法也加上单例模式的逻辑
    	if (instance == null) {
            throw new RuntimeException();
        }
    }
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

 
 

序列化机制破解单例模式

序列化/序列化经常帮助我们曲线救国(必须Java对象的深拷贝),但它也有做坏事的时候。

想一想,如果我们把单例对象序列化后存进文件,然后反序列化出来,得到的还是原先的那个单例对象吗?

public class DestroySingletonBySerialize {
    public static void main(String[] args) throws Exception {

        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        System.out.println(instance1 == instance2);		// true

		// 序列化
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("/Users/samarua/Documents/object.txt")));
        objectOutputStream.writeObject(instance2);

		// 反序列化
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("/Users/samarua/Documents/object.txt")));
        Singleton instance3 = (Singleton) objectInputStream.readObject();

        System.out.println(instance1 == instance3);		// false
    }
}

如何避免单例模式序列化机制破解?

序列化时永远不会调用构造方法,所以之前的反制方法失效了。这里,利用了序列化/反序列化机制的一个细节,在反序列化返回对象之前,会先看看有没有 readResolve() 方法,如果有,则返回该方法所返回的对象。

class Singleton implements Serializable {
    private static Singleton instance;
    private Singleton() { }
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    // 反序列时通过该方法返回了instance实例
    private Object readResolve() {
        return instance;
    }
}

 
 
 
 
 

 
 
 
 
 


http://www.niftyadmin.cn/n/1329872.html

相关文章

【设计模式】简单工厂模式(Simple Factory Pattern)

&#x1f525; 核心 一个工厂能够根据需要产生不同的产品。 如果是根据参数生产产品&#xff0c;则称为简单工厂模式&#xff1b;定义一个创建对象的工厂接口&#xff0c;其子类自己已经决定好了生产哪一种产品&#xff0c;则称为工厂模式&#xff1b;这个子类工厂能生产一套…

【设计模式】工厂模式(Factory Pattern)

&#x1f525; 核心 一个工厂能够根据需要产生不同的产品。 如果是根据参数生产产品&#xff0c;则称为简单工厂模式&#xff1b;定义一个创建对象的工厂接口&#xff0c;其子类自己已经决定好了生产哪一种产品&#xff0c;则称为工厂模式&#xff1b;这个子类工厂能生产一套…

【设计模式】抽象工厂模式(Abstract Factory Pattern)

&#x1f525; 核心 一个工厂能够根据需要产生不同的产品。 如果是根据参数生产产品&#xff0c;则称为简单工厂模式&#xff1b;定义一个创建对象的工厂接口&#xff0c;其子类自己已经决定好了生产哪一种产品&#xff0c;则称为工厂模式&#xff1b;这个子类工厂能生产一套…

【设计模式】建造器模式(Builder Pattern)

&#x1f525; 核心 通过建造器&#xff0c;使用多个简单的对象一步一步构造出一个复杂的对象。 &#x1f641; 问题场景 你现在从一名程序开发者转行为了一名房屋建筑师。你的任务就是建房子。 你很快建好了一个 房子(House) 。这个房子普普通通&#xff0c;压根不是什么难…

【设计模式】原型模式(Prototype Pattern)

&#x1f525; 核心 直接复制已有对象&#xff0c;而不依赖它们所属的类。 对象本身自己复制自己。 &#x1f641; 问题场景 有一天你在森林中散步时&#xff0c;突然发现不远处泛着蓝光。你凑近过去&#xff0c;发现这个是一架刚刚坠落的UFO。 UFO上已经没有了外星人&…

【设计模式】单例模式(Singleton Pattern)

&#x1f525; 核心 单例模式保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。 饿汉很饿&#xff0c;在类加载时就实例化&#xff1b;懒汉很懒&#xff0c;第一次调用时才初始化。 &#x1f641; 问题场景 假如你是上帝&#xff0c;你的手边有一个 Sun(太…

【设计模式】适配器模式(Adapter Pattern)

&#x1f525; 核心 适配器模式使得原本不兼容的两个类可以合作工作。 适配器的本质是欺骗&#xff0c;它有着A类的外表&#xff0c;却有着B类的内容。 &#x1f641; 问题场景 你是一个手机制造商。最近的科技发展迅猛&#xff0c;产生了一个让你头疼不已的问题。 之前处于…

【设计模式】装饰器模式(Decorator Pattern)

&#x1f525; 核心 装饰器模式是对现有类的一个包装&#xff0c;是继承的一种替代方案。 装饰类中拥有一个指向现有类的成员变量。 &#x1f641; 问题场景 你是一名MOBA游戏设计师。 在你设计的这个MOBA游戏中&#xff0c;英雄升级后可以选择学习一个附加的技能。现在&am…