3.1 Class Declarations, Constructors, and Object Lifecycle

Key Takeaways

  • Class declarations define blueprints using specific modifier ordering, where top-level classes are restricted to public or package-private access.
  • The Java compiler inserts a default no-argument constructor calling super() only when no constructors are explicitly declared in the class body.
  • Constructor chaining via this() or super() must strictly be the first statement in a constructor, preventing circular invocations and ensuring deterministic hierarchy construction.
  • Initialization follows a rigid multi-phase lifecycle: superclass static members, subclass static members, superclass instance initializers/constructors, and finally subclass instance initializers/constructors.
Last updated: September 2026

Class Declarations, Constructors, and Object Lifecycle

In Java SE 21, classes represent the fundamental structural blueprints of object-oriented applications. Mastering class declarations, constructor mechanics, initialization sequences, and object lifecycle transitions is essential for writing defect-free enterprise software and scoring highly on the 1Z0-830 examination.


Anatomy of a Class Declaration

The Java Language Specification (JLS §8.1) defines the formal grammar for top-level and nested class declarations. A class declaration consists of optional annotations, access modifiers, non-access modifiers, the class keyword, the identifier name, optional generic type parameters, an optional extends clause, an optional implements clause, and the class body enclosed in braces:

// Formal class declaration structure
[Access Modifier] [Non-Access Modifiers] class ClassName<TypeParameters> 
        extends SuperClassName 
        implements InterfaceOne, InterfaceTwo {
    // Member declarations: fields, methods, constructors, initializers, nested types
}

Top-Level Class Constraints and Compilation Units

  1. Top-Level Access Restrictions: A top-level class (declared directly inside a .java compilation unit) can only use either public access or package-private (default) access (omitting any access modifier keyword). Applying private or protected to a top-level class results in a compile-time error.
  2. Compilation Unit File Naming: A single .java source file can contain at most one public top-level class. If a public class is present, the filename must exactly match the public class name followed by the .java extension, respecting case sensitivity (e.g., public class PaymentService must reside in PaymentService.java). A compilation unit may contain multiple package-private top-level classes.
  3. Non-Access Modifiers on Classes:
    • final: Declares that the class cannot be extended or subclassed by any other class (e.g., java.lang.String, java.lang.Integer).
    • abstract: Declares that the class cannot be directly instantiated with the new operator. It may contain abstract methods requiring subclass implementation.
    • sealed / non-sealed: Controls and restricts which specific classes may extend this class via the permits clause.
    • Illegal Modifier Combinations: A class cannot be simultaneously declared final and abstract, as their semantics are diametrically opposed (final mandates no subclasses, while abstract mandates subclassing for instantiation).
    • strictfp: Obsolete since Java 17 (all floating-point calculations are strictly IEEE 754 evaluated), but syntactically allowed for backward compatibility.
// Valid compilation unit: PaymentGateway.java
package com.payment.core;

import java.io.Serializable;

// Public top-level class matching file name
public final class PaymentGateway<T extends Serializable> implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("Gateway closed");
    }
}

// Valid package-private top-level helper class in the same file
class GatewayHelper {
    static void logTrace(String msg) {
        System.out.println("[TRACE] " + msg);
    }
}

Constructors and Compiler Synthesis Rules

A constructor is an executable code block invoked during object allocation to initialize an instance's fields and enforce initial invariant conditions.

Constructor Characteristics

  • Name: Must exactly match the identifier of the declaring class (case-sensitive).
  • Return Type: Must declare no return type whatsoever (not even void). If a return type is declared, the compiler treats the construct as a regular instance method rather than a constructor.
  • Modifiers: May declare any access modifier (public, protected, package-private, private). Constructors cannot be declared static, final, abstract, or synchronized.
public class AccountService {
    private final String accountId;
    
    // Legitimate parameterized constructor
    public AccountService(String accountId) {
        this.accountId = accountId;
    }
    
    // EXAM TRAP: Method declaring return type void, NOT a constructor!
    public void AccountService(String accountId) {
        System.out.println("This is a regular method, not a constructor!");
    }
}

The Compiler-Generated Default Constructor

If and only if a class declares zero explicit constructors, the Java compiler automatically synthesizes a default no-argument constructor:

  • Parameter List: Zero parameters (no-arg).
  • Access Modifier: Matches the visibility of the declaring class (public for public classes, package-private for package-private classes).
  • Body: Contains a single statement: super(); which delegates to the no-argument constructor of the direct superclass.
  • Synthesis Suppression: The moment a developer declares any explicit constructor (regardless of its parameter count or access modifier, including private), the compiler suppresses default constructor synthesis completely.
class Vehicle {
    protected int wheels;
    // Explicit parameterized constructor suppresses default constructor
    public Vehicle(int wheels) {
        this.wheels = wheels;
    }
}

class Truck extends Vehicle {
    // COMPILE ERROR if Truck has no explicit constructor!
    // The compiler attempts to insert:
    // public Truck() { super(); }
    // but Vehicle has no no-arg Vehicle() constructor!
    
    // Fix: Explicitly invoke the superclass parameterized constructor
    public Truck() {
        super(18);
    }
}

Constructor Chaining: this() and super() Mechanics

Constructor chaining allows constructor overloads within the same class to reuse initialization logic or delegate upward to superclass initializers.

public class ServerConfig {
    private final String host;
    private final int port;
    private final boolean ssl;

    // Default configuration: delegates to 2-arg constructor
    public ServerConfig() {
        this("localhost", 8080);
    }

    // 2-arg configuration: delegates to master 3-arg constructor
    public ServerConfig(String host, int port) {
        this(host, port, true);
    }

    // Master 3-arg constructor
    public ServerConfig(String host, int port, boolean ssl) {
        super(); // Implicit or explicit call to Object constructor
        this.host = host;
        this.port = port;
        this.ssl = ssl;
    }
}

Strict Rules Governing Explicit Constructor Invocations

  1. First Statement Requirement: An explicit constructor invocation (this(...) or super(...)) must strictly be the first executable statement in the constructor body. Placing any statement, method call, or variable declaration before this(...) or super(...) triggers a compile-time error in Java SE 21.
  2. Mutual Exclusivity: A constructor cannot contain both this(...) and super(...). Invoking this(...) passes the responsibility of calling super(...) to the targeted sibling constructor.
  3. No Circular Invocations: Circular constructor delegation chains (such as Constructor A invoking this() to Constructor B, which invokes this() back to Constructor A) are identified by javac and rejected with a recursive constructor invocation compilation error.
  4. Pre-Construction State Access: Expressions passed as arguments to this(...) or super(...) cannot access instance variables or call instance methods on the uninitialized instance (this), because the superclass object state has not yet completed its construction. Static fields and static helper methods can be safely passed as arguments.

Complete Multi-Phase Initialization Sequence

Java guarantees deterministic order of execution for static initializers, instance fields, and constructor bodies across complex inheritance hierarchies.

class Ancestor {
    static String staticAncestor = trace("1. Ancestor static field");
    String instanceAncestor = trace("5. Ancestor instance field");

    static { trace("2. Ancestor static block"); }
    { trace("6. Ancestor instance block"); }

    public Ancestor() {
        trace("7. Ancestor constructor body");
    }

    static String trace(String msg) {
        System.out.println(msg);
        return msg;
    }
}

class Descendant extends Ancestor {
    static String staticDescendant = trace("3. Descendant static field");
    String instanceDescendant = trace("8. Descendant instance field");

    static { trace("4. Descendant static block"); }
    { trace("9. Descendant instance block"); }

    public Descendant() {
        super();
        trace("10. Descendant constructor body");
    }
}

Deterministic Initialization Phases

PhaseCategoryExecuted ElementsTiming and Cardinality
Phase 1Class LoadingSuperclass static variable initializers & static { ... } blocksEvaluated in textual order; executes once when superclass is loaded.
Phase 2Class LoadingSubclass static variable initializers & static { ... } blocksEvaluated in textual order; executes once when subclass is loaded.
Phase 3InstantiationSuperclass instance variable initializers & { ... } instance blocksEvaluated in textual order; executes upon each new instantiation.
Phase 4InstantiationSuperclass constructor bodyExecutes immediately following superclass instance initializers.
Phase 5InstantiationSubclass instance variable initializers & { ... } instance blocksEvaluated in textual order; executes upon each new instantiation.
Phase 6InstantiationSubclass constructor bodyExecutes to completion, returning the fully initialized instance reference.

Object Lifecycle, Reachability, and Cleaners

An object's lifecycle begins upon heap allocation and concludes when its memory is reclaimed by the Garbage Collector (GC).

public class MemoryTracker {
    public static void runSimulation() {
        Node nodeA = new Node("A"); // Node A allocated (1 live reference)
        Node nodeB = new Node("B"); // Node B allocated (1 live reference)
        
        nodeA.next = nodeB;         // A references B
        nodeB.next = nodeA;         // B references A (Circular Island)
        
        nodeA = null;               // Node A has no local root reference
        nodeB = null;               // Node B has no local root reference
        // Both Node A and Node B form an isolated island: ELIGIBLE FOR GC
    }
}

Object Reachability and GC Mechanics

  • GC Roots: Live references residing in active thread call stacks (local variables, parameters), static class variables, and JNI handles form root sets.
  • Unreachable Objects: Any object that cannot be reached through a continuous chain of references starting from a GC Root is eligible for immediate reclamation.
  • Islands of Isolation: Circular reference graphs where isolated objects reference each other but are disconnected from all live GC Roots are immediately eligible for garbage collection.
  • System.gc(): Submits a non-binding hint requesting GC execution. The JVM is never obligated to honor or immediately execute this request.
  • Deprecation and Removal of finalize(): The finalize() method was deprecated in Java 9 and permanently marked for removal. Modern Java SE 21 applications must implement deterministic resource cleanup via java.lang.AutoCloseable with try-with-resources, or register asynchronous cleanup actions using java.lang.ref.Cleaner and PhantomReference.

Common 1Z0-830 Exam Traps

  • Method Looking Like Constructor: Watch for return types on constructor declarations (e.g., public void User()). It compiles as a method and will not be invoked by new User().
  • Missing Superclass Default Constructor: If a superclass defines only SuperClass(int x) and a subclass constructor does not explicitly write super(val), compilation fails on the subclass constructor header.
  • Static Blocks Running Once: Static initialization runs only upon the initial loading and linking of the class; instantiating multiple objects does not re-execute static initializers.
  • Illegal Initializer Placement: Attempting to put statements before this() or super() inside constructor bodies triggers compiler errors.
Loading diagram...
Java Class Initialization and Construction Flow
Test Your Knowledge

Given the following class definitions:

class Device {
    protected String name;
    public Device(String name) {
        this.name = name;
    }
}

class Sensor extends Device {
    private double calibration;
    public Sensor(String name, double calibration) {
        // Line 1
        this.calibration = calibration;
    }
}
Which statement correctly completes Line 1 so that the code compiles successfully?

A
B
C
D
Test Your Knowledge

Examine the following Java program:

class Alpha {
    static { System.out.print("A "); }
    { System.out.print("B "); }
    Alpha() { System.out.print("C "); }
}

class Beta extends Alpha {
    static { System.out.print("D "); }
    { System.out.print("E "); }
    Beta() {
        super();
        System.out.print("F ");
    }
    
    public static void main(String[] args) {
        System.out.print("1 ");
        new Beta();
        System.out.print("2 ");
        new Beta();
    }
}
What is the exact console output when running Beta?

A
B
C
D
Test Your Knowledge

Consider the following constructor declarations inside a class named Transaction:

public class Transaction {
    private String id;
    private double amount;
    
    public Transaction(String id) {
        this(id, 0.0);
    }
    
    public Transaction(String id, double amount) {
        this.id = id;
        this.amount = amount;
    }
    
    public Transaction() {
        System.out.println("Creating default");
        this("TX-000");
    }
}
Why does this code fail to compile?

A
B
C
D
Test Your Knowledge

Consider the following reference allocations in a method:

public void processNodes() {
    Node n1 = new Node("Alpha"); // Node Alpha
    Node n2 = new Node("Beta");  // Node Beta
    Node n3 = new Node("Gamma"); // Node Gamma
    
    n1.neighbor = n2;
    n2.neighbor = n1;
    n3.neighbor = n2;
    
    n1 = null;
    n2 = null;
    // Point X
    n3 = null;
    // Point Y
}
At Point X and Point Y, how many Node objects are eligible for garbage collection?

A
B
C
D