成员未在子类中初始化

时间:2012-02-22 08:15:34

标签: java

//src/com/test/animal/Animal.java
package com.test.animal;

public class Animal
{
    Animal()
    {
        init();
    }

    public void init()
    {
        System.out.println("parent init()");
    }
}

//src/com/test/animal/Dog.java 
package com.test.animal;

public class Dog extends Animal
{
    String name = null;

    Dog()
    {
        super();
    }

    public void init()
    {
        System.out.println("child init()");
        super.init();
        name = new String("dog");
        System.out.println("name: "+name);
    }

    public static void main(String[] args)
    {
        Dog d = new Dog();
        System.out.println("name: "+d.name);
    }
}

输出结果为:

child init()
parent init()
name: dog
name: null

似乎调用了child中的init(),但是没有保存NAME值!为什么? 如果我将NAME移动到父级,那就没关系。然而,保留在孩子身上更合理,因为它是特定于狗的。

另外,我可以在child的构造函数中显式调用init()来解决这个问题。这不是那么好。

2 个答案:

答案 0 :(得分:6)

执行的顺序如下:

  1. 调用Dog构造函数。
  2. 它调用Animal构造函数。
  3. Animal构造函数调用init方法。因为它在Dog中被覆盖,所以会调用Dog版本。
  4. Dog.init中,您将name设置为"dog"
  5. Dog.init方法返回。
  6. 现在,Dog的成员变量已初始化。这会将name设置为null
  7. 结果:name将为null

    一个很好的例子,说明为什么你不应该调用可以从构造函数中重写的方法 - 因为它会导致这样的意外。

    旁注:永远不要这样做:

    // Unnecessarily creating a new String object
    name = new String("dog");
    

    请改为:

    name = "Dog";
    

    永远不必从字符串文字中显式创建新的String对象。

答案 1 :(得分:1)

这是因为首先调用Animal的构造函数,其中name设置为“dog”,然后调用Dog的构造函数,其中name被重置为null。

为了更清楚,您的代码类似于以下内容:

public class Dog extends Animal
{
    String name;

    Dog()
    {
        super();
        name = null;
    }

    public void init()
    {
        System.out.println("child init()");
        super.init();
        name = new String("dog");
        System.out.println("name: "+name);
    }

    public static void main(String[] args)
    {
        Dog d = new Dog();
        System.out.println("name: "+d.name);
    }
}

这是为什么在基类构造函数中调用可覆盖的方法是非常糟糕的做法的原因之一。