[java设计模式]破解单例设计模式,禁止偷生、超生!

引言

虽然国家规定了计划生育,但是仍然有很多人。冒着坐牢、推房子、丢工作的风险,偷偷的生下自己的宝宝!对程序而言,同样也可以,虽然我们私有化的构造方法,但是同样有很多种方式去创建新的对象。

[java设计模式]破解单例设计模式,禁止偷生、超生!

反射破解法

主要原理就是,我们获取单例类的构造方法,然后把私有访问权限打开。然后进行创建对象。具体代码如下:
首先搞一个单例类:

/**
 * 先搞一个单例类。
 * @ClassName : Eager  //饿汉单例
 * @Description :   //类加载的时候,进行初始化。
 */
public class Hungry {
    private static Hungry instance = new Hungry();

    /**
     * 私有构造方法
     */
    private Hungry() {
    }

    /**
     * 外部过去实例的通道
     * @return
     */
    public static Hungry getInstance() {
        return instance;
    }
}

进行破解:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @ClassName : 破解
 * @Description :此类演示破解的过程
 */
public class Crack {
    public static void main(String[] args) {
        // 先去校验我们的单例写的是是否成功,我们没有重写equals方法,所有比较是对象的地址值。
        // 此处我们获取了两次,然后进行比较。
        if (Hungry.getInstance()==Hungry.getInstance()){
            // 输出此句话,说明我们的单例设计模式是成功的。
            System.out.println("单例设计模式是成功的");
        }
        // 正常获取单例对象
        Hungry instance = Hungry.getInstance();
        try {
            // 反射获取构造方法
            Constructor<Hungry> declaredConstructor =              Hungry.class.getDeclaredConstructor();
            // 破解private访问权限
            declaredConstructor.setAccessible(true);
            // 使用构造方法创建对象
            Hungry hungry = declaredConstructor.newInstance();
            // 将我们反射获取的对象跟正常渠道获取的单例进行对比
            System.out.println(instance==hungry);
            // 如果输出的是false,说明就是破解成功了。
        } catch (NoSuchMethodException e) {
            System.out.println("获取构造方法异常");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

既然我们反射可以破解我们的单例类,那我们应该知道如何加强防护。

/**
 * @ClassName : Eager  //饿汉单例
 * @Description :   //类加载的时候,进行初始化。
 */
public class Hungry {
    private static Hungry instance = new Hungry();

    /**
     * 私有构造方法
     */
    private Hungry() {
        // 在此处加强防护,针对instance进行判空处理。
        if (instance != null) {
            throw new IllegalArgumentException("对象不允许反射创建");
        }
    }

    /**
     * 外部过去实例的通道
     * @return
     */
    public static Hungry getInstance() {
        return instance;
    }
}

利用对象序列化技术破解

还是老样子,首先搞一个单例对象,此实例对象要实现序列化接口

/**
 * @ClassName : Eager  //饿汉单例
 * @Description :   //类加载的时候,进行初始化。
 */
public class Hungry {
    private static Hungry instance = new Hungry();

    /**
     * 私有构造方法
     */
    private Hungry() {
        // 在此处加强防护,针对instance进行判空处理。
        if (instance != null) {
            throw new IllegalArgumentException("对象不允许反射创建");
        }
    }

    /**
     * 外部过去实例的通道
     * @return
     */
    public static Hungry getInstance() {
        return instance;
    }
}

使用序列化技术进行破解~

import java.io.*;

/**
 * @ClassName : SerializableCrack 序列化破解demo
 * @Description : 演示序列化破解单例设计模式
 */
public class SerializableCrack {
    public static void main(String[] args) {
        // 同样,我们先验证我们的单例是成功的。
        if (Hungry.getInstance() == Hungry.getInstance()) {
            System.out.println("单例设计模式是成功的!");
        }
        // 首先,我们获取一个对象
        Hungry hungry = Hungry.getInstance();
        // 对对象进行序列化的文本中
        try {
            // 创建一个对象序列化流。我们将对象输出到1.txt文本中,等后续读取使用
            ObjectOutputStream objectOutput = new ObjectOutputStream(new FileOutputStream(new File("1.txt")));
            // 输出对象
            objectOutput.writeObject(hungry);
            // 创建一个对象序列化读取流。
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("1.txt")));
            // 读取对象
            Hungry object = (Hungry)objectInputStream.readObject();
            // 进行比较,如果输出的是false,说明破解成功。
            System.out.println(hungry==object);
        } catch (Exception e) {
            System.out.println("序列化失败");
        }
    }
}

加强防护,可以使用readResolve方法,替换到反序列化的对象。

/**
 * @ClassName : Eager  //饿汉单例
 * @Description :   //类加载的时候,进行初始化。
 */
public class Hungry implements Serializable {
    private static Hungry instance = new Hungry();

    /**
     * 私有构造方法
     */
    private Hungry() {
        // 在此处加强防护,针对instance进行判空处理。
        if (instance != null) {
            throw new IllegalArgumentException("对象不允许反射创建");
        }
    }

    /**
     * 外部过去实例的通道
     * @return
     */
    public static Hungry getInstance() {
        return instance;
    }

    /**
     * 此方法可以替换到反序列化的对象。
     */
    Object readResolve() throws ObjectStreamException {
        return instance;
    }
}

差不多就这个样子了。下一篇讲工厂设计模式~

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!