Class C
overrides A.foo()
, and polymorphism is active even in a constructor in Java. Therefore when the constructor in A
calls foo()
when we're building an instance of C
, it's C.foo()
that actually gets called.
C.foo()
in turn prints out B.i
, so we might expect 6
to be printed out - but instance variable initializers are only executed after superclass constructors, so at the point of execution, B.i
is 0.
Basically, the order of execution of a constructor is:
- Execute chained constructors, either explicitly
this(...)
to chain to another constructor in the same class, or explicitly super(...)
to chain to a constructor in the superclass, or implicitly super()
to chain to a parameterless constructor in the superclass.
- For constructors which have chained to a superclass constructor, execute the variable initializers.
- Execute the code in the constructor body
Rewriting the code to avoid using variable initializers and variable shadowing makes this clearer, while still keeping the code equivalent:
public class A {
int ai;
public A() {
super();
ai = 5;
foo();
}
public void foo() {
System.out.println(ai);
}
}
class B extends A {
int bi;
public B() {
super();
bi = 6;
}
}
class C extends B {
int ci;
public C() {
super();
ci = 7;
}
public void foo() {
System.out.print(bi);
}
public static void main(String[] args) {
C c = new C();
}
}
As an aside, the "variable hiding" part of this doesn't come into play if you make all your fields private to start with, which is what I'd recommend. That just leaves the issues of calling virtual methods from a constructor, which is generally a bad idea due to expecting an object to be able to work before it's had chance to fully initialize, and the perhaps-surprising timing of variable initializer execution.
If you avoid calling virtual methods from constructors, even the timing of variable initializers becomes irrelevant - at least almost always.