一 问题演示
1 破坏单例模式
除枚举方式外,其它单例类(Singleton)可以创建多个对象。有两种方式可以破坏单例模式,分别是序列化和反射。
1.1 序列化反序列化
Singleton类:
public class Singleton implements Serializable {
// 私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Test类:
public class Test {
public static void main(String[] args) throws Exception {
// 往文件中写对象
// writeObject2File();
// 从文件中读取对象
Singleton s1 = readObjectFromFile();
Singleton s2 = readObjectFromFile();
// 判断两个反序列化后的对象是否是同一个对象
System.out.println(s1 == s2);
}
private static Singleton readObjectFromFile() throws Exception {
// 创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\a.txt"));
// 第一个读取Singleton对象
Singleton instance = (Singleton) ois.readObject();
return instance;
}
public static void writeObject2File() throws Exception {
// 获取Singleton类的对象
Singleton instance = Singleton.getInstance();
// 创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\a.txt"));
// 将instance对象写出到文件中
oos.writeObject(instance);
}
}
上面代码运行结果是
false
,表明序列化和反序列化已经破坏了单例设计模式。
1.2 反射
Singleton类:
public class Singleton {
// 私有构造方法
private Singleton() {}
private static volatile Singleton instance;
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}
Test类:
public class Test {
public static void main(String[] args) throws Exception {
// 获取Singleton类的字节码对象
Class clazz = Singleton.class;
// 获取Singleton类的私有无参构造方法对象
Constructor constructor = clazz.getDeclaredConstructor();
// 取消访问检查
constructor.setAccessible(true);
// 创建Singleton类的对象s1
Singleton s1 = (Singleton) constructor.newInstance();
// 创建Singleton类的对象s2
Singleton s2 = (Singleton) constructor.newInstance();
// 判断通过反射创建的两个Singleton对象是否是同一个对象
System.out.println(s1 == s2);
}
}
上面代码运行结果是
false
,表明序列化和反序列化已经破坏了单例设计模式
注意:枚举方式不会出现这两个问题。
二 问题的解决
1 序列化、反序列方式破坏单例模式的解决方法
在Singleton类中添加readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
Singleton类:
public class Singleton implements Serializable {
// 私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
源码解析:
ObjectInputStream类
public final Object readObject() throws IOException, ClassNotFoundException{
...
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);//重点查看readObject0方法
.....
}
private Object readObject0(boolean unshared) throws IOException {
...
try {
switch (tc) {
...
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法
...
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
private Object readOrdinaryObject(boolean unshared) throws IOException {
...
//isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,
obj = desc.isInstantiable() ? desc.newInstance() : null;
...
// 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
// 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量
// 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。
Object rep = desc.invokeReadResolve(obj);
...
}
return obj;
}
2 反射方式破坏单例模式的解决方法
public class Singleton {
// 私有构造方法
private Singleton() {
/*
反射破坏单例模式需要添加的代码
*/
if(instance != null) {
throw new RuntimeException();
}
}
private static volatile Singleton instance;
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}
说明:
这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。