Java Super Constructor: Syntax and Common Pitfalls
java super constructor: Understand how to call superclass constructors in Java with super(), including syntax, execution order, and common mistakes to avoid.
When a subclass is instantiated, its constructor must call a constructor of its superclass. In Java, the super keyword provides that link, and the java super constructor call is the first statement in a subclass constructor. This article explains the syntax, the implicit behavior, and the pitfalls that commonly trip up developers.
The Role of super() in Constructor Chaining
Every constructor in Java, whether explicit or implicit, eventually calls a constructor of its superclass. If you do not write a super() call, the compiler inserts a no-argument super() automatically. This chaining ensures that the superclass fields are initialized before the subclass adds its own state.
Consider a simple parent class:
public class Animal { public Animal() { System.out.println("Animal constructor"); } }
A subclass that does not declare any constructor gets a default constructor that calls super() implicitly:
public class Dog extends Animal { // implicit super() is inserted here }
When you instantiate Dog, the output is Animal constructor. The implicit call is always a no-argument call, so the superclass must have a visible no-argument constructor for this to compile.
Calling a Parameterized Superclass Constructor
If the superclass does not have a no-argument constructor, or if you need to pass specific values to initialize superclass fields, you must explicitly call super(...) with arguments. This call must be the first statement in the subclass constructor.
public class Vehicle { private String model; public Vehicle(String model) { this.model = model; } } public class Car extends Vehicle { private int doors; public Car(String model, int doors) { super(model); this.doors = doors; } }
The super(model) call passes the model string to the superclass constructor. After that call returns, the subclass constructor can initialize its own fields. This is the standard way to ensure the superclass is fully constructed before subclass logic runs.
Passing Arguments to the Superclass Constructor
Arguments to super() can be literals, variables, or expressions, as long as they match a superclass constructor signature. You can also call a helper method to compute a value, but that method must not access instance fields of the subclass because the superclass constructor has not run yet.
public class Base { public Base(int value) { } } public class Derived extends Base { public Derived() { super(computeBaseValue()); } private static int computeBaseValue() { return 42; } }
Here computeBaseValue() is static, so it does not depend on instance state. If you try to call an instance method or access an instance field in the super() argument, the compiler will reject it because the object is not fully initialized at that point.
Common Mistakes with super()
One frequent mistake is attempting to call super() after this() or after any other statement. Java requires that super() be the first line in the constructor. For example, the following code will not compile:
public class Wrong extends Base { public Wrong() { System.out.println("Before super"); super(); // compile error } }
Another mistake is forgetting to call super() when the superclass lacks a no-argument constructor. The compiler will report an error like "constructor Base in class Base cannot be applied to given types". This forces you to explicitly pass the required arguments.
A third issue is calling super() in a constructor that also calls this(). If you use this() to delegate to another constructor in the same class, that other constructor must call super(). The super() call cannot appear in both constructors directly; only the constructor that actually executes the chain calls super().
Constructor Chaining and Execution Order
When a subclass object is created, the execution order is strict: superclass constructor first, then subclass constructor. This applies to every level in the hierarchy. If you have a chain of three classes, the topmost superclass constructor runs first, then each subclass in order.
public class A { public A() { System.out.println("A"); } } public class B extends A { public B() { System.out.println("B"); } } public class C extends B { public C() { System.out.println("C"); } }
Creating new C() prints A, B, C. This order is guaranteed by the Java language specification. Understanding this helps when you have initialization logic that depends on superclass state being ready.
When You Cannot Use super()
There are a few contexts where super() is not allowed. You cannot call super() from a static method or a static initializer block, because there is no instance. You also cannot call super() from a constructor of a final class if that class has no superclass (though Object is the implicit superclass, and super() is allowed but redundant).
More importantly, you cannot call super() from a constructor that is also a this() call in the same constructor body. The delegation must be handled by the constructor that actually initializes the object.
Performance and Maintainability Considerations
Constructor chaining adds a small runtime cost because each constructor call involves a method invocation and stack frame setup. In practice, this overhead is negligible compared to the work done inside constructors, so you should not optimize by trying to avoid super() calls.
From a maintainability perspective, explicit super() calls make dependencies clear. If a superclass changes its constructor signature, the compiler will force you to update all subclasses. This is a good thing because it prevents silent initialization errors. However, long constructor chains can become difficult to trace. Keeping constructors short and delegating to a single primary constructor helps.
A useful pattern is to have one constructor that takes all parameters and calls super(...), while other constructors use this(...) to delegate. This reduces duplication and makes the superclass call appear in only one place.
public class Rectangle extends Shape { public Rectangle(int width, int height) { super(width, height); } public Rectangle(int size) { this(size, size); } }
Here the second constructor delegates to the first, which then calls super(). This keeps the superclass contract in one location and simplifies future changes.
Final Technical Note: super() with Records and Sealed Classes
If you are using Java records, a record cannot explicitly extend another class, so super() is not applicable beyond Object. For sealed classes, the same rules apply as for regular classes; a subclass must call super() with the appropriate arguments. The compiler enforces the same first-statement rule, so the behavior is consistent across modern and legacy Java versions.