Java-为什么打印null?

时间:2011-09-06 13:08:26

标签: java runtime

public class Base {
public Base() {
    x = 0;
    bar();
}

public Base(int x) {
    this.x = x;
    foo();
}

public void foo() {
    System.out.println("Base.foo : " + x);
}

private void bar() {
    System.out.println("Base.bar:" + x.toString());
}

protected Integer x;
}

    public class Derived extends Base {
       public Derived() {
       bar();
       }
       public Derived(int x, int y) {
         super(x);
         this.y = y;
       }
       public void foo() {
         System.out.println("Derived.foo : " + x + ", " + y);
       }
       public void bar() {
         System.out.println("Derived.bar:" + x.toString() + ", " + y.toString());
       }
       private Integer y;


       public static void main(String[] args) {
        Base b = new Derived(10, 20);
      }
}

为什么打印"Derived.foo:" 10, nulll而不是20而不是null? y是Derived的私有变量,它用20初始化。它在它的范围内..那么为什么它为null?

8 个答案:

答案 0 :(得分:7)

因为首先调用超级构造函数(super(x))。这个超级构造函数调用方法foo。然后Derived构造函数将y初始化为20(this.y = y)。因此,当调用foo时,y尚未初始化。

在构造函数中调用可覆盖的方法是一种不好的做法,因为正如您刚才注意到的,它可以在部分构造的对象上调用重写方法。

答案 1 :(得分:2)

println来自方法foo,它是从Base的构造函数调用的,它是从Derived的构造函数调用的(通过super之前初始化y。所以期望空值。

答案 2 :(得分:2)

这样做是因为超类(Base)构造函数在设置Derived.foo()成员变量之前调用y

所以这是基本的操作顺序:

main(...)
Derived(10,20) (start constructor)
Base(10) (start constructor)
this.x = x
foo() (calls Derived.foo() which prints the message you see)

之后

this.y = y

答案 3 :(得分:2)

如果您按照构造函数的调用,它会更清楚:

  1. 调用Derived(int x, int y)构造函数
  2. 调用超级构造函数Base(int x)
  3. 设置x变量
  4. 调用重写的foo()方法
  5. 执行println()
  6. 然后 设置y变量
  7. 如您所见,在您尝试打印出值后,y变量已设置。这就是为什么你看到null的未初始化值。

    从构造函数中调用可覆盖的方法是一个常见的错误,因为它可以在完全构造对象之前允许未初始化的变量转义。

答案 4 :(得分:1)

当你进入超级类y的构造函数时,尚未实例化。因此,对foo()的调用将包含null y

答案 5 :(得分:1)

由Super-Constructor调用触发的Derived.foo()打印,之后用20初始化。所以在打印时它仍然是空的。

答案 6 :(得分:1)

所有的答案确实都是正确的,但下次你可以在打印东西的函数中设置一个断点。 然后,您可以通过读取正在调用的部分来检查调用堆栈并查看代码。

这样你就可以追踪y不会被初始化。

祝你好运! 罗埃尔

答案 7 :(得分:1)

因为在Base类构造函数中调用foo()时y尚未初始化。

链条走了 主要 - >派生(x,y) - >基数(x) - >初始化x,foo()。但是,因为你在Derived中启动调用,它会覆盖foo(),派生的foo()实际上是由解释器执行的。