Externalizable 的替代方法

如果我们不是特别想要实现 Externalizable 接口,那么就还有另一种方法。我们可以实现

Serializable 接口,并添加(注意我说的是“添加”,而非“重载”或者“实现”)名为

writeObject()和 readObject()的方法。这样一旦对象被序列化或者被反序列化,就会自

动地分别调用这两个方法。也就是说,只要我们提供了这两个方法,就会使用它们而不是缺

省的序列化机制。

 

这些方法必须具有准确的方法签名:

 

private voidwriteObject(ObjectOutputStream stream)

throwsIOException;

 

private voidreadObject(ObjectInputStream stream)

throwsIOException, ClassNotFoundException

 

从设计的观点来看,现在事情变得真是不可思议。首先,我们可能会认为由于这些方法不是

基类或者Serializable 接口的一部分,所以应该在它们自己的接口中进行定义。但是注意

它们被定义成了private,这意味着它们仅能被这个类的其他成员调用。然而,实际上我们

并没有从这个类的其他方法中调用它们,而是 ObjectOutputStream   和

ObjectInputStream 对象的 writeObject( )和 readObject( )方法调用我们对象的

writeObject( )  和 readObject( )方法(注意关于这里用到的相同方法名,我尽量抑制住

不去谩骂。一句话:混乱)。你可能想知道 ObjectOutputStream    和 ObjectInputStream

对象是怎样访问你的类中的 private 方法的。我们只能假设这正是序列化神奇的一部分。

 

在任何情况下,在接口中定义的所有东西都自动是 public 的,因此如果 writeObject( )     和

readObject( )必须是 private 的,那么它们不会是接口的一部分。因为我们必须要完全遵

循其方法签名,所以其效果就和实现了接口一样。

 

在你调用ObjectOutputStream.writeObject()时,会检查你所传递的 Serializable 对

象,看看是否实现了它自己的 writeObject( )。如果是这样,就跳过正常的序列化过程并

调用它的writeObject( )。readObject( )的情形与此相同。

 

还有另外一个技巧。在我们的 writeObject()内部,可以调用 defaultWriteObject()来选

择执行缺省的writeObject()。类似地,在 readObject()内部,我们可以调用

defaultReadObject()。下面这个简单的例子演示了如何对一个 Serializable 对象的存储

与恢复进行控制:

 

//:c12:SerialCtl.java

// Controllingserialization by adding your own

// writeObject()and readObject() methods.

importcom.bruceeckel.simpletest.*;

importjava.io.*;

public class SerialCtlimplements Serializable {

    private static Test monitor = new Test();

  private String a;

    private transient String b;

    public SerialCtl(String aa, String bb) {

        a = "Not Transient: " + aa;

        b = "Transient: " + bb;

    }

    public String toString() { return a +"\n" + b; }

  private void writeObject(ObjectOutputStreamstream)

  throws IOException {

    stream.defaultWriteObject();

    stream.writeObject(b);

    }

    private void readObject(ObjectInputStreamstream)

    throws IOException, ClassNotFoundException{

    stream.defaultReadObject();

    b = (String)stream.readObject();

    }

    public static void main(String[] args)

    throws IOException, ClassNotFoundException{

        SerialCtl sc = newSerialCtl("Test1", "Test2");

    System.out.println("Before:\n" +sc);

    ByteArrayOutputStream buf= newByteArrayOutputStream();

        ObjectOutputStream o = newObjectOutputStream(buf);

    o.writeObject(sc);

    // Now get it back:

    ObjectInputStream in = new ObjectInputStream(

      newByteArrayInputStream(buf.toByteArray()));

    SerialCtl sc2 = (SerialCtl)in.readObject();

    System.out.println("After:\n" +sc2);

    monitor.expect(new String[] {

     "Before:",

     "Not Transient: Test1",

     "Transient: Test2",

          "After:",

     "Not Transient: Test1",

     "Transient: Test2"

        });

    }

} ///:~

在这个例子中,有一个 String 域是普通域,而另一个是 transient(瞬时)域,用来证明

非 transient 域由 defaultWriteObject()方法保存,而 transient 域必须在程序中明确保

存和恢复。域是在构造器内部而不是在定义处进行初始化的,以此可以证实它们在反序列化

期间没有被一些自动机制初始化。

 

如果我们打算使用缺省机制写入对象的非 transient 部分,那么我们必须调用

defaultWriteObject()作为 writeObject( )中的第一个操作,并让defaultReadObject()

作为 readObject( )中的第一个操作。这些都是奇怪的方法调用。例如,如果我们正在为

ObjectOutputStream调用 defaultWriteObject( )并且没有传递任何参数,然而不知何

故它却可以运行,并且知道对象的引用以及如何写入非 transient 部分。奇怪之极。

 

对 Transient 对象的存储和恢复使用到了我们比较熟悉的代码。请再考虑一下在这里发生

了什么事情。在main()中,创建 SerialCtl 对象,然后将其序列化到 ObjectOutputStream

里(注意在这种情况下,使用的是缓冲区而不是文件——这对于 ObjectOutputStream 来

说则是完全一样的)。序列化发生在下面这行代码当中:

 

o.writeObject(sc);

 

writeObject()方法必须检查 sc,判断它是否拥有自己的 writeObject()方法(不是检查接

口——这里根本就没有接口,也不是检查类的类型,而是利用反射来来真正地搜索方法)。

如果有,那么就会使用它。对 readObject()也采用了类似的方法。或许这是解决这个问题

唯一切实可行的方法,但它确实有点古怪。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值