简体   繁体   English

Java-为什么打印null?

[英]Java- why does it print null?

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);
      }
}

Why does it print "Derived.foo:" 10, nulll and not 20 instead of the null? 为什么打印"Derived.foo:" 10, nulll而不是20而不是null? y is a private variable of Derived, and it was initialized with 20. it's in its scope.. so why is it null? y是Derived的私有变量,它用20初始化。它在其范围内..那么为什么它为null?

Because the super constructor is first called ( super(x) ). 因为首先调用超级构造函数( super(x) )。 This super constructor calls the method foo. 这个超级构造函数调用方法foo。 Then the Derived constructor initializes y to 20 ( this.y = y ). 然后Derived构造函数将y初始化为20( this.y = y )。 So when foo is called, y is not initialized yet. 因此,当调用foo时, y尚未初始化。

It's a bad practice to call overridable methods in constructors, because, as you just noticed, it can call overridden methods on partially constructed objects. 在构造函数中调用可覆盖的方法是一种不好的做法,因为正如您刚才注意到的,它可以在部分构造的对象上调用重写方法。

The println comes from method foo which is called from Base 's constructor, which is called from Derived 's constructor (via super ) before you initialize y . println来自方法foo ,它从Base的构造函数调用, 初始化y 之前Derived的构造函数(通过super )调用。 So the null value is expected. 所以null值是预期的。

It does that because the superclass ( Base ) constructor calls Derived.foo() before the y member variable has been set. 这样做是因为超类( Base )构造函数在设置y成员变量之前调用Derived.foo()

So here is the basic order of operations: 所以这是基本的操作顺序:

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

Then after that 然后在那之后

this.y = y

If you follow the calls from the constructor it makes it clearer: 如果你按照构造函数的调用,它会更清楚:

  1. Calls the Derived(int x, int y) constructor 调用Derived(int x, int y)构造函数
  2. Calls the super constructor Base(int x) 调用超级构造函数Base(int x)
  3. Sets the x variable 设置x变量
  4. Calls the overridden foo() method 调用重写的foo()方法
  5. Performs the println() 执行println()
  6. Then sets the y variable 然后设置y变量

As you can see, the y variable is getting set after you try to print out the value. 如您所见,在您尝试打印出值后,y变量已设置。 That is why you are seeing the uninitialized value of null . 这就是为什么你看到null的未初始化值。

Calling overridable methods from a constructor is a common mistake, as it can allow uninitialized variables to escape before the object is fully constructed. 从构造函数调用可覆盖的方法是一个常见的错误,因为它可以允许未初始化的变量在完全构造对象之前转义。

When you get into the constructor of the super class y is not yet instantiated yet. 当你进入超类的构造函数时, y尚未实例化。 So the call to foo() will have a null y . 所以调用foo()将有一个null y

It is printed by Derived.foo() triggered by Super-Constructor call before it is initialized with 20 afterwards. 它由Derived.foo()打印,由Super-Constructor调用触发,之后用20初始化。 So while printing it is still null. 所以在打印时它仍然是空的。

All the answers are indeed correct but next time you can maybe put a breakpoint in the function where you are printing something. 所有答案都是正确的,但下次你可以在你正在打印东西的函数中设置一个断点。 Then you can simply examine the call stack and folow the code by reading the parts that are being called. 然后,您可以通过读取正在调用的部分来检查调用堆栈并查看代码。

This way you could have traced that y would not be initialized. 这样你就可以追踪y不会被初始化。

Good luck! 祝好运! Roel 罗埃尔

because y has not been initialised when foo() is called in the Base class constructor. 因为在Base类构造函数中调用foo()时y尚未初始化。

the chain goes main -> Derived(x,y) -> Base(x) -> initialise x, foo(). 链转为主 - >派生(x,y) - >基(x) - >初始化x,foo()。 However, because you're starting the call inside Derived, which overrides foo(), derived's foo() is actually executed by the interpreter. 但是,因为你在Derived中启动调用,它会覆盖foo(),所以派生的foo()实际上是由解释器执行的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM