单例模式破解方法除了反射和序列化还有一种方法

面试中常常问到单例模式,并且实际中也是经常使用的方式,但单例模式在哪些情况下会被破解呢?被破解了如何应对破解呢?

反射破解和防破解

/**
 * 饿汉式
 */
public class SingletonTest {
    private SingletonTest(){
        System.out.println("private SingletonTest()");
    }
    private static final SingletonTest INSTANCE = new SingletonTest();
    public static SingletonTest getInstance(){
        return INSTANCE;
    }
    public static void otherMethod(){
        System.out.println("otherMethod()");
    }
}

这是一个单例模式,接下来用反射创建这个对象

 public static void main(String[] args) throws Exception {
        SingletonTest.otherMethod();
        System.out.println(SingletonTest.getInstance());
        reflection(SingletonTest.class);
    }
    private static void reflection(Class<?> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        System.out.println("反射创建实例:" + constructor.newInstance());
    }

返回结果

private SingletonTest()
otherMethod()
com.whf.mian.shi.single.SingletonTest@3c679bde
private SingletonTest()
反射创建实例:com.whf.mian.shi.single.SingletonTest@16b4a017

如何应对这种情况呢?

在私有构造函数中增加一下代码,禁止通过反射机制调用

 private SingletonTest(){
        if(INSTANCE!=null){
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private SingletonTest()");
    }

序列化破坏单例

单例模式要实现序列化接口,才能够应用于序列化

/**
 * 饿汉式
 */
public class SingletonTest implements Serializable {
    private SingletonTest(){
        System.out.println("private SingletonTest()");
    }
    private static final SingletonTest INSTANCE = new SingletonTest();
    public static SingletonTest getInstance(){
        return INSTANCE;
    }
    public static void otherMethod(){
        System.out.println("otherMethod()");
    }
}

通过序列化创建这个对象

public static void main(String[] args) throws Exception {
        SingletonTest.otherMethod();
        System.out.println(SingletonTest.getInstance());
        serializable(SingletonTest.getInstance());
    }
    private static void serializable(Object instance) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        System.out.println("反序列化创建实例:"+ois.readObject());
    }

返回结果

private SingletonTest()
otherMethod()
com.whf.mian.shi.single.SingletonTest@3c679bde
反序列化创建实例:com.whf.mian.shi.single.SingletonTest@735f7ae5

这种情况只需要在单例对象中添加readResolve方法,直接返回该对象即可

 //防止反序列化对象
    public Object readResolve(){
        return INSTANCE;
    }


Unsafe 破坏单例

使用unsafe创建该对象

 public static void main(String[] args) throws Exception {
        SingletonTest.otherMethod();
        System.out.println(SingletonTest.getInstance());
        unsafe(SingletonTest.class);
    }
    private static void unsafe(Class<?> clazz) throws InstantiationException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Constructor<?> constructor = Unsafe.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Unsafe o1 = (Unsafe) constructor.newInstance();
        Object o = o1.allocateInstance(clazz);
        System.out.println("Unsafe 创建实例:" + o);
    }

返回结果

private SingletonTest()
otherMethod()
com.whf.mian.shi.single.SingletonTest@3c679bde
Unsafe 创建实例:com.whf.mian.shi.single.SingletonTest@16b4a017

这种方式还不知道怎么预防。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章