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
总结
反射会创了多个实例,最后经过判断没有返回新的实例,创建多个实例但是并没有使用。