单例设计模式-序列化破坏单例模式?反射攻击?

1、问题猜想,假如将一个对象通过序列化放到一个文件后,再取出来看是否与本身相等?
public class HungrySingleton implements Serializable {

  private final static HungrySingleton hungrySingleton;

  static {
    hungrySingleton = new HungrySingleton();
  }

  private HungrySingleton() {

  }
  public static HungrySingleton getInstance() {
    return hungrySingleton;
  }
}

public class Test {

  public static void main(String[] args) throws IOException, ClassNotFoundException {

    HungrySingleton instance = HungrySingleton.getInstance();
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("single_file"));
    oos.writeObject(instance);

    File file = new File("single_file");
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
    HungrySingleton newInstance = (HungrySingleton) ois.readObject();

    System.out.println(instance);
    System.out.println(newInstance);
    System.out.println(instance == newInstance);
  }
}

结果:

com.wenwen.design.pattern.Creational.singleton.HungrySingleton@45ee12a7
com.wenwen.design.pattern.Creational.singleton.HungrySingleton@7699a589
false

由此可见,instance和newInstance不相等,就违背了单例模式的一个初衷,通过序列化和反序列化拿到了不同的对象。而我们是希望拿到一个对象。

那么如何解决呢?

在单例中加一个方法就可解决,如下代码所示:

public class HungrySingleton implements Serializable {

  private final static HungrySingleton hungrySingleton;

  static {
    hungrySingleton = new HungrySingleton();
  }

  private HungrySingleton() {

  }
  public static HungrySingleton getInstance() {
    return hungrySingleton;
  }
  private Object readResolve() {
    return hungrySingleton;
  }
}

再次运行测试类之后,结果如下:

com.wenwen.design.pattern.Creational.singleton.HungrySingleton@45ee12a7
com.wenwen.design.pattern.Creational.singleton.HungrySingleton@45ee12a7
true

为什么如此神奇呢?这就要大家深入看下源码了。

2、单例模式是否存在反射攻击?
public static void main(String[] args)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Class objectClass = HungrySingleton.class;
    Constructor constructor = objectClass.getDeclaredConstructor();
    constructor.setAccessible(true);

    HungrySingleton hungrySingleton = HungrySingleton.getInstance();
    HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
    System.out.println(hungrySingleton);
    System.out.println(newInstance);
  }

当出现.setAccessible(true);作用就是让我们在用反射时访问私有变量,那么我们的单例就会受到反射攻击的危害。结果不会报错。

com.wenwen.design.pattern.Creational.singleton.HungrySingleton@677327b6
com.wenwen.design.pattern.Creational.singleton.HungrySingleton@14ae5a5

 

 

 

 

 
posted @ 2019-04-16 20:28 左手背右手背 阅读(...) 评论(...) 编辑 收藏