单例模式破坏及解决方案
在单例模式的七种实现方式中除了枚举外六种单例模式的实现方式存在一个共同的缺陷,就是存在可以创建多个实例的方法,有两种方法可以破坏单例的唯一性。
反射
单例测试类如下,使用双重检查锁定实现方式:
public class DCLSingleton /* implements Serializable */{
private static volatile DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if(instance == null) {
synchronized (DCLSingleton.class) {
if(instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
演示
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Class clazz = DCLSingleton.class;
//获取被private修饰的私有无参构造
Constructor constructor = clazz.getDeclaredConstructor();
// 跳过访问检查 暴力反射
constructor.setAccessible(true);
DCLSingleton instance1 = (DCLSingleton) constructor.newInstance();
DCLSingleton instance2 = (DCLSingleton) constructor.newInstance();
System.out.println(instance1.equals(instance2));
}
}
测试输出结果如下,输出false,单例唯一性被破坏。
DesignPatterns.Singleton.DCLSingleton@14ae5a5
DesignPatterns.Singleton.DCLSingleton@7f31245a
false
解决方案
修改私有构造方法,使用构造方法创建实例时直接抛出运行时异常中断,双检锁为例,私有方法修改如下:
public class DCLSingleton {
private static volatile DCLSingleton instance;
private DCLSingleton() {
throw new RuntimeException();
}
public static DCLSingleton getInstance() {
if(instance == null) {
synchronized (DCLSingleton.class) {
if(instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
经测试,修改后的代码使用反射创建实例时抛出异常,实例创建失败,反射破坏单例唯一性问题解决。
序列化
单例测试类如下,使用饿汉式实现方式:
public class SingletonStatic implements Serializable {
private static SingletonStatic instance = new SingletonStatic();
private SingletonStatic(){}
public static SingletonStatic getInstance(){
return instance;
}
}
演示
public class SerializableTest {
public static void main(String[] args) throws Exception {
//write();
DCLSingleton s1 = read();
DCLSingleton s2 = read();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
}
//从测试文件中读取DCLSingleton实例
private static DCLSingleton read() throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("C:\\Users\\16964\\Desktop\\test.txt"));
DCLSingleton instance = (DCLSingleton) objectInputStream.readObject();
return instance;
}
//将DCLSingleton实例写入测试文件
public static void write() throws Exception {
DCLSingleton instance = DCLSingleton.getInstance();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C:\\Users\\16964\\Desktop\\test.txt"));
objectOutputStream.writeObject(instance);
}
}
测试输出结果如下,输出false,单例唯一性被破坏。
DesignPatterns.Singleton.DCLSingleton@5fd0d5ae
DesignPatterns.Singleton.DCLSingleton@2d98a335
false
解决方案
解决序列化的方式是在单例类中加入一个readResolve()方法,并且由该方法返回已经被创建好的实例,readResolve()方法会在反序列化时通过反射的方式调用,如果不加入readResolve()方法,会返回通过new关键字创建的对象。
public class SingletonStatic implements Serializable {
private static SingletonStatic instance = new SingletonStatic();
private SingletonStatic(){}
public static SingletonStatic getInstance(){
return instance;
}
private Object readResolve() {
return instance;
}
}
测试输出结果如下,输出true,序列化破化单例唯一性问题解决。
DesignPatterns.Singleton.SingletonStatic@5fd0d5ae
DesignPatterns.Singleton.SingletonStatic@5fd0d5ae
true