- 浏览: 58080 次
- 性别:
- 来自: 上海
文章分类
最新评论
反序列化的时候,并不是调用类的构造函数来实现实例的构建,而是通过一种语言之外的对象创建机制来构造对象实例。。从底层源码来看,生成实例时调用了java.reflect.Constructor 的newInstance()方法:
// 用反射生成实例 public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //。。。。。。此处省略。。。。。。。 return (T) constructorAccessor.newInstance(initargs); }
而最后实际上是调用了ConstructorAccessor 的newInstance()方法,而再往下,ConstructorAccessor 调用了本地方法。
由此产生了两个问题:
(一)、通过序列化可以任意创建实例,不受任何限制。那么如何确保单例类反序列化的时候满足单例特性
(二)、由于不调用类自己的构造器,可能通过构造器来保证的一些限制条件就很难满足。比如,有两个参数max,min,构造 时必须满足max>min
下面提供了解决上述两个问题的方法。
先看看单例模式是如何被序列化蹂躏的,再来看看我们解决方案。
// 单例类 public class ASingleton implements Serializable{ private static final long serialVersionUID = -4647546107954516623L; private static final ASingleton INSTANCE = new ASingleton(); private ASingleton(){ System.out.println("construct a ASingleton"); } public static ASingleton getInstance(){ return INSTANCE; } } // 单例测试类 public class TestSingleton { public static void main(String[] args) throws IOException, ClassNotFoundException{ ASingleton s = ASingleton.getInstance(); SerialUtils.writeObject(s, "asingleton.byte"); System.out.println(s); SerialUtils.readObject("asingleton.byte"); } }
测试方法会打印出序列化前对象,和反序列化后的对象,我们看看是否是同一个实例
construct a ASingleton
file size:51bytes
test.serial.resolve.ASingleton@3dfeca64
read form asingleton.byte get test.serial.resolve.ASingleton@457471e0
可以看出来不是同一个实例。这个问题的解决方案是:可以通过覆写readResolve 这个方法来实现,readResolve 会在反序列化时调用,当从流中获取信息重建对象生成实例后,将会调用这个方法,将生成的实例替换成我们想要的实例,看代码:
// 单例类覆写了readResolve public class ASingleton implements Serializable{ // 此处省略100 个字 private Object readResolve(){ System.out.println("resolve instance"); return INSTANCE;} }
可以看出,我们覆写了这个方法,并且将那个唯一的实例(INSTANCE)返回,从而保证了单例。看
看运行结果证明我们的想法:
construct a ASingleton
file size:51bytes
test.serial.resolve.ASingleton@3dfeca64
resolve instance
read form asingleton.byte get test.serial.resolve.ASingleton@3dfeca64
可以看出是一个实例,并且readResolve 也被调用了。
到底替换实例的过程是怎样的呢,我们在jdk 源码中寻求答案:
// 源码中是如何使用readResolve的 private Object readOrdinaryObject(boolean unshared) throws IOException{ // ... ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize(); Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } // .... if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); // … if (rep != obj) { handles.setObject(passHandle, obj = rep); } } return obj; }
从代码可以看出,原理是这样的:
(1)首先会从流中读取信息,重建对象obj
(2) 判断是否覆写了readResolve 方法,如果没有覆写则直接返回obj
(3) 如果覆写了readResolve 方法,则调用readResolve 方法返回实例rep,如果rep与obj不相等,则用rep 替换obj,最后返回obj替换过程就是这样的,至此,单例问题解决了。
最后看看第二个问题如何解决。第二个问题的解决思路有两个,一个是覆写readObject 并且在方法里校验我们的约束信息,如果有问题就抛出异常。代码如下:
// 利用readObject 来防攻击 public class Range implements Serializable{ // 此出省略若干字 private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException{ s.defaultReadObject(); int max = this.max; int min = this.min; if(max < min){ throw new IllegalArgumentException("max must bigger than min"); } } // …. }
另一个解决思路是让对象实现ObjectInputValidation中的public void validateObject() throws InvalidObjectException 方法。在该方法中校验反序列化之后得实例必须满足的某些特性。如不满足则抛出相应的异常。代码如下:
序列化类的代码如下:
package zheyuan.experiment4.com; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectInputValidation; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class ValidationPerson implements Serializable,ObjectInputValidation { private static final long serialVersionUID = 5497993111607600169L; private String firstName; private String lastName; private List<String> lis=new ArrayList<String>(); public ValidationPerson(){ lis.add("validationPerson"); } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public List<String> getLis() { return lis; } public void setLis(List<String> lis) { this.lis = lis; } public void validateObject() throws InvalidObjectException { //这里的这个异常是肯定会抛出来的,只是为了演示该方法的使用 if(this.lis.contains("validationPerson")){ throw new InvalidObjectException("validationPerson缺失!"); } } //注册验证对象 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.registerValidation(this, 1); in.defaultReadObject(); } }
测试类的代码如下:
package zheyuan.experiment4.com; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; public class ValidationSerializableTest { /** * 反序列化的时候通过继承实现接口ObjectInputValidation中的方法校验反序列化之后类的某些特性 * @param args */ public static void main(String[] args) throws Throwable{ ValidationPerson vp=new ValidationPerson(); //序列化 OutputStream out=new FileOutputStream("validation_sec"); ObjectOutputStream oos=new ObjectOutputStream(out); oos.writeObject(vp); //反序列化 InputStream ins=new FileInputStream("validation_sec"); ObjectInputStream ois=new ObjectInputStream(ins); ValidationPerson vpNew=(ValidationPerson)ois.readObject(); System.out.println(vpNew.getLis().get(0)); } }
运行结果如下:
Exception in thread "main" java.io.InvalidObjectException: validationPerson缺失! at zheyuan.experiment4.com.ValidationPerson.validateObject(ValidationPerson.java:50) at java.io.ObjectInputStream$ValidationList$1.run(ObjectInputStream.java:2207) at java.security.AccessController.doPrivileged(Native Method) at java.io.ObjectInputStream$ValidationList.doCallbacks(ObjectInputStream.java:2203) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:358) at zheyuan.experiment4.com.ValidationSerializableTest.main(ValidationSerializableTest.java:27)
发表评论
-
JVM参数以及调优
2011-10-24 23:39 838一、JVM配置参数中文说明: 1、-Xmixed ... -
分布式服务框架之NIO(一)
2011-10-22 23:08 1348NIO在实现分布式服务框架中非阻塞高并发的服务器端功能 ... -
Spring声明线程池配置示例
2011-09-13 14:19 770<bean id="***Ta ... -
HTTPS下载的问题
2011-08-31 17:18 1767遇到一个很恶心的问题,开发以及测试环境是http协议,下 ... -
实现自己的csv文件解析引擎
2011-08-26 14:29 1492前言: 这里仅仅支持Excel文件导出的CSV文件,解 ... -
迷茫了
2011-08-08 23:09 770又迷茫了,感觉没事可做,什么都不想做 实际并不是没事可做,实 ... -
通用的excel报表生成工具类
2011-07-20 20:41 1155下面这个工具类是今天半天的劳动成果。 以后自己也可能用得到。 ... -
正则表达式元字符总结
2011-07-14 23:22 956正则表达式元字符总结如下: 点号(.):任何单字符的通 ... -
多并发情况下日志信息中如何区分不同线程(客户端)调用
2011-07-13 21:57 1433在企业开发中,常常会遇到这样的需求:通过一个唯一标 ... -
windows下perl开发环境搭建
2011-07-09 22:07 69361、下载并安装ActivePerl,貌似不需要额外的配置,一步 ... -
如何让右键菜单出现“命令行在这里”,即cmd here
2011-06-29 00:24 4750要在命令行下跳转到某个嵌套很深的目录下时,使用cd命令等比 ... -
使用JMock简介
2011-06-28 13:36 987一、常用关键字 one ... -
利用数据库锁实现简单的防并发编程
2011-06-22 23:27 1134大约有两类情况: 1、一个程序代码块同一时刻只允许一个 ... -
程序员如何减少BUG
2011-06-21 22:04 2541最近一个项目出了大量的BUG,很是惭愧,有没有可以尽量规避BU ... -
一些常用的正则表达式(项目中经常用到)
2011-06-15 17:11 8148最近做的一个内部系统项目,涉及大量的文本校验,里面用到了一些常 ... -
解析Excel文件转换科学计数法字符串为正常数字
2011-06-14 22:55 5685问题出现的情形是这样的: excel文件中某个字段,既 ... -
js正则表达式去除表单提交字符串前后的空格
2011-06-13 19:31 1476str为表单提交数据。 str=str.replace ... -
debug容器启动类报MMO异常解决办法
2011-06-10 19:58 748类似的,在eclipse的VM arguments中设置参数: ... -
理想的设计特征
2011-06-09 00:57 738其实下面这些是来自于代码大全,觉得讲得实在是精辟无比,想摘抄下 ... -
最近的几点关于编码的心得
2011-05-25 00:35 6841、编写一个类或者新建一个变量的时候,名字问题需要仔细的斟酌斟 ...
相关推荐
书中将解释HTTP协议,Web API的控制器和路由,以及数据序列化等。 5. **ASP.NET Core**:第二版还可能涵盖了ASP.NET Core,这是跨平台的下一代ASP.NET框架。ASP.NET Core集成了Web Forms、MVC和Web API,具有高性能...
#### 二、揭秘 DataSet 序列化的内幕 在Web Service中,DataSet作为常用的数据容器,在数据传输过程中扮演着重要角色。然而,原始的DataSet序列化方式存在较大的数据冗余,尤其是在包含大量数据时,这不仅增加了...
开发者将学习如何利用Spring MVC,结合@Controller、@RequestMapping等注解定义HTTP端点,以及如何使用Jackson库进行JSON序列化和反序列化。此外,还会介绍Spring Data JPA和Hibernate,用于简化数据库操作,实现ORM...
- 读写文件:MATLAB可以通过fread、fwrite、textscan等函数读取二进制或文本文件,save和load函数用于序列化和反序列化MATLAB变量。 - 数据导入导出:可以将数据导入到MATLAB工作空间,或者导出为其他格式(如CSV...
- **线粒体**:细胞的能量工厂,通过氧化磷酸化过程产生能量。 ### 三、分子生物学 #### DNA与遗传 - **DNA结构**:双螺旋结构,由四种碱基组成(腺嘌呤A、鸟嘌呤G、胞嘧啶C、胸腺嘧啶T)。 - **遗传密码**:DNA上...
对于网络编程,书中会介绍如何使用HTTP协议进行数据交换,以及使用JSON或XML格式进行数据序列化和反序列化。同时,还会讲解如何实现异步任务处理,比如使用AsyncTask或Handler,以避免阻塞UI线程。 进一步,书中的...
7. **媒体类型格式化**:Web API可以自动序列化和反序列化数据,使其能以不同的格式(如JSON或XML)传输。这部分将讨论如何处理不同的数据格式。 8. **测试与调试**:介绍如何测试Web API服务,包括单元测试、集成...
1. **统计分析**:MATLAB提供了丰富的统计函数,用于数据的描述性统计、假设检验、回归分析、时间序列分析等。例如,`mean()`、`std()`用于计算平均值和标准差,`histogram()`绘制直方图,`corrcoef()`计算相关系数...
在软件开发领域,Hessian是一种高效的二进制序列化协议,它由Caucho Technology公司开发,常用于实现远程方法调用(RMI)和Web服务。本次我们将深入探讨Hessian 4.0.7版本,包括其jar包的使用以及源码分析,帮助...
当多个通道的转换信号被用于调制并出现在最终的数字化输出中时,就会出现杂散信号。 在交错ADC系统中,即使输入信号是正弦波,也会出现增益杂散。若ADC之间存在增益误差失配,即两个ADC的增益不相等,那么交错的...
- JSON解析:介绍如何使用Gson、Jackson或org.json库解析和序列化JSON数据。 通过以上章节的学习,开发者可以掌握Android应用开发的基础和进阶技巧,包括界面设计、程序流程控制、数据管理和网络通信等关键领域。...
开发者将了解如何使用HttpURLConnection或OkHttp库发送网络请求,以及如何处理JSON数据,如使用Gson或Jackson库进行序列化和反序列化。 第14章可能会讲解Android的权限管理和安全相关的话题。随着Android系统的更新...
1. 内存管理:通过Tungsten项目,Spark实现了自定义的内存管理,优化了数据序列化和反序列化,减少GC开销。 2. 数据本地性:Spark会尽可能地将任务调度到数据所在的节点执行,提高数据读取速度。 3. 执行计划优化...
这一章可能详细讲解了使用HTTP/HTTPS进行网络请求,如使用Android的HttpURLConnection、OkHttp库或者Retrofit库进行网络请求,还有JSON解析(Gson或Jackson)以及数据的序列化和反序列化。 4. **第14章**:可能涉及...
5. **Effect模块**:Effect模块提供了高级动画效果,如Parallel(并行效果)、Sequence(序列效果)和Transition(过渡效果),可以创建复杂的动画序列。 通过学习《Prototype和script.aculo.us终极揭秘》这本书,...
这几个附录远远超出了简单的汇编语言参考向导,讲述了公共代码段(common code fragments)和常用编译器对几种典型的代码序列表现出来的编译器习性(complier idioms),并介绍了识别和破解它们的方法。
通过序列化Java对象,GWT可以自动处理客户端与服务器间的类型转换和通信细节。 6. **源码分析** 书中的"part1,2,3"可能分别涵盖了GWT的基础、进阶和实战内容。通过源码学习,开发者可以了解GWT内部如何处理事件、...
- **数据序列化**:选择高效的序列化器,如BinaryFormatter或DataContractSerializer。 - **缓存策略**:利用缓存减少不必要的服务调用。 本实例通过VS2010展示了WCF的基本用法,包括服务创建、调用和服务配置。...
例如,信号处理工具箱可用于分析和处理时间序列数据,图像处理工具箱则适用于图像分析和处理任务。 为了提升MATLAB的可扩展性,书中的高级章节可能会讨论MATLAB与外部程序(如C、C++或Fortran)的接口,以及如何...