33

假设你有这两个类,Foo 和 Bar,其中 Bar 扩展 Foo 并实现Serializable

class Foo {

public String name;

public Foo() {
    this.name = "Default";
}

public Foo(String name) {
    this.name = name;
}
}

class Bar extends Foo implements java.io.Serializable {

public int id;

public Bar(String name, int id) {
    super(name);
    this.id = id;
}
}

请注意, Foo 没有实现Serializable. 那么当 bar 被序列化时会发生什么?

    public static void main(String[] args) throws Exception {

    FileOutputStream fStream=new FileOutputStream("objects.dat");
    ObjectOutputStream oStream=new ObjectOutputStream(fStream);
    Bar bar=new Bar("myName",21);
    oStream.writeObject(bar);

    FileInputStream ifstream = new FileInputStream("objects.dat");
    ObjectInputStream istream = new ObjectInputStream(ifstream);
    Bar bar1 = (Bar) istream.readObject();
    System.out.println(bar1.name + "   " + bar1.id);

} 

它打印“默认 21”。问题是,为什么在类未序列化时会调用默认构造函数?

4

3 回答 3

24

Serializable 只是给定类的“标记接口”。

但是该类必须遵守某些规则:

http://docs.oracle.com/javase/1.5.0/docs/api/java/io/Serializable.html

为了允许序列化不可序列化类的子类型,子类型可以负责保存和恢复超类型的公共、受保护和(如果可访问)包字段的状态。仅当它扩展的类具有可访问的无参数构造函数来初始化类的状态时,子类型才可以承担此责任。如果不是这种情况,则声明类 Serializable 是错误的。

回答@Sleiman Jneidi 在评论中提出的问题,在上面提到的 oracle 文档中,它明确提到

在反序列化过程中,不可序列化类的字段将使用类的公共或受保护的无参数构造函数进行初始化。可序列化的子类必须可以访问无参数构造函数。可序列化子类的字段将从流中恢复。

因此,调用的类 Foo 的默认无参数构造函数导致初始化。

于 2011-12-25T23:33:44.057 回答
4

可能是defaultWriteObject 只能写入当前类的非静态和非瞬态字段。一旦超类没有实现 Serializable 接口,就无法将超类中的字段序列化到流中。

于 2013-08-07T13:13:46.370 回答
0

实际上,当您将读取父类对象时,因为它根本没有序列化......所以对于非序列化的事情,JVM 再次经历与我们使用 new 关键字创建新对象时相同的过程。

于 2013-12-25T05:48:09.227 回答