constructors and init order
the exact order fields, initializers and constructors run in.
- chapter
- chapter 03.02
- reading time
- 6 min read
new Child() runs things in a fixed order. first, static init for Parent, then Child (once per class, ever). then the object is allocated with every field zeroed. then the constructors chain up through this(...) and super(...) to Object, and on the way back down each class runs its field initializers and init blocks, then the rest of its constructor body.
parents first. always.
the picture
you know how a subclass constructor can't touch anything until super(...) returns? there's a reason, and it's the order.
think of a two-storey house. you hire the contractor for the top floor (Child), but the first thing he does is call the ground-floor contractor (Parent) and wait. the ground floor gets finished completely (its fields, its init blocks, its constructor body) before anyone lays a single brick upstairs. the top-floor contractor got called first and walks out last.
new: the static rows only ever happen once per class. then call new Child("rex") directly and watch the this(...) hop disappear. the allocation always comes before any constructor code.constructors, quickly
a constructor has the class's name and no return type. it's not really a method: you can't call it on an object, only through new, this(...) or super(...).
class Point {
final int x, y;
Point(int x, int y) { // the real one
this.x = x;
this.y = y;
}
Point() {
this(0, 0); // delegate to the real one
}
}- this(...)
- call another constructor of the same class. handy for defaults: every path ends up in one "real" constructor.
- super(...)
- call the parent's constructor. if you don't write either, javac quietly inserts
super(). - default constructor
- if a class declares no constructor at all, javac adds a no-arg one that just calls
super(). declare any constructor and you lose it. - init block
- a bare
{ ... }in the class body. runs for every object, as if pasted into each constructor that callssuper(...). - static block
static { ... }. runs once, when the class is initialized.
walk through it
here's a real program that prints at every single step. two classes, statics, fields, init blocks, a this(...) and two new Child()s. the stack shows the constructor chain growing and shrinking.
- 1class Parent {
- 2 static String ps = Main.log("Parent static field");
- 3 static { Main.log("Parent static block"); }
- 4
- 5 String pf = Main.log("Parent field");
- 6 { Main.log("Parent init block"); }
- 7
- 8 Parent() {
- 9 super();
- 10 Main.log("Parent() body");
- 11 }
- 12}
- 13
- 14class Child extends Parent {
- 15 static String cs = Main.log("Child static field");
- 16 static { Main.log("Child static block"); }
- 17
- 18 String cf = Main.log("Child field");
- 19 { Main.log("Child init block"); }
- 20
- 21 Child() {
- 22 this("rex");
- 23 Main.log("Child() body");
- 24 }
- 25
- 26 Child(String name) {
- 27 super();
- 28 Main.log("Child(String) body");
- 29 }
- 30}
- 31
- 32public class Main {
- 33 static String log(String what) {
- 34 System.out.println(what);
- 35 return what;
- 36 }
- 37
- 38 public static void main(String[] args) {
- 39 new Child();
- 40 log("--- again");
- 41 new Child();
- 42 }
- 43}
·main is about to say new Child(). but Child has never been used, so before any object exists, its classes get initialized.
pf and cf both null, and three constructor frames pile up before a single initializer runs. then compare stdout before and after --- again.see? the static lines print once (scrub to the last step and read stdout). everything else prints twice, in exactly the same order. and Child(), the constructor new actually called, is the last one to print.
class A {
{ IO.print("a "); }
A() { IO.print("A "); }
static { IO.print("sA "); }
}
class B extends A {
static { IO.print("sB "); }
B() { IO.print("B "); }
{ IO.print("b "); }
}
void main() {
new B();
new B();
}where the initializers actually go+
there's no "run the initializers" step in the JVM. javac pastes field initializers and init blocks into every constructor that calls super(...), right after that call. constructors that start with this(...) don't get a copy, which is why the initializers run once per object, not once per constructor. javap -c Child on the program above:
Child();
aload_0
ldc "rex"
invokespecial Child.<init>(String) // this("rex"). no initializers here
ldc "Child() body" ...
Child(java.lang.String);
aload_0
invokespecial Parent.<init>() // super()
ldc "Child field" ... putfield cf // the field initializer, pasted in
ldc "Child init block" ... // the init block, pasted in
ldc "Child(String) body" ...static initializers get the same treatment, collected in source order into one hidden method called <clinit>, which the JVM runs when the class is first initialized. more in class loading.
the trap: overridable calls in a constructor
the parent's constructor runs before the child's fields are set. so if the parent calls a method the child overrides, the child's code runs against a half-built object.
class Base {
Base() {
describe(); // runs Pet's version, before Pet is ready
}
void describe() {
IO.println("a base");
}
}
class Pet extends Base {
final String name;
Pet(String name) {
this.name = name; // too late: super() already ran
}
@Override
void describe() {
IO.println("a pet named " + name);
}
}
void main() {
new Pet("rex");
}a pet named null
name is final and it still prints null. you just watched a final field change value. kinda cursed.
java 25: statements before super()
since java 25+ a constructor can run code before super(...) (flexible constructor bodies, JEP 513). you still can't read this, call instance methods or touch super there, but you can assign your own fields. so the fix is to set name first:
class Base {
Base() {
describe();
}
void describe() {
IO.println("a base");
}
}
class Pet extends Base {
final String name;
Pet(String name) {
this.name = name; // before super(): allowed since java 25
super();
}
@Override
void describe() {
IO.println("a pet named " + name);
}
}
void main() {
new Pet("rex");
}a pet named rex
the prologue is also the right place for validation (throw before the parent does any work). what it can't do is read the object:
class Dog extends Animal {
String tag = "dog";
Dog(String name) {
IO.println(tag);
super(name);
}
}error: cannot reference tag before supertype constructor has been called
- static init runs once per class, parent before child, on first use.
- the object exists, all zeroed, before any constructor code runs.
- constructors chain up first (
this(...),super(...)), then do their work on the way back down. - field initializers and init blocks run right after
super(...)returns, in source order. - never call an overridable method from a constructor. in java 25 you can at least set your fields before
super().