对于Serializable超类,如果我们序列化子类,为什么调用超级构造函数

时间:2015-04-16 20:04:45

标签: java inheritance serialization constructor

import java.io.*;
class Animal implements Serializable
{
    String type;

    Animal()
    {
        System.out.println("Animal's default constructor");
    }
}


class Dog extends Animal
{
    int weight;
    Dog(String type, int weight)
    {
        this.type = type;
        this.weight = weight;
        System.out.println("running the Dog's dual arg'd constructor");
    }
}


class DogSerialized
{
    public static void main(String[] args)
    {
        Dog d = new Dog("hairy",29);
        System.out.println(d.type+"  "+d.weight);
        try{
            FileOutputStream fs = new FileOutputStream("fileSerialized.ser");
            ObjectOutputStream os = new ObjectOutputStream(fs);
            os.writeObject(d);
            os.close();
        }//end of try block
        catch(Exception e){}
        Dog d1 = null;

        try{
            FileInputStream fis = new FileInputStream("fileSerialized.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);
            d1 = (Dog) ois.readObject();
        }//end of try block
        catch(Exception e){}
        System.out.println(d1.type+"  "+d1.weight);
    }
}

运行此代码,我能够看到调用super的构造函数。我不知道为什么/如何调用它。你能否解释一下这种特殊情况(序列化 - 反序列化超级为Serializable的子类实例)

4 个答案:

答案 0 :(得分:1)

它的代码Dog d = new Dog("hairy",29);调用超类构造函数而不是序列化过程。每当您创建子类的对象时,它都会调用其父类的构造函数。

答案 1 :(得分:1)

您的问题与序列化无关。使用new创建任何实例时,构造函数将运行。当对象被反序列化时,它们不会运行。

一个超类的构造函数是隐式调用的,因为编译器会为你插入一个对super();的无参数调用,作为子类构造函数中的第一个语句。要更清楚地理解这个概念,您必须了解构造函数链接。它只不过是在Java中,每个构造函数都会调用构造函数 它的超类隐式调用super();,除非你明确调用this();(请参阅重载的construtors)。

在您的情况下,Dog extends AnimalAnimal extends Object(因为我们在Java中创建的每个类都将扩展Object类。)

现在,当您在main方法中说Dog d = new Dog("hairy",29);时,会执行以下步骤

  1. 首先调用你的Dog构造函数。但它不会初始化 您的变量typeweight
  2. 由于您Dog的构造函数中的第一个语句是super(); (隐式),调用你的Animal类的构造函数。

  3. 您的Animal的构造函数中的第一个语句是super(); (隐式)所以Object类的构造函数被调用。

  4. 此时我们处于堆栈顶部,因为Java中的类Object 在继承层次结构中处于最高级别。所以对象 构造函数完成并弹出堆栈。

  5. 堆栈中的下一个构造函数是Animal。它将完成 super()之后的语句;声明和弹出的 叠加。
  6. 堆栈中的最终构造函数是Dog。它也将完成所有 其中的剩余语句并弹出堆栈。
  7. 来到您的示例,当您致电Dog d = new Dog("hairy",29);时,编译器会插入super();,如下所示

     Dog(String type, int weight)
        {
            super(); //You can also explicitly state this. This will invoke Animal() constructor before going to below statements.
            this.type = type;
            this.weight = weight;
            System.out.println("running the Dog's dual arg'd constructor");
        }
    

    要点:

    1. 任何构造函数中的第一个语句是对...的隐式调用 super();或明确调用this();
    2. 当您明确调用super();时,其中的参数应匹配 使用超类构造函数的参数。例如,如果您的Animal构造函数有2个参数,例如Animal(String type, int weight),并且在Dog构造函数中调用super();,则会出现编译器错误。

    3. 构造函数永远不会被继承。它们无法被覆盖。

答案 2 :(得分:0)

构造函数不在子类中继承。因此,为了创建子类​​对象,首先需要通过调用超级构造函数来创建基类(超类)对象(在您的示例中非常正常)。在创建super之后,调用子类构造函数以添加任何子类字段。对象就像阴离子:内层是超类,外层是子类。

您可以在此article中详细了解它。

答案 3 :(得分:-1)

接口,Serializable与否,对调用构造函数没有任何影响。

子类在创建父类时始终调用它的父元素的构造函数。


您可以尝试将Animal类更改为:

class Animal implements Serializable
{
    String type;

    Animal(String something)
    {
        System.out.println("Animal's default constructor");
    }
}

代码将无法编译,因为在Dog构造函数中,您不会调用其父代的构造函数。