Since which moment can the fields of subclass be referred to, in the process of the instance construction?
From the point where the call to the superclass constructor is complete. Remember that prior to that time, instance fields will only have their default values anyway — even if you've used an initializer on them, or an instance initialization block; in both cases, the code to do that is inserted in the constructor(s) for the class, after the superclass constructor call. Consider this pair of classes:
class ParentClass {
ParentClass(String s) {
}
}
class Subclass extends ParentClass {
private String subclassField = "init";
Subclass() {
super("bar");
}
}
if we look at the bytecode of Subclass
via javap -c Subclass
, we see:
class Subclass extends ParentClass {
Subclass();
Code:
0: aload_0
1: ldc #1 // String bar
3: invokespecial #2 // Method ParentClass."":(Ljava/lang/String;)V
6: aload_0
7: ldc #3 // String init
9: putfield #4 // Field subclassField:Ljava/lang/String;
12: return
}
Note that that's compiled our code as though we'd written it like this:
class Subclass extends ParentClass {
private String subclassField;
Subclass() {
super("bar");
this.subclassField = "init"; // *** Note this moved
}
}
(And yes, if you're curious, that code is repeated in every constructor if there's more than one.)
What is the rationale behind "cannot refer to an instance field while invoking a constructor", whereas invoking overriddable method in superclass constructor is okay?
You'd have to ask James Gosling. But consider that unlike instance fields, instance methods have useful values prior to the superclass constructor call, and in fact using a method during construction can be very useful at times. Using an overrideable method is poor practice, however; any methods you use during construction (directly or indirectly) should be either final or private (e.g., effectively final). The compiler doesn't make that distinction for you; perhaps if the rules were being written now, it would.