欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

单例模式存在的问题——破坏单例模式,序列化和反射

发布时间:2025/4/16 编程问答 31 豆豆
生活随笔 收集整理的这篇文章主要介绍了 单例模式存在的问题——破坏单例模式,序列化和反射 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

破坏单例模式:

使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。

有两种方式,分别是序列化和反射



序列化反序列化

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,表明序列化和反序列化已经破坏了单例设计模式。



反射

Singleton类:

package com.itheima.pattern.singleton.demo8;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;}} }

Client.java

package com.itheima.pattern.singleton.demo8;import java.lang.reflect.Constructor;/*** @version v1.0* @ClassName: Client* @Description:* 测试使用反射破坏单例模式* @Author: dym*/ public class Client {public static void main(String[] args) throws Exception {//1,获取Singleton的字节码对象Class clazz = Singleton.class;//2,获取无参构造方法对象Constructor cons = clazz.getDeclaredConstructor();//3,取消访问检查cons.setAccessible(true);//4,创建Singleton对象Singleton s1 = (Singleton) cons.newInstance();Singleton s2 = (Singleton) cons.newInstance();System.out.println(s1 == s2); //如果返回的是true,说明并没有破坏单例模式,如果是false,说明破坏了单例模式} }

上面代码运行结果是false,表明序列化和反序列化已经破坏了单例设计模式

注意:枚举方式不会出现这两个问题。



问题的解决

  • 序列化、反序列方式破坏单例模式的解决方法

    在Singleton类中添加readResolve()方法,在反序列化时被反射调用

                 如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。

Singleton类:

package com.itheima.pattern.singleton.demo7;import java.io.Serializable;/*** @version v1.0* @ClassName: Singleton* @Description: 静态内部类方式* * @Author: dym*/ 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;}//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回public Object readResolve() {return SingletonHolder.INSTANCE;}}

Client.java

package com.itheima.pattern.singleton.demo7;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream;/*** @version v1.0* @ClassName: Client* @Description:* 测试使用序列化破坏单例模式** 桌面路径: C:\Users\Think\Desktop* * @Author: dym*/ public class Client {public static void main(String[] args) throws Exception { // writeObject2File();readObjectFromFile();readObjectFromFile();}//从文件读取数据(对象)public static void readObjectFromFile() throws Exception {//1,创建对象输入流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\a.txt"));//2,读取对象Singleton instance = (Singleton) ois.readObject();System.out.println(instance);//释放资源ois.close();}//向文件中写数据(对象)public static void writeObject2File() throws Exception {//1,获取Singleton对象Singleton instance = Singleton.getInstance();//2,创建对象输出流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\a.txt"));//3,写对象oos.writeObject(instance);//4,释放资源oos.close();} }



源码解析:

ObjectInputStream类

public final Object readObject() throws IOException, ClassNotFoundException{...// if nested read, passHandle contains handle of enclosing objectint 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() 方法执行结果为trueif (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {// 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量// 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。Object rep = desc.invokeReadResolve(obj);...}return obj; }

反射方式破解单例的解决方法

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;}} }

或者是这种方式

package com.itheima.pattern.singleton.demo8;/*** @version v1.0* @ClassName: Singleton* @Description: 静态内部类方式* * @Author: dym*/ public class Singleton {private static boolean flag = false;//私有构造方法private Singleton() {synchronized (Singleton.class) {//判断flag的值是否是true,如果是true,说明非第一次访问,直接抛一个异常,如果是false的话,说明第一次访问if (flag) {throw new RuntimeException("不能创建多个对象");}//将flag的值设置为trueflag = true;}}//定义一个静态内部类private static class SingletonHolder {//在内部类中声明并初始化外部类的对象private static final Singleton INSTANCE = new Singleton();}//提供公共的访问方式public static Singleton getInstance() {return SingletonHolder.INSTANCE;} }

Client.java

package com.itheima.pattern.singleton.demo8;import java.lang.reflect.Constructor;/*** @version v1.0* @ClassName: Client* @Description:* 测试使用反射破坏单例模式* * @Author: dym*/ public class Client {public static void main(String[] args) throws Exception {//1,获取Singleton的字节码对象Class clazz = Singleton.class;//2,获取无参构造方法对象Constructor cons = clazz.getDeclaredConstructor();//3,取消访问检查cons.setAccessible(true);//4,创建Singleton对象Singleton s1 = (Singleton) cons.newInstance();Singleton s2 = (Singleton) cons.newInstance();System.out.println(s1 == s2); //如果返回的是true,说明并没有破坏单例模式,如果是false,说明破坏了单例模式} }

总结

以上是生活随笔为你收集整理的单例模式存在的问题——破坏单例模式,序列化和反射的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。