Java学习杂谈(四) - 编程入门网
Java学习杂谈(四)时间:2011-01-26 blogjava Gay Bird1.关于序列化和反序列化 应该大家都大概知道Java中序列化和反序列化的意思,序列化就是把一个Java对象转换成二进制进行磁盘上传输或者网络流的传输,反序列化的意思就是把这个接受到的二进制流重新组装成原来的对象逆过程。它们在Java中分别是通过ObjectInputStream和 ObjectInputStream这两个类来实现的(以下分别用ois和oos来简称)。 oos的writeObject()方法用来执行序列化的过程,ois的readObject()用来执行反序列化的过程,在传输二进制流之前,需要讲这两个高层流对象连接到同一个Channel上,这个Channel可以是磁盘文件,也可以是socket底层流。所以无论用哪种方式,底层流对象都是以构造函数参数的形式传递进oos和ois这两个高层流,连接完毕了才可以进行二进制数据传输的。例子: 可以是文件流通道 file = new File(“C:/data.dat”); oos = new ObjectOutputStream(new FileOutputStream(file)); ois = new ObjectInputStream(new FileInputStream(file)); 或者网络流通道 oos = new ObjectOutputStream(socket.getOutputStream()); ois = new ObjectInputStream(socket.getInputStream()); 不知道大家是否注意到oos总是在ois之前定义,这里不希望大家误解这个顺序是固定的么?回答是否定的,那么有顺序要求么?回答是肯定的。原则是什么呢? 原则是互相对接的输入/输出流之间必须是output流先初始化然后再input流初始化,否则就会抛异常。大家肯定会问为什么?只要稍微看一看这两个类的源代码文件就大概知道了,output流的任务很简单,只要把对象转换成二进制往通道中写就可以了,但input流需要做很多准备工作来接受并最终重组这个Object,所以ObjectInputStream的构造函数中就需要用到output初始化发送过来的header信息,这个方法叫做readStreamHeader(),它将会去读两个Short值用于决定用多大的缓存来存放通道发送过来的二进制流,这个缓存的size因jre的版本不同是不一样的。所以output如果不先初始化,input的构造函数首先就无法正确运行。 对于上面两个例子,第一个顺序是严格的,第二个因为oos和ois连接的已经不是对方了,而是socket另外一端的流,需要严格按照另外一方对接的output流先于对接的input流打开才能顺利运行。 这个writeObject和readObject本身就是线程安全的,传输过程中是不允许被并发访问的。所以对象能一个一个接连不断的传过来,有很多人在运行的时候会碰到EOFException,然后百思不得其解,去各种论坛问解决方案。其实笔者这里想说,这个异常不是必须声明的,也就是说它虽然是异常,但其实是正常运行结束的标志。EOF表示读到了文件尾,发送结束自然连接也就断开了。如果这影响到了你程序的正确性的话,请各位静下心来看看自己程序的业务逻辑,而不要把注意力狭隘的聚集在发送和接受的方法上。因为笔者也被这样的bug困扰了1整天,被很多论坛的帖子误解了很多次最后得出的教训。如果在while循环中去readObject,本质上是没有问题的,有对象数据来就会读,没有就自动阻塞。那么抛出EOFException一定是因为连接断了还在继续read,什么原因导致连接断了呢?一定是业务逻辑哪里存在错误,比如NullPoint、ClassCaseException、ArrayOutofBound,即使程序较大也没关系,最多只要单步调适一次就能很快发现bug并且解决它。 难怪一位程序大师说过:解决问题90%靠经验,5%靠技术,剩下5%靠运气!真是金玉良言,笔者大概查阅过不下30篇讨论在while循环中使用 readObject抛出EOFExceptionde 的帖子,大家都盲目的去关注解释这个名词、反序列化的行为或反对这样写而没有一个人认为EOF是 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |