Java中ObjectOutputStream的简单研究(转)

原文地址:Java中ObjectOutputStream的简单研究(转)作者:漫天飞雪


大家都知道在庞大而复杂的java IO体系中,java.io.ObjectOutputStream和java.io.ObjectInputStream这两个类可以方便的实现对象的序列化(Serialize)和反序列化(Deserialize)。但是其中的机制究竟如何,可能没有太多人关注,今天正好有人问我object output stream中的reset是干什么的,就借机会研究一下,就做了以下的简单的实验。

   不过在实验以前先说说我的想法,我以前也很粗略的观察过object output stream序列化一个对象后生成的二进制传,大概就是类名加上一些非静态成员变量的数据,然后用自己的编码规则,也没往心里去。当时的感觉就是应用范围有限,因为这样序列化的对象只能用java自己的Object Input Stream来反序列化,所以就没有跨平台,跨语言的特性了。有时候为了将来能和其他平台通信,我甚至自己为某个类写过序列化和反序列化的方法(object-> byte[] 和byte[] -> object)。但是今天仔细看了一下,发现jdk果然比自己手写的那些简陋的代码要精巧的多。

实验的准备就是一个可以序列化的类:

class Abean implements Serializable {

   private static final long serialVersionUID = -4903107312403938616L;

   String aa;

   String bb;

}

成员变量定义成字符型,是为了查看文件方便。需要注意的就是必须实现Serializable接口,否则直接exception了。

实验代码如下:

   FileOutputStream fos = new FileOutputStream("ff");

   ObjectOutputStream oos = new ObjectOutputStream(fos);

   Abean aa = new Abean();

   aa.aa = "xx";

   aa.bb = "yy";

   for (int i = 0; i < 10; i++) {

       oos.writeObject(aa);

       oos.writeObject(aa);

//     oos.reset();

   }

   fos.close();

   FileInputStream fis = new FileInputStream("ff");

   ObjectInputStream ois = new ObjectInputStream(fis);

   for (int i = 0; i < 10; i++) {

       Abean d = (Abean) ois.readObject();

       System.out.printf("%st%sn", d.aa, d.bb);

   }

测试代码也同样简单,通过控制其中一行来查看前后的区别。

   之所以设计成写入10次同样的对象,就是测试前就猜想,reset文档中所谓的“清除写入的对象的信息”,可能就意味着如果不reset,后面写入的对象能利用之前写入对象的一些信息,来减小写入的数据。

   果然,前后两次实验(注释和不注释其中那行),生成的文件的大小分别为:172字节和794字节。果然和预测一致,看来后面对象利用了前面已经写入的数据,大概后面再写入就加入了如“重复以前写入的某个对象”这样的信息,而不是再写入一次,所以文件大小减小了很多。

   再仔细分析一下两次生成的文件,第二次就是写入了10次同样的信息,其中大概包括了前面说的类名,数据成员类型,每个数据成员的值。而第一次的文件,只写了一次这些信息,之后循环出现了了18次71 00 7E 00 02(16进制的数)这个串。

   再来看下面一个实验,把原来循环语句改成

   for (int i = 0; i < 10; i++) {

       Abean aa = new Abean();

       aa.aa = "xx";

       aa.bb = "yy";

       oos.writeObject(aa);

       oos.writeObject(aa);

//     oos.reset();

   }

   也就是每次都new一个对象出来,这样不reset时文件大小为271字节,每次reset时文件大小为794字节(和之前一样)。

   再仔细看一眼两次不reset的文件,由172字节增加到271字节,一共增加了99个字节。第一个对象的信息结束时,两者是一样大的,而后面重复出现了9次一个21字节的串,也就是21*9 = 189,所以增加了90个字节。

   当然以上实验都是基于感性的认识和分析。由于java虚拟机有不同的实现,所以这套机制必然有详细的文档说明,鉴于时间原因,就不再仔细研究了。

   回到文章开头的那个问题,调用reset之后,会清除所有缓存的对象信息,再传输任何对象都会传输完整的一份内容。这显然会增加IO负担,但有些场景可能有它特殊的用处。

表:各个实验中生成文件大小的对比

实验

文件大小(字节)

reset,只new一次

172

每次rest,new一次

794

reset,每次new

271

每次reset,每次new

794

   总结一下,Object Output/Input Stream这套对象的序列化、反序列化机制确实有了不错的优化,在对象可能重复的情况下,能有效减低IO开销。但是对对象很少不重复的情况下,因为附带了类型信息,就很可能反而增加IO负担了。


你可能感兴趣的:(java)