2009-01-27

Java的序列化(serialization)

  不论如何,我们一定会遇到需要要保存状态的时候,一般,面对这种情况有两个比较好的选择:

  1. 将对象serialization,写入文件,之后只需要让程序从文件中对其进行deserialization即可,但是这种情况只适用于只有Java会读取这些数据。
  2. 以某种约定的方式,将某些状态信息写入文本文件

  好,现在考虑序列化的这种情况,一开始先介绍把序列化对象写入文件:

FileOutputStream fs = new FileOutputStream("outfile");

ObjectOutputStream os = new ObjectOutputStream(fs);

os.writeObject(object1);

//调用writeObject方法的时候对象就被serialization成流了。。

os.writeObject(object2);

os.close();

  好吧,过程很简单,对象先被碾平到ObjectOutputStream,之后ObjectOutputStream连接到FileOutputStream,然后FileOutputStream写入文件中。

  从上面的过程我们明白了,序列化只是把对象的实例变量的值压缩成流,保存在文件里而已。那么,现在问题来了,如果我的这个对象有其他对象的引用怎么办?比如有个字符串的实例变量。

  事实上这里面的问题是这样,首先,被序列化的对象必须要能还原会原来的状态,这是序列化存在的意义,第二,序列化对象展开之后存在的内存地址肯定不同了。由此两天可推得,一旦一个对象被序列化了,它所有的实例变量,以及它的实例变量引用的其他变量和这些变量再引用的东西,全部都会被自动序列化保存了。  但是,这里必须要注意的是,如果一个可以序列化的对象被序列化了,那他所包含的实例变量引用的对象也必须能够序列化,否则。额。信息不全显然是废物。。。

  在Java中,要是一个类能够序列化,就需要实现Serializable接口,这个接口其实没有任何方法,只是用以告知JVM这个类的对象是可以序列化的。当然,实现了这个接口只表示这个类的对象能被序列化,但是不一定要被序列化,如果在某种情况下这个对象不需要被序列化,那么只需要加上transient标记即可,比如String类的对象:

import java.net.*;

class test implements Serializable {

transient String temp;//序列化时会以null保存

String storage;//序列化时将被序列化

}

了解了序列化之后,接下来是解序列化,同样,首先还是看它的过程:

FileInputStream fs = new FileInputStream("infile");
ObjectInputStream os = new ObjectinputStream(fs);

//ObjectImputStream负责从数据流中deserialization出对象
Object one = os.readObject();

//每执行一次能够读取出一个被serialization的对象,这个过程同之前序列化的过程一样,如果读取的次数超过拥有的对象个数会抛出异常,readObject方法返回的对象是Object类型的
Object two = os.readObject();
SpecifyClass scone = (SpecifyClass) one;//通过强制类型转换获得对象的引用

SpecifyClass sctwo = (SpecifyClass) two;

os.close();//os关闭时fs也会自动关闭

解序列化有些规则:

  • JVM会通过存储的信息判断出对象的类型
  • 当JVM试着加载对象的类的时候如果JVM无法找到或加载这个类,JVM也会抛出异常
  • 被解序列化的对象的构造函数是不会执行的。但是,如果这个对象有个不可序列化的父类,那么这个这个父类及其在继承树上的所有父类的构造函数都将被执行。因此,对于一个存在不可序列化的父类的可序列化的类来说,它的这些父类的资料全部都会被初始化。
  • 静态变量是不会被序列化的

没有评论: