标签:
Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并可以在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可以通过网络进行(这对于实现远程调用-RMI是非常重要的)。这同样意味着序列化机制能弥补不同操作系统间的差异,也就是说,可以在运行Windows系统的计算机上创建一个对象,将其序列化,通过网络将它发送给一台运行Unix系统的计算机,然后在那里准确的重新组装。为了序列化一个对象,首先要创建某些OutputStream对象,然后将其封装在一个ObjectOutputStream对象内,这时,只需要调用writeObject()即可将对象序列化,并将其发送给OutputStream,要将一个序列重组为一个对象,需要将一个InputStream封装在ObjectInputStream内,然后调用readObject(),并将对象进行向下转型。下面是一个例子:
File file = new File();
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
Person person = new Person("John", 101, Gender.MALE);//实现了Serializable接口
oout.writeObject(person);
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Object newPerson = oin.readObject(); // 没有强制转换到Person类型
oin.close();
System.out.println(newPerson);
上述过程中我们采用的是缺省的序列化机制,我们并没有对序列化进行控制。现在假设我们出于安全的考虑,不希望对象的某一部分被序列化,那么我们该怎么办呢?在这种情况下,其实我们可以通过实现Externalizable接口代替实现Serializable接口来对序列化过程进行控制。Externalizable接口继承了Serializable接口,同时增添了两个方法:writeExternal()和readExternal()。这两个方法会在序列化和重组的过程中被自动调用,以便执行一些特殊操作。实际上,当我们恢复一个可序列化(Serializab le)对象时,对象完全以它存储的二进制为基础重组,而不调用构造器,而对一个Externalizable对象,所有普通的缺省构造器都会被调用,然后调用readExternal()。注意这些机制的契合:我们在writeExternal()方法中将来自于对象的重要信息写入,然后在readExternal()方法中恢复数据,由于重组时会自动调用缺省的构造器,那些没有在明确在readExtenal()方法中恢复的数据就会采用来自缺省构造器的值,这就让我们能够指定对哪些值进行序列化。 Externalizable实际是声明说:“关闭自动序列化吧,需要序列化的部分我自己来动手”。如果我们操作的是一个Serializable对象,那么所有的序列化操作都会自动进行,此时,实际上我们还是可以对其予以控制,那就是用transient(瞬时)关键字逐个域地关闭序列化。 如果我们不是特别想实现Externalizable接口,那么还有另一种方法-我们可以实现Serializable接口,并添加writeObject()和readObject()方法。这两个方法必须具有明确的方法签名如下:
private void writeObject(ObjectOutputStream stream)throws IOException;
private void readObject(ObjectInputStream stream)throws IOException, ClassNotFoundException;
注意,这些方法被定义为了private,但是我们并不在这个类的其他方法中调用它们,反而是ObjectOutputStream和ObjectInputStream对象的writeObject()和readObject()方法调用我们对象的writeObject()和readObject()方法。它的实际工作步骤是这样的:在你调用ObjectOutputStream.writeObject()时,会检查你所传递的Serializable对象,看看是否实现了它自己的writeObject(),如果是这样,就跳过正常的序列化过程并调用它的writeObject()。readObject()的情形与此相同。另外,在我们的writeObject()内部,我们可以调用defaultWriteObject()来选择执行缺省的writeObject()。类似的,在readObject()内部,我们可以调用defaultReadObject()。 另外,我们还需要注意transient关键字,对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。
标签:
原文地址:http://www.cnblogs.com/shiercifang/p/4739153.html