存在的问题
1. 问题演示
破坏单例模式:使单例可以创建多个对象,枚举方式除外。有两种方式:序列化和反射。
1.1 序列化和反序列化破坏单例模式
任意一种单例模式:
/**
* 静态内部类单例模式(除枚举单例外其他任何一个单例模式都行)
*/
public class Singleton implements Serializable {
// 私有构造方法
private Singleton() {
}
// 静态内部类
public static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供获取实例的方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
客户端:
public class Client {
public static void main(String[] args) throws Exception {
// writeObjectToFile();
readObjectFromFile();
readObjectFromFile();
}
// 从文件中读取对象
public static void readObjectFromFile() throws Exception{
// 1. 创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\A\\Desktop\\object.txt"));
// 2. 读取对象
Singleton instance = (Singleton) ois.readObject();
System.out.println(instance);
// 3. 释放资源
ois.close();
}
// 向文件中写对象
public static void writeObjectToFile() throws Exception{
// 1. 获取singleton对象
Singleton instance = Singleton.getInstance();
// 2. 创建文件输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\A\\Desktop\\object.txt"));
// 3. 将singleton对象写入文件
oos.writeObject(instance);
// 4. 释放资源
oos.close();
}
}
两次从文件中读出的对象结果:
org.singletonPattern.problems.Singleton@4ee285c6
org.singletonPattern.problems.Singleton@621be5d1
由上可见,从一个文件中两次读取对象,两个对象不是同一个对象,就破坏了单例模式。
1.2 通过反射破坏单例模式
仍然使用上述单例模式,代码同上
客户端:
public class Client2 {
public static void main(String[] args) throws Exception {
// 1. 获取Single的字节码对象
Class<Singleton> clazz = Singleton.class;
// 2. 获取Singleton的无参构造方法
Constructor<Singleton> cons = clazz.getDeclaredConstructor();
// 3. 暴力访问(取消访问检查,因为Single对象中的无参构造是私有的)
cons.setAccessible(true);
// 4. 通过反射创建Singleton对象
Singleton s1 = cons.newInstance();
Singleton s2 = cons.newInstance();
System.out.println(s1 == s2); // false
}
}
返回false
,表示反射破坏了单例模式,通过反射创建了两次对象。
2. 问题的解决
2.1 序列化和反序列化问题的解决方法
在Singleton
类中添加readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新对象。
public class Singleton implements Serializable {
// 私有构造方法
private Singleton() {
}
// 静态内部类
public static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供获取实例的方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
// 当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回。防止反序列化重新创建新的对象
public Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
org.singletonPattern.problems.Singleton@2077d4de
org.singletonPattern.problems.Singleton@2077d4de
这样反序列化回来的对象就保持了单例。
追踪源代码:
2.2 反射模式破坏单例模式的解决方法
在构造方法中添加逻辑,使用synchronized
来确保线程安全
public class Singleton implements Serializable {
private static boolean flag = false;
// 私有构造方法
private Singleton() {
synchronized (Singleton.class) {
// 判断falg的值是否为true,如果是true,则说明非第一次访问,直接抛出异常;
// 如果是false,说明是第一次访问,正常创建
if (flag) {
throw new RuntimeException("单例模式被侵犯!");
}
// 将flag设置为true
flag = true;
}
}
// 静态内部类
public static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供获取实例的方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
// 当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回。防止反序列化重新创建新的对象
public Object readResolve() {
return SingletonHolder.INSTANCE;
}
}