前面我们学习了对象序列化的知识,那么在对象的序列化中,子类和父类实现序列化接口和其构造函数调用规则是什么呢?我们直接通过一段代码来测试一下:
class Father implements Serializable{
public Father() {
System.out.println("这是父类");
}
}
class Son extends Father{
public Son() {
System.out.println("这是子类");
}
}
class Sonson extends Son{
public Sonson() {
System.out.println("这是子类的子类");
}
}
首先我们完成三个类之间的继承关系,Father->Son->Sonson,我们在Father类中实现序列化接口,接下来进行测试,看看每个类的构造函数调用情况:
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\log_network.txt"));
Sonson son=new Sonson();
oos.writeObject(son);
oos.flush();
oos.close();
首先对Sonson对象进行序列化,其次进行反序列化输出对象:
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\log_network.txt"));
Sonson son=(Sonson) ois.readObject();
System.out.println(son);
ois.close();
得到测试结果:
com.objectserialize.Sonson@1b28cdfa
发现该子类的直接父类Sonson以及间接父类Father的构造函数并没有被显式调用
那如果把序列化接口放在子类呢?
class Bar{
public Bar() {
System.out.println("bar");
}
}
class Bar1 extends Bar{
public Bar1() {
System.out.println("Bar1");
}
}
class Bar2 extends Bar1 implements Serializable{
public Bar2() {
System.out.println("Bar2");
}
}
这是序列化接口在子类Bar2中得以实现,再次进行测试,序列化:
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\log_network.txt"));
Bar2 son=new Bar2();
oos.writeObject(son);
oos.flush();
oos.close();
反序列化:
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\log_network.txt"));
Bar2 son=(Bar2) ois.readObject();
System.out.println(son);
ois.close();
测试结果为:
bar
Bar1
com.objectserialize.Bar2@e9e54c2
测试结果显示,当父类没有实现序列化接口时,被显式调用
那么,为什么父类实现序列化接口后,子类的构造函数就不会被显式调用?
我们将序列化的对象写入文件中,由于父类实现了序列化接口,父类对象已经保存到了文件中,在反序列化的过程中,java要构造一个对象,首先要构造其父类对象,由于父类被保存在了文件里,因此不需要再进行显式调用父类的构造函数,而子类继承了父类的序列化接口,因此子类的构造函数也不会被显式调用
子类实现了序列化接口,为什么会调用父类的构造函数呢?
我们将子类对象进行序列化时,由于子类实现了序列化接口而父类并没有,则父类对象也就不会被序列化,因此父类对象没有被保存在文件中,在反序列化的过程中,由于子类对象已经被保存到了文件中,因此子类对象的构造函数不会被显式调用。而父类对象没有被保存,在构造父类对象时需要调用父类对象的构造函数