3.2 Constructors, Overloading, Initialization Order, and Garbage Collection

Key Takeaways

  • The compiler provides a default no-argument constructor only if no constructors of any kind are declared in the class; its access modifier matches the class declaration.

  • Explicit constructor invocations using this(...) or super(...) must appear as the very first executable statement in a constructor body and cannot form a circular invocation cycle.

  • Initialization follows a rigid order: superclass static blocks -> subclass static blocks -> superclass instance initializers/fields -> superclass constructor -> subclass instance initializers/fields -> subclass constructor.

  • Invoking an overridden instance method from a superclass constructor executes the subclass implementation before subclass fields are initialized, resulting in default field values.

  • Objects become eligible for garbage collection when they are no longer reachable from any live GC root; circular references in an island of isolation are collected without issue.

Last updated: October 2026

Constructors, Initialization Order, and Garbage Collection

Understanding object instantiation, constructor mechanics, and memory management is fundamental to writing reliable Java applications and succeeding on the 1Z0-829 exam. A solid grasp of the order of execution during object creation and the reachability graph during garbage collection prevents subtle runtime bugs.


1. Constructor Mechanics and Default Constructors

A constructor initializes a newly created object. Syntactically, a constructor resembles a method but has no return type (not even void) and must match the class name exactly.

Compiler-Generated Default Constructor Rules

  1. Condition: The compiler generates a default no-argument constructor only if the class defines zero explicit constructors.
  2. Signature and Body: The default constructor takes no arguments and contains a single statement: super();.
  3. Access Level: The access modifier of the default constructor matches the access modifier of the class itself. A public class receives a public default constructor; a package-private class receives a package-private default constructor.
  4. Supersession: As soon as any constructor is explicitly declared (even a constructor with multiple parameters), the compiler does not generate a default no-arg constructor.

Warning

If a superclass declares only parameterized constructors (e.g., SuperClass(int x)), any subclass that does not explicitly invoke super(x) in its own constructors will fail to compile. This occurs because the subclass constructor implicitly attempts to call super(), which no longer exists in the superclass.


2. Constructor Overloading and Chaining (this and super)

Constructors can be overloaded by providing different parameter counts, types, or orders. To avoid duplicate initialization code, constructors can chain to other constructors.

Chaining Rules

  • this(...): Calls an overloaded constructor in the same class.
  • super(...): Calls a constructor in the immediate superclass.
  • First Statement Constraint: If present, this(...) or super(...) must be the first executable statement in the constructor body.
  • Mutual Exclusivity: A constructor cannot call both this(...) and super(...). If neither is written, the compiler automatically inserts super(); on line 1.
  • No Circularity: Constructor chaining cannot form an infinite recursive loop (e.g., A() calls this(1) and A(int) calls this()). The compiler detects constructor recursion and issues a compile-time error: recursive constructor invocation.
  • Static Context Restriction: Arguments passed to this(...) or super(...) cannot reference instance members, instance methods, or the this reference of the object under construction because the object instance does not yet exist.
public class Account {
    private String id;
    private double balance;

    public Account() {
        this("DEFAULT", 0.0); // Chained constructor call
    }

    public Account(String id, double balance) {
        super(); // Implicit if omitted
        this.id = id;
        this.balance = balance;
    }
}

3. Complete Class and Instance Initialization Order

When a class is referenced and an object is instantiated via new, initialization proceeds through two distinct phases: Class (Static) Initialization and Instance Initialization.

Phase 1: Class (Static) Initialization (Occurs once upon first class reference)
  1. Superclass static fields & static initialization blocks (in declaration order)
  2. Subclass static fields & static initialization blocks (in declaration order)

Phase 2: Instance Initialization (Occurs every time 'new' is executed)
  3. Subclass constructor is entered, immediately delegating via super(...)
  4. Superclass instance fields & instance initializer blocks (in declaration order)
  5. Superclass constructor body executes
  6. Subclass instance fields & instance initializer blocks (in declaration order)
  7. Subclass constructor body executes

The Classic Exam Trap: Calling Overridden Methods from Super Constructors

If a superclass constructor invokes an instance method that is overridden by the subclass, dynamic method dispatch invokes the subclass implementation at runtime. However, at that moment, the subclass instance fields have not yet been initialized—they hold default primitive values (0, false) or null.

class Base {
    int count = 10;
    Base() {
        display(); // Calls overridden method in Child!
    }
    void display() {
        System.out.println("Base count: " + count);
    }
}

class Child extends Base {
    int count = 20; // Initialized AFTER Base constructor completes
    void display() {
        System.out.println("Child count: " + count);
    }
}

public class InitTest {
    public static void main(String[] args) {
        new Child(); // Outputs: Child count: 0
    }
}

At the time Base() runs, Child.count is still uninitialized (0). The output is Child count: 0, not 20 or 10.


4. Object Lifecycle, Heap Allocation, and Garbage Collection

Stack vs. Heap Allocation

  • Stack: Stores active thread execution frames, local primitive variables, and references to objects residing on the heap.
  • Heap: Stores all object instances and arrays. Memory is allocated on the heap whenever new is evaluated.

Root Reachability

Java employs a tracing garbage collector that builds an object graph starting from Garbage Collection (GC) Roots:

  • Active thread local variables on call stacks.
  • Static reference variables held in loaded classes.
  • JNI (Java Native Interface) global and local references.

An object on the heap is eligible for garbage collection when there is no path of references connecting it to any live GC root.

Becoming Eligible for GC

  1. Dereferencing / Nullification: Setting the reference variable to null (ref = null;).
  2. Reassignment: Pointing the reference to another object (ref = new OtherObject();).
  3. Out of Scope: When a method returns, its local stack frame pops, discarding local references.
  4. Islands of Isolation: When two or more unreferenced objects reference each other in a closed loop, but none can be reached from any GC root. The entire group is immediately eligible for garbage collection.
class Node {
    Node friend;
}

public class IsolationDemo {
    public static void main(String[] args) {
        Node a = new Node(); // Object 1
        Node b = new Node(); // Object 2
        a.friend = b;
        b.friend = a;
        
        a = null;
        b = null;
        // Both Object 1 and Object 2 form an island of isolation and are eligible for GC!
    }
}

5. Garbage Collection Invocations and Cleanup Modernization

System.gc()

Invoking System.gc() or Runtime.getRuntime().gc() sends a request to the JVM suggesting that a collection cycle be performed. It is never a guarantee. The JVM may ignore the request or defer execution.

Deprecation and Removal of Finalizers

Historically, the Object.finalize() method allowed an object to perform cleanup before memory reclamation. However, finalizers suffer from fundamental design flaws: unpredictable timing, potential thread stalls, ignored exceptions, severe performance overhead, and the ability to resurrect dead objects.

  • finalize() was deprecated in Java 9.
  • Marked for removal in Java 18 (JEP 421).

Modern Alternatives: AutoCloseable and Cleaner

  1. AutoCloseable & Try-With-Resources: The preferred standard for prompt, deterministic resource reclamation (e.g., closing file descriptors, network connections, and database handles).
  2. java.lang.ref.Cleaner: Introduced in Java 9 to replace finalizers for non-deterministic background cleanup using phantom references, avoiding object resurrection and thread starvation.
Test Your Knowledge

Examine the following code snippet:

class BaseComponent {
    int id = 100;
    BaseComponent() {
        render();
    }
    void render() {
        System.out.print("Base:" + id + " ");
    }
}
class ButtonComponent extends BaseComponent {
    int id = 200;
    void render() {
        System.out.print("Button:" + id + " ");
    }
}
public class Runner {
    public static void main(String[] args) {
        new ButtonComponent();
    }
}

What does the program output when executed?

A

Base:100 Button:200

B

Base:100

C

Button:200

D

Button:0

Test Your Knowledge

Given the following code:

class Node {
    Node link;
}
public class GraphTest {
    public static void main(String[] args) {
        Node first = new Node();  // Object 1
        Node second = new Node(); // Object 2
        first.link = second;
        second.link = first;
        first = null;
        second = null;
        // Point of evaluation
    }
}

At the point of evaluation, how many objects are eligible for garbage collection?

A

Neither object is eligible for garbage collection because their mutual references maintain an active reference count.

B

Only Object 1 is eligible for garbage collection because Object 2's link still exists.

C

Both Object 1 and Object 2 are eligible for garbage collection because they form an isolated cycle unreachable from any live thread root.

D

Neither object can be collected until System.gc() is explicitly invoked to break circular references.

Test Your Knowledge

Consider the following class declarations:

class Computer {
    Computer(String model) {
        System.out.print("Model:" + model + " ");
    }
}
class Server extends Computer {
    Server() {
        System.out.print("Server-ready ");
        super("Rack");
    }
}

Why does Server fail to compile?

A

The Computer class must define an explicit no-argument constructor before any subclass can extend it.

B

A subclass constructor cannot pass literal strings to a superclass constructor.

C

The call to super(...) must be the first statement inside the constructor body.

D

The Computer class lacks a public access modifier on its constructor.

Sections you finish are checked off in the contents.