Java对象序列化和继承

34

假设你有两个类,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);

} 

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


1
你不能在不调用其构造函数的情况下突然创建一个无辜类的实例,因此序列化规范要求调用非可序列化类的构造函数。/ 你可能需要一个序列代理。 - Tom Hawtin - tackline
3个回答

24

Serializable只是给一个类加上“标记接口”。

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

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

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

回答@Sleiman Jneidi在评论中提出的问题,在上述Oracle文档中清楚地说明:

在反序列化期间,将使用类的公共或受保护的无参数构造函数初始化非可序列化类的字段。子类需要访问一个无参数构造函数。可序列化子类的字段将从流中恢复。

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


我知道。但为什么它会调用默认构造函数?我不是在问什么是Serializable? - Sleiman Jneidi
5
@sleimanjneidi 因为 "为了允许非可序列化类的子类型被序列化,子类型可能会承担起保存和恢复超类型公共、受保护和(如果可访问)包字段状态的责任"。这意味着 Bar 应该手动设置 Fooname 字段,因为 Foo 不可序列化。 - fge

4

如果父类没有实现Serializable接口,它将无法进行序列化。因此,在反序列化时,将调用其默认构造函数,并为其所有变量分配默认值。 - Akash5288

0
实际上,当您将父类对象读回来时,它根本没有被序列化...因此,对于非序列化的内容,JVM会再次执行与使用new关键字创建新对象时相同的过程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接