原创

破坏单例模式


首先,我先给出一种单例模式的实现方式,用静态内部类去实现单例模式

public class Singleton implements Serializable{
    // 私有构造方法
    private Singleton(){}

   private static class SingletonHolder {
        private static Singleton instance = new Singleton();
   }

   public static Singleton getInstance() {
        return SingletonHolder.instance;
   }
}

我们设计单例模式的意义就是对于某个类只能创建一个对象,但是现在我们可以通过序列化和反射的机制,去破坏单例模式,从而创建出不同的对象

序列化

public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //writeObject2File();
        readObjectFromFile();
        readObjectFromFile();
    }
	
	// 读取文件中的数据
    public static void readObjectFromFile() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\刘柄岐\\Desktop\\a.txt"));
        Singleton instance = (Singleton) ois.readObject();

        System.out.println(instance);
        ois.close();
    }

    //向文件中写数据(对象)
    public static void writeObject2File() throws IOException {
        Singleton instance = Singleton.getInstance();

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\刘柄岐\\Desktop\\a.txt"));
        oos.writeObject(instance);

        //释放资源
        oos.close();
    }
}

控制台打印

com.lbq.pattern.singleton.demo3.Singleton@5b6f7412
com.lbq.pattern.singleton.demo3.Singleton@27973e9b

Process finished with exit code 0

可以看出来,通过序列化再反序列化创建出来的对象,是不同的对象

解决方法

在Singleton类中添加readResolve()方法,在反序列化时被调用,如果定义了这个方法,就返回这个方法的值,如果没有,则返回新new出来的对象

//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
    public Object readResolve() {
        return SingletonHolder.instance;
    }

这样反序列化生成的对象就还是同一个了

反射

public static void main(String[] args) throws Exception{
        // 1.获取Singleton的字节码对象
        Class clazz = Singleton.class;

        Constructor cons = clazz.getDeclaredConstructor();
        cons.setAccessible(true);

        Singleton s1 =  (Singleton) cons.newInstance();
        Singleton s2 =  (Singleton) cons.newInstance();

        // 输出为false,所以破化了单例模式
        System.out.println(s1 == s2);
    }

通过反射获取其私有构造方法,去创建对象从而破坏了单例模式

解决方法

反射能够破坏单例模式,是因为它可以调用其构造方法,就是是private也没用,放射一样能调用,那么我们可以在它的构造方法里面,写一些逻辑,判断是否是第一次创建,如果不是第一次直接抛异常

private static boolean flag = false;
// 私有构造方法
private Singleton(){
    synchronized (Singleton.class) {
        //判断flag的值是否是true,如果是true,说明非第一次访问,直接抛出异常,如果是false的话,说明是第一次
        if (flag) {
            throw new RuntimeException("单例对象不能创建多个");
        }
        flag = true;
    }

}

单例模式常见的一些实现,比如双重检测锁,静态内部类,都是可以通过序列化和反序列化去破坏的,而且往往这些实现方式,设计比较复杂,官方其实推荐我们用另一种实现,枚举,枚举的实现设计简单,没有线程安全问题,而且序列化和反射也是破坏不了的

枚举实现如下

public enum Singleton {
    INSTANCE;
}

没了!!简单吧

Java
  • 作者:刘柄岐
  • 发表时间: 2022-08-07 15:47
  • 版权声明:自由转载-非商用