第五章 破坏单例模式

本文深入探讨了Java中的单例模式,包括简单工厂、工厂方法、抽象工厂模式以及单例模式的实现。特别地,文章详细分析了如何通过反射和序列化破坏单例模式的唯一性,并提供了防止此类破坏的方法。对于反射破坏,指出即使通过反射调用构造方法也能保持单例;对于序列化问题,解释了如何通过`readResolve()`方法防止单例在反序列化时被重新创建。文章最后总结了确保单例模式安全的重要性和实践技巧。
摘要由CSDN通过智能技术生成

Java设计模式浅析

第一章 简单工厂模式
第二章 工厂方法模式
第三章 抽象工厂模式
第四章 单例模式
第五章 破坏单例模式


一、反射破坏单例

上一章中单例模式的构造方法除了加上 private 关键字,没有做任何处理。
如果我们使用反射来调用其构造方法,在调用 getInstance() 方法,
应该有两个不同的实例。现在来看一段测试代码,以 LazyInnerClassSingleton 为例:

示例代码

LazyInnerClassSingleton

public class LazyInnerClassSingleton {
    private LazyInnerClassSingleton() {
        /*if (LazyHolder.INSTANCE != null) {
            throw new RuntimeException("不允许创建多个实例");
        }*/
    }

    // 注意关键字final,保证方法不被重写和重载
    public static final LazyInnerClassSingleton getInstance() {
        // 在返回结果之前,会先加载内部类
        // 调用内部类属性
        return LazyHolder.INSTANCE;
    }

    // 默认不加载
    //LazyHolder  单例持有者
    // 最终返回 LazyHolder 的属性 LAZY 的引用,最终把引用返回到客户端
    private static class LazyHolder {
        private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
    }
}

 /*if (LazyHolder.INSTANCE != null) {
        throw new RuntimeException("不允许创建多个实例");
    }*/

测试代码

LazyInnerClassSingletonTest

public class LazyInnerClassSingletonTest {
    public static void main(String[] args) {
        
        try {
            // 声明一个Class对象 该对象是LazyInnerClassSingleton
            Class<?> clazz = LazyInnerClassSingleton.class;
            // 通过反射获取私有的构造方法
            Constructor c = clazz.getDeclaredConstructor(new Class[]{});
            // 强制访问  --private 私有成员变量 设置强制访问
            c.setAccessible(true);
            // 暴力初始化
            Object o1 = c.newInstance();
            // 调用了两次构造方法,相当于“new”了两次
            Object o2 = c.newInstance();
            System.out.println(o1 == o2);
            //System.out.println(o1);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
注释结果

在这里插入图片描述

非注释结果

在这里插入图片描述

二、序列化破坏单例

一个单例对象创建好后,有时候需要将对象序列化然后写入磁盘,
下次使用时再从磁盘中读取对象并进行反序列化,将其转化为内存对象。
反序列化后的对象会重新分配内存,即重新创建。如果序列化的目标对象为单例对象,
就违背了单例模式的初衷,相当于破坏了单例,来看一段代码:
示例代码

SeriableSingleton

public class SeriableSingleton implements Serializable {
    // 序列化就是把内存中的状态通过转换成字节码的形式
    // 从而转换一个 I/O 流,写入其他地方(可以是磁盘、网络 I/O)
    // 内存中的状态会永久保存下来

    // 反序列化就是将已经持久化的字节码内容转换为I/O流
    // 通过I/O流的读取,进而将读取的内容转换为Java对象
    // 在转换过程中会重新创建对象new
    private SeriableSingleton(){}
    private static final SeriableSingleton instance = new SeriableSingleton();
    public static SeriableSingleton getInstance(){
        return instance;
    }
      //避免序列化破坏单例
   /* private Object readResolve(){
        return instance;
    }*/
}

测试代码

SeriableSingletonTest

import java.io.*;


public class SeriableSingletonTest {
    public static void main(String[] args) {
        SeriableSingleton s1 = null;
        SeriableSingleton s2 = SeriableSingleton.getInstance();

        FileOutputStream fos = null;
        try {
            //文件输出流
            fos = new FileOutputStream("SeriableSingleton.obj");
            //对象输出流
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            //写对象
            oos.writeObject(s2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois= new ObjectInputStream(fis);
            s1 = (SeriableSingleton) ois.readObject();
            ois.close();
            fis.close();

            System.out.println(s1 == s2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
注释结果

在这里插入图片描述

非注释结果

在这里插入图片描述
//避免序列化破坏单例
/* private Object readResolve(){
return instance;
}*/

readResolve 详解见
https://www.yuque.com/yinjianwei/vyrvkf/uxggg3#lqkFI

总结

反射会创了多个实例,最后经过判断没有返回新的实例,创建多个实例但是并没有使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值