I stumbled upon this piece of code.
I tried to guess what will be the result of running it before actually doing so.
I was really confused when I saw them & in need of some explanations.
This is the code:
public class A {
String bar = "A.bar";
A() { foo(); }
public void foo() {
System.out.println("A.foo(): bar = " + bar);
}
}
public class B extends A {
String bar = "B.bar";
B() { foo(); }
public void foo() {
System.out.println("B.foo(): bar = " + bar);
}
}
public class C {
public static void main(String[] args) {
A a = new B();
System.out.println("a.bar = " + a.bar);
a.foo();
}
}
The output is:
B.foo(): bar = null
B.foo(): bar = B.bar
a.bar = A.bar
B.foo(): bar = B.bar
Why is that?
- How is
bar = null? - Why is
a.bar = A.bareven appear? I haven't instantiatedAat all. - And if
Aappears, why is it afterB?
Idos
There are a few facts you ought to know before I start explaining every single step in your code's execution:
- Field references are resolved based on reference type and method calls are resolved during run-time (in a dynamic-fashion) based on the object type.
super()is placed implicitly in every constructor even if you don't put it there yourself (it is not called if you callsuper(int x, int y)for instance).- It is considered very bad practice to call "override-able" methods from a constructor - you will see why when we go through the execution.
Now let's break down your code step-by-step:
- You instantiate
Bby calling its default constructorB(). - As I said before, the call to
super()is added implicitly to any constructor, soA()is immediately called. - Inside
A()you callfoo(), which is overridden in classBand that is whyfoo()is called fromB. - Inside
B'sfoo()you get the outputB.foo(): bar = nullsince Java didn't get to initializeB's fields yet (its constructor hasn't been executed yet!) and the fields of object type are initialized tonullby default. - Now that we are done with the constructor of
A()we go back to the constructor ofB(). - Inside said constructor, we have the call to
foo()again, which is againB'sfoo(). But different from last time,Bhave its fields initialized (after the call tosuper()) properly so you get the expectedB.foo(): bar = B.bar. - Now we are back to the warm embrace of
main. - You access
a.bar, and since as I said field references are resolved based on reference type, you get the fieldbarofA. - Lastly, for the same reason, you call
a.foo()which again triggersB'sfoo()which printsb.baronce again.
And we are done! :)
Further references and worthwhile reading materials:
Static and dynamic binding explained
Order of constructor calls
来源:https://stackoverflow.com/questions/34814659/perplexing-output-in-java-with-inheritance-and-overriding-methods