Calling overridable methods in constructors in Java
Subclass fields are not yet initialized — a classic trap.
String name = "Kai";. You print it and see null. The initializer didn't fail — it simply hadn't run yet.Recall the order
Creating a Kid: 1. the parent constructor runs; 2. Kid's field initializers run; 3. Kid's constructor body runs. During step 1, Kid's fields still hold their defaults: 0, false, null.
Dispatch works in constructors too
While Base() runs, the object already is a Kid. So if Base() calls an overridable method, Kid's override runs — before Kid's fields are initialized. It sees the defaults.
Say hello too early
What does this print?
class Base {
Base() { hello(); }
void hello() { }
}
class Kid extends Base {
String name = "Kai";
void hello() {
System.out.println("Hi " + name); }
}
void main() { new Kid(); }Hi KaiHi nullNothing
Show the answer
Hi null. Base() dispatches to Kid's hello(), but name = "Kai" only runs after the parent constructor returns.
A list that isn't there yet
What happens?
class Base {
Base() { init(); }
void init() { }
}
class Kid extends Base {
List<String> items = new ArrayList<>();
void init() { items.add("x"); }
}
void main() { new Kid(); }Runs fineCompile errorThrows NullPointerException
Show the answer
It throws **NullPointerException**. Kid.init() runs from Base's constructor while items is still null. The same timing also explains "0 then 5" puzzles with int fields.
Calling from a constructor
class Base {
Base() { init(); }
void init() { ... }
}Any subclass can hijack init() before it's ready.
class Base {
Base() { init(); }
private void init() { ... }
}Only call private, final or static methods from constructors — they can't be overridden.
Java 25's escape hatch
Flexible constructor bodies (JEP 513) let a subclass **assign its own fields before super(...)**. Then even an overridden method called from the parent constructor sees the real value.
class Kid extends Base {
final String msg;
Kid() {
msg = "hi"; // before super!
super();
}
void show() { System.out.println(msg); }
}In real projects
*Effective Java* states it bluntly: constructors must not invoke overridable methods. IntelliJ and Sonar flag "overridable method call in constructor", because these bugs hide until someone writes a subclass — often months later, on another team.
Key takeaways
- Overrides run even when called from a parent constructor
- Subclass field initializers haven't run yet: fields are 0/null
- Call only private, final or static methods from constructors
- Java 25: fields assigned before super(...) are already set
Twist: declare the field as final String name = "Kai"; and it prints Hi Kai. A final field with a constant value is inlined by the compiler, so the uninitialized field is never read.
Practice questions
What does this print?
class Base {
Base() { show(); }
void show() { System.out.println("base"); }
}
class Kid extends Base {
String msg = "hello";
void show() { System.out.println(msg); }
}
void main() { new Kid(); }- base
- hello
- null
- Throws NullPointerException
Check your answer
null. Base() runs first and dispatches to Kid.show(). Kid's field initializer msg = "hello" hasn't run yet, so msg is still null.
What does this print?
class Base {
Base() { System.out.println(size()); }
int size() { return 1; }
}
class Kid extends Base {
int n = 5;
int size() { return n; }
}
void main() { System.out.println(new Kid().size()); }- 1 5
- 0 5
- 5 5
- 1 1
Check your answer
0 5. During Base(), Kid.size() runs while n is still 0. Once construction finishes, n is 5.