Java Super Constructor: Syntax and Common Pitfalls
Calling a superclass constructor in Java with super(): learn the required syntax, how implicit constructor chaining works, and the mistakes to avoid.
When a subclass is instantiated, the constructor chain must include a constructor of its superclass. In Java, the super(...) call is how a subclass constructor invokes that superclass constructor. This article explains the required syntax, the implicit call that the compiler adds, and the common mistakes developers make with superclass constructors.
The Role of super() in Constructor Chaining
For ordinary class hierarchies, every constructor chain includes a call to a superclass constructor. In java.lang.Object itself, there is no superclass to call. If a constructor does not begin with this(...) or an explicit super(...), the compiler inserts a no-argument super() call. That call starts the superclass initialization, so 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, the constructor that initializes the object must explicitly call super(...) with arguments. This call must be the first statement in that 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 that match a superclass constructor signature. A static helper method can compute an argument value. You cannot use an instance method or instance field in the argument list, because the superclass constructor has not run yet and the object is not fully initialized.
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. The compiler rejects instance method calls or instance field accesses in super(...) arguments for this reason.
Common Mistakes with super()
One frequent mistake is trying to put any statement before super(). Java requires that a super(...) call be the first statement in the constructor. For example, this code will not compile:
public class Wrong extends Base { public Wrong() { System.out.println("Before super"); super(); // compile error: super must be the first statement } }
Another mistake is forgetting to provide a super(...) call when the superclass has no no-argument constructor. The compiler reports an error such as constructor Base in class Base cannot be applied to given types, forcing you to pass the required arguments.
A related mistake is mixing this(...) and super(...) in the same constructor. Since both forms must be the first statement, only one can appear. If you delegate with this(...), the receiving constructor calls super(...); the superclass call appears in only one place.
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 write it in a static method or static initializer block, because there is no current instance to initialize. Inside a constructor, super(...) cannot be combined with this(...), because both forms must be the first statement. If you delegate to another constructor with this(...), the receiving constructor is the one that must start with super(...).
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.