单例模式存在的问题——破坏单例模式,序列化和反射
生活随笔
收集整理的这篇文章主要介绍了
单例模式存在的问题——破坏单例模式,序列化和反射
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
破坏单例模式:
使上面定义的单例类(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,说明破坏了单例模式} }总结
以上是生活随笔为你收集整理的单例模式存在的问题——破坏单例模式,序列化和反射的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 软件设计原则——合成复用原则
- 下一篇: JDK源码解析-Runtime类