Back to Blog
Java

Java Constructor Chaining: this() and super()

java constructor chaining: Learn how Java constructor chaining works with this() and super() calls, including execution order, restrictions, and common pitfalls.

constructor chainingthis() callsuper() callobject initializationinheritance
Diagram showing two Java constructors linked by this() and super() calls, illustrating constructor chaining flow.

java constructor chaining requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

In Java, constructor chaining is the practice of one constructor invoking another constructor in the same class or in a direct parent class. This mechanism relies on two special call statements: this() for same-class constructors and super() for parent-class constructors. Understanding how these calls behave is essential for controlling object initialization, avoiding code duplication, and debugging subtle initialization-order issues.

The Two Forms of Constructor Chaining: this() and super()

Java supports two distinct forms of constructor chaining. The first uses this(...) to call another constructor defined in the same class. This is commonly used to provide default values or to let one constructor delegate to a more complete one. The second uses super(...) to call a constructor from the immediate parent class. Every constructor must eventually reach a super() call, either explicitly or implicitly, because object construction always starts at the root Object class.

public class Rectangle { private int width; private int height; public Rectangle() { this(1, 1); // calls the two-argument constructor } public Rectangle(int width, int height) { this.width = width; this.height = height; } }

Here the no-argument constructor chains to the parameterized one using this(1, 1). This keeps the initialization logic in a single place and avoids duplicating field assignments.

For inheritance, the super() call is used to invoke a parent constructor. If you do not write an explicit super() call, the compiler inserts a no-argument super() automatically. This means every constructor chain ultimately reaches Object's constructor.

public class Square extends Rectangle { private String label; public Square(int side) { super(side, side); // calls Rectangle(int, int) this.label = "square"; } }

Order of Execution in a Chained Constructor Chain

When a constructor calls another constructor, the called constructor executes first, and the calling constructor continues only after the called one returns. This is critical for inheritance: the parent constructor runs before any subclass field initializers or constructor body statements. The exact order is:

  1. The first statement of a constructor must be a this() or super() call (or the compiler inserts super()).
  2. The called constructor runs to completion, including its own super() chain.
  3. After the called constructor returns, instance variable initializers in the current class run.
  4. The remaining body of the current constructor executes.

Consider this example:

class Base { Base() { System.out.println("Base constructor"); } } class Derived extends Base { private int value = 10; Derived() { super(); // implicit, but shown for clarity System.out.println("Derived constructor, value=" + value); } }

The output is always:

Base constructor Derived constructor, value=10

The parent constructor finishes before the derived class's field initializer value = 10 runs. This order is guaranteed by the Java Language Specification and cannot be altered.

Rules and Restrictions for Constructor Calls

Constructor chaining has strict syntactic rules that developers frequently trip over. The most important is that a this() or super() call must be the first statement in a constructor. You cannot place any other code before it. This restriction exists because the object's memory layout must be initialized before any logic runs.

Another rule is that you cannot use both this() and super() in the same constructor. A constructor can delegate to either another constructor in the same class or a constructor in the parent class, but not both. If you need to call a parent constructor after a same-class constructor, you chain through the same-class constructor, which in turn calls super().

public class Example { public Example() { this(0); // legal } public Example(int x) { // super() is implicit here } }

Attempting to call this() after a statement or using super() after this() results in a compile-time error. Also, a constructor cannot call itself directly or indirectly in a cycle; the compiler rejects recursive constructor calls.

Practical Example: Chaining with Overloaded Constructors

Overloaded constructors are the most common use case for this() chaining. Suppose you have a configuration class with several optional parameters. Instead of duplicating validation and assignment logic, you can funnel all constructors through a single primary constructor.

public class ServerConfig { private final String host; private final int port; private final boolean tls; public ServerConfig(String host) { this(host, 443, true); } public ServerConfig(String host, int port) { this(host, port, port == 443); } public ServerConfig(String host, int port, boolean tls) { this.host = host; this.port = port; this.tls = tls; } }

The two-argument constructor delegates to the three-argument constructor, and the one-argument constructor delegates to the two-argument one. This pattern ensures all validation and assignment happens in one place, reducing the risk of inconsistent state.

When using this pattern, be careful with default values that depend on other arguments. In the example above, tls is derived from port, which is a deliberate design choice. If defaults are independent, consider using static factory methods for more clarity.

Common Mistakes and How to Avoid Them

A frequent mistake is assuming that field initializers run before the super() call. As shown earlier, field initializers run after the parent constructor returns. This can lead to unexpected null values if a parent constructor calls an overridden method that relies on subclass fields.

class Parent { Parent() { print(); } void print() { System.out.println("Parent"); } } class Child extends Parent { private String name = "child"; @Override void print() { System.out.println(name); // prints null, not "child" } }

When new Child() is executed, the Parent constructor calls the overridden print() method before Child's name field is initialized. The output is null. Avoid calling overridable methods from constructors, especially in a chained hierarchy.

Another mistake is forgetting that an implicit super() call requires a no-argument constructor in the parent class. If the parent class only defines parameterized constructors, the child class must explicitly call super(...) with matching arguments. Otherwise the compiler reports an error.

Constructor Chaining and Inheritance: When super() Is Implicit

In a class hierarchy, constructor chaining is not optional; it is always present. Even if you do not write a super() call, the compiler inserts one that calls the parent's no-argument constructor. This implicit behavior can be surprising when the parent class lacks a no-argument constructor. In that case, you must explicitly call super(...) as the first statement of each child constructor.

class Vehicle { private String model; Vehicle(String model) { this.model = model; } } class Car extends Vehicle { Car(String model) { super(model); // required because Vehicle has no no-arg constructor } }

This explicit call ensures the parent's state is initialized before the child's constructor body runs. It also makes the dependency visible, which helps maintainers understand the initialization contract.

Performance and Maintainability Considerations

Constructor chaining itself has negligible runtime cost; the JVM resolves constructor calls efficiently, and the overhead of an extra invocation is minimal compared to object allocation and field initialization. The real benefit is maintainability. By centralizing initialization logic, you reduce duplication and make the code easier to modify. For example, adding a new validation rule in the primary constructor automatically applies to all overloaded constructors that chain to it.

However, excessive chaining can hurt readability. If a constructor chain becomes longer than three levels, consider whether the design is too complex. Deep chains are harder to trace and can obscure which constructor actually sets a particular field. In such cases, static factory methods or the Builder pattern often provide clearer intent.

Another operational concern is the interaction with final fields. A final field must be assigned exactly once during construction. When using constructor chaining, the assignment can happen in the primary constructor, and all chained constructors must not reassign it. This is naturally satisfied if you delegate all assignments to one constructor, but it becomes a compile-time error if you try to assign a final field in multiple constructors.

Finally, be aware that constructor chaining does not change the fundamental rule that all instance fields are initialized to their default values (null, 0, false) before any constructor body executes. This includes fields in the parent class. Understanding this order helps you predict behavior when constructors interact with overridden methods or when exceptions are thrown during initialization.

java constructor chaining: Practical Usage and Code Examples | RYUSLOG DEV