设计模式番外篇:通过序列化和反射破坏单例模式及其预防

一.通过序列化来破坏单例模式

在java中,对象的序列化与反序列化是通过readObject和writeObject来实现的

1.序列化的前提

被序列化的类必须实现Serializable接口

定义一个序列化方法

  1. private static <T> void serialize(Object obj, Class<T> clazz) {
  2. String fileName = "singleton";
  3. try {
  4. /**
  5. * 将对象数据写入到文件中
  6. */
  7. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(fileName));
  8. objectOutputStream.writeObject(obj);
  9. objectOutputStream.close();
  10. /**
  11. * 通过文件将对象读取出来
  12. */
  13. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(fileName));
  14. T newInstance = (T) objectInputStream.readObject();
  15. objectInputStream.close();
  16. System.out.println("instance:"+obj);
  17. System.out.println("newInstance"+newInstance);
  18. System.out.println(obj == newInstance);
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. }

测试代码:

  1. public static void main(String[] args) {
  2. HungrySingleton instance = HungrySingleton.getInstance();
  3. serialize(instance, HungrySingleton.class);
  4. }

如果HungrySingleton这个类不实现Serialize方法,那么会抛出以下异常:

  1. java.io.NotSerializableException: com.shixinke.practise.design.pattern.content.creation.singleton.HungrySingleton

改造HungrySingleton,使之实现序列化

  1. /**
  2. * 饿汉式
  3. * @author shixinke
  4. */
  5. public class HungrySingleton implements Serializable {
  6. private static final HungrySingleton INSTANCE = new HungrySingleton();
  7. private HungrySingleton() {
  8. }
  9. public static HungrySingleton getInstance() {
  10. return INSTANCE;
  11. }
  12. }
2.在被序列化的类加上readResolve方法
  1. /**
  2. * 饿汉式
  3. * @author shixinke
  4. */
  5. public class HungrySingleton implements Serializable {
  6. private static final HungrySingleton INSTANCE = new HungrySingleton();
  7. private HungrySingleton() {
  8. }
  9. public static HungrySingleton getInstance() {
  10. return INSTANCE;
  11. }
  12. private Object readResolve() {
  13. return INSTANCE;
  14. }
  15. }

Q:为什么要定义readResolve方法呢?

A: 这个还得从readObject这个方法说起,readObject会根据对象的类型来执行不同的操作,对于Object对象,那它会查找是否有readResolve这个方法,如果存在,则执行它

readResolve原理

二.通过反射来破坏单例模式

1.通过反射创建新的对象

虽然原类中的构造方法是私有的,但是反射是无所不能的

  1. /**
  2. * 通过反射来破坏单例模式
  3. * @author shixinke
  4. */
  5. public class ReflectDestroy {
  6. private static <T> void reflect(Object instance, Class<T> clazz) {
  7. try {
  8. Constructor constructor = clazz.getDeclaredConstructor();
  9. /**
  10. * 将构造方法设置成可访问的
  11. */
  12. constructor.setAccessible(true);
  13. T newInstance = (T) constructor.newInstance();
  14. System.out.println("instance:" + instance);
  15. System.out.println("newInstance:" + newInstance);
  16. System.out.println(instance == newInstance);
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. public static void main(String[] args) {
  22. HungrySingleton instance = HungrySingleton.getInstance();
  23. reflect(instance, HungrySingleton.class);
  24. }
  25. }
2.在构造方法中判断是否被实例化,抛出异常
  1. /**
  2. * 饿汉式
  3. * @author shixinke
  4. */
  5. public class HungrySingleton implements Serializable {
  6. private static final HungrySingleton INSTANCE = new HungrySingleton();
  7. private HungrySingleton() {
  8. /**
  9. * 添加判断
  10. */
  11. if (INSTANCE != null) {
  12. throw new RuntimeException("单例类不能被反射");
  13. }
  14. }
  15. public static HungrySingleton getInstance() {
  16. return INSTANCE;
  17. }
  18. private Object readResolve() {
  19. return INSTANCE;
  20. }
  21. }