Skip to content
Part IA Michaelmas Term

Object Creation and Initialisation

new allocates memory on the heap, zero-initialises fields, runs field initialisers in declaration order, then runs the constructor body after any super(...) call. The full initialisation sequence:

  1. Superclass fields are zero-initialised, then initialisers run, then the superclass constructor body executes. This continues up the chain to Object.
  2. Then this class’s fields are zero-initialised, initialisers run, then the constructor body executes.

This ordering ensures invariants are always maintained — a superclass’s constructor never sees subclass fields (they are still zero at that point).

Worked Example

class Grandparent {
    int x = 10;
    Grandparent() {
        print();
    }
    void print() { System.out.println("Grandparent.x = " + x); }
}

class Parent extends Grandparent {
    int y = 20;
    Parent() {
        print();
    }
    @Override void print() { System.out.println("Parent: x=" + x + ", y=" + y); }
}

class Child extends Parent {
    int z = 30;
    Child() {
        super();
        print();
        z = 40;
    }
    @Override void print() { System.out.println("Child: x=" + x + ", y=" + y + ", z=" + z); }
}

public class Test {
    public static void main(String[] args) {
        new Child();
    }
}

When new Child() executes, the sequence is:

  1. Memory for Child allocated on the heap; all fields zeroed (x=0, y=0, z=0)
  2. Child() constructor body begins with an implicit super() call
  3. Parent() constructor body begins with an implicit super() call
  4. Grandparent() constructor runs:
    • x initialiser sets x = 10
    • Constructor body calls this.print() — but this is a Child (dynamic dispatch), so Child.print() runs, printing x=10, y=0, z=0
  5. Parent() constructor continues:
    • y initialiser sets y = 20
    • Constructor body calls this.print() — again dispatches to Child.print(), printing x=10, y=20, z=0
  6. Child() constructor continues:
    • z initialiser sets z = 30
    • Constructor body calls this.print(), printing x=10, y=20, z=30
    • Then z = 40 executes

This demonstrates why calling overridable methods from constructors is dangerous — z was 0 when the grandparent’s constructor ran because it hadn’t been initialised yet.

The Stages of new

StageWhat Happens
1. AllocationMemory for the new object is allocated on the heap
2. ZeroingAll fields are set to default values (0, false, null, \u0000)
3. super()The superclass constructor is invoked, recursively
4. Instance initialisersField initialisers and instance initialiser blocks run in declaration order
5. Constructor bodyThe remainder of the constructor’s own code executes

Initialiser Blocks

Instance initialisers run before the constructor body. They are declared as a bare { ... } block directly inside a class body:

class Example {
    int a;
    {
        a = 5;
        System.out.println("Instance initialiser: a = " + a);
    }

    Example() {
        System.out.println("Constructor: a = " + a);
    }
}

Static initialisers run once when the class is first loaded by the JVM — before any instance is created and before any static method or field is accessed:

class Example {
    static int count;
    static {
        count = 42;
        System.out.println("Static initialiser: count = " + count);
    }
}

Static initialisers run in declaration order and are useful for initialising static fields that cannot be expressed in a single expression.

Object Lifetime

An object exists from construction until it becomes unreachable — meaning no live reference chain from a GC root reaches it. At that point the object becomes eligible for collection.

An object is not destroyed immediately when it becomes unreachable. Garbage collection happens at the JVM’s discretion — the collector may run minutes later, or never, depending on memory pressure and JVM implementation. You cannot force the GC to run; System.gc() is merely a suggestion.

GC roots include:

  • Local variables on active stack frames
  • Static fields of loaded classes
  • Active thread references
  • JNI (native interface) references
  • Objects kept alive by finalisers or reference queues

An object that is unreachable but whose finalize() method has not yet been called may briefly become reachable again if finalize() stores the this reference somewhere — this is called object resurrection and is one reason finalize() is deprecated.