scoresvideos
AP Computer Science A
One-page, printable cheatsheet
Cheatsheet visualization
Find gaps with guided practice
Guided practice grid visualization
Table of Contents

💻ap computer science a review

9.2 Writing Constructors for Subclasses

Verified for the 2025 AP Computer Science A examCitation:

When you create an object in Java, a constructor is called to set up the initial state of that object. In inheritance relationships, constructors play a special role because both the superclass and subclass need to be properly initialized. This guide explores how constructors work in inheritance, how to call the superclass constructor from a subclass, and the rules that govern this process. Understanding these concepts will help you create robust class hierarchies where objects are correctly initialized at all levels.

Constructors and Inheritance Basics

Before diving into the details, let's clarify some important points about constructors in inheritance:

  • Constructors are not inherited: Unlike instance variables and methods, constructors do not get inherited from the superclass to the subclass.
  • Each class must define its own constructors: Every class needs its own constructor(s) to initialize its specific instance variables.
  • Superclass initialization is critical: When creating a subclass object, the superclass portion must be properly initialized first.

The Constructor Chain

When you create an object of a subclass, a constructor chain occurs:

  1. The subclass constructor is called
  2. The superclass constructor is called (either explicitly or implicitly)
  3. If the superclass extends another class, that constructor is called next
  4. This continues up the inheritance chain to the Object class
  5. Execution then returns down the chain, with each constructor completing its initialization

The super Keyword in Constructors

The super keyword serves a special purpose in constructors. When used with parentheses super(), it calls a constructor from the immediate superclass.

Some important rules about using super() in constructors:

  • It must be the first statement in the subclass constructor
  • If you don't include it explicitly, Java automatically inserts a call to the superclass's no-argument constructor
  • You can pass arguments to the superclass constructor using super(arg1, arg2, ...)

Writing a Basic Subclass Constructor

Let's look at a basic example of a superclass and subclass with constructors:

// Superclass
public class Person {
    private String name;
    private int age;
    
    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // Getters and other methods...
}

// Subclass
public class Student extends Person {
    private String studentId;
    
    // Constructor that calls the superclass constructor
    public Student(String name, int age, String studentId) {
        super(name, age);  // Call to superclass constructor
        this.studentId = studentId;
    }
    
    // Additional methods...
}

In this example:

  1. The Student constructor takes parameters for all necessary values
  2. It calls the Person constructor using super(name, age) to initialize the inherited instance variables
  3. It then initializes its own instance variable studentId

Passing Parameters to the Superclass Constructor

When calling the superclass constructor, you need to provide appropriate values for its parameters. These values can come from:

  • Parameters passed to the subclass constructor
  • Calculated values
  • Default values
  • Constants

The actual parameters passed in the call to the superclass constructor provide the values that the superclass constructor uses to initialize the object's instance variables. This is how the inherited instance variables get their initial values.

public class Employee extends Person {
    private String employeeId;
    private double salary;
    
    public Employee(String name, int age, String employeeId, double salary) {
        // Pass name and age to the Person constructor
        super(name, age);
        
        // Initialize Employee-specific variables
        this.employeeId = employeeId;
        this.salary = salary;
    }
}

Implicit Constructor Calls

If you don't explicitly call a superclass constructor using super(), Java automatically inserts a call to the superclass's no-argument constructor as the first statement in your constructor:

// This code:
public Student(String studentId) {
    this.studentId = studentId;
}

// Is equivalent to:
public Student(String studentId) {
    super();  // Implicitly added by Java
    this.studentId = studentId;
}

This means the superclass must have a no-argument constructor available. If the superclass doesn't have a no-argument constructor, you'll get a compilation error unless you explicitly call a different superclass constructor.

Calling Different Superclass Constructors

A superclass might have multiple constructors, and you can call any of them using super() with the appropriate arguments:

public class Vehicle {
    private String make;
    private String model;
    private int year;
    
    // Constructor with all parameters
    public Vehicle(String make, String model, int year) {
        this.make = make;
        this.model = model;
        this.year = year;
    }
    
    // Constructor with make and model only (default year)
    public Vehicle(String make, String model) {
        this(make, model, 2023);  // Call the other constructor
    }
    
    // No-argument constructor
    public Vehicle() {
        this("Unknown", "Unknown", 2023);  // Call the main constructor
    }
}

public class Car extends Vehicle {
    private int numberOfDoors;
    
    // Call the 3-parameter superclass constructor
    public Car(String make, String model, int year, int numberOfDoors) {
        super(make, model, year);
        this.numberOfDoors = numberOfDoors;
    }
    
    // Call the 2-parameter superclass constructor
    public Car(String make, String model, int numberOfDoors) {
        super(make, model);  // Uses the current year by default
        this.numberOfDoors = numberOfDoors;
    }
    
    // Call the no-argument superclass constructor
    public Car(int numberOfDoors) {
        super();  // Uses default values for make, model, and year
        this.numberOfDoors = numberOfDoors;
    }
}

The Constructor Execution Sequence

When a subclass object is created, the constructors execute in a specific sequence:

  1. Memory is allocated for the entire object (including superclass portions)
  2. The superclass constructor chain executes (all the way up to Object)
  3. Instance variables are initialized to their default values
  4. Superclass initializer blocks run (in order of appearance)
  5. The superclass constructor body executes
  6. Subclass initializer blocks run (in order of appearance)
  7. The subclass constructor body executes

This sequence ensures that the superclass is fully initialized before the subclass code runs, regardless of whether the superclass constructor is called implicitly or explicitly.

The Role of the Object Constructor

All classes in Java ultimately inherit from the Object class. This means every constructor chain eventually calls the Object constructor. The Object constructor is a no-argument constructor that initializes the fundamental state of any Java object.

// Complete constructor chain for Student object
new Student(...);  // Start with Student constructor
    super(...);    // Student calls Person constructor
        super();   // Person calls Object constructor
            // Object constructor executes
        // Person constructor completes
    // Student constructor completes
// Student object fully initialized

Common Mistakes and Problems

1. Missing No-Argument Constructor

If your superclass doesn't have a no-argument constructor and you don't explicitly call another constructor, you'll get a compilation error:

// Superclass with no default constructor
public class Person {
    private String name;
    
    // Only constructor requires a name
    public Person(String name) {
        this.name = name;
    }
}

// This will cause a compilation error
public class Student extends Person {
    private String studentId;
    
    // Error: Person doesn't have a no-arg constructor
    public Student(String studentId) {
        // Implicit super() call fails
        this.studentId = studentId;
    }
}

// Fix: Explicitly call the available constructor
public class Student extends Person {
    private String studentId;
    
    public Student(String name, String studentId) {
        super(name);  // Explicitly call the available constructor
        this.studentId = studentId;
    }
}

2. Calling super() After Other Statements

The call to the superclass constructor must be the first statement in the subclass constructor:

// This will cause a compilation error
public Student(String name, int age, String studentId) {
    this.studentId = studentId;  // Error: This must come after super()
    super(name, age);  // Error: super() must be first statement
}

3. Failing to Initialize All Instance Variables

Make sure all instance variables are properly initialized:

// Problematic: Doesn't initialize age
public Student(String name, String studentId) {
    super(name);  // Only initializes name, not age
    this.studentId = studentId;
}

// Better: Ensures all variables are initialized
public Student(String name, String studentId) {
    super(name, 0);  // Initialize age with a default value
    this.studentId = studentId;
}

Practical Example: Multi-Level Inheritance

Let's look at a complete example with multi-level inheritance:

// Top-level superclass
public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person constructor executed");
    }
    
    // Getters and other methods...
}

// Mid-level class
public class Student extends Person {
    private String studentId;
    
    public Student(String name, int age, String studentId) {
        super(name, age);
        this.studentId = studentId;
        System.out.println("Student constructor executed");
    }
    
    // Additional methods...
}

// Subclass of Student
public class GraduateStudent extends Student {
    private String researchArea;
    
    public GraduateStudent(String name, int age, String studentId, String researchArea) {
        super(name, age, studentId);
        this.researchArea = researchArea;
        System.out.println("GraduateStudent constructor executed");
    }
    
    // Additional methods...
}

// Using the classes
public class Main {
    public static void main(String[] args) {
        GraduateStudent grad = new GraduateStudent("John", 25, "G12345", "Computer Science");
        // Output:
        // Person constructor executed
        // Student constructor executed
        // GraduateStudent constructor executed
    }
}

The output shows the execution order of constructors, from the top of the hierarchy down to the most specific subclass.

Best Practices for Subclass Constructors

  1. Always call the appropriate superclass constructor: Choose the constructor that best initializes the inherited state.

  2. Pass meaningful values to the superclass: Don't just pass default values unless appropriate.

  3. Document constructor requirements: Make it clear which superclass constructor is called and why.

  4. Keep constructors focused: Each constructor should only initialize the state of its class.

  5. Provide multiple constructors if needed: Different initialization scenarios may require different constructors.

  6. Consider using constructor chaining: Use this() to call other constructors in the same class to reduce duplication.

public class Student extends Person {
    private String studentId;
    private String major;
    
    // Primary constructor
    public Student(String name, int age, String studentId, String major) {
        super(name, age);
        this.studentId = studentId;
        this.major = major;
    }
    
    // Secondary constructor with default major
    public Student(String name, int age, String studentId) {
        this(name, age, studentId, "Undeclared");  // Call the primary constructor
    }
}

Summary

Constructors play a vital role in inheritance by ensuring that objects are properly initialized at all levels of the class hierarchy. Remember that constructors are not inherited, but subclass constructors must call a superclass constructor (either explicitly with super() or implicitly). The super() call must be the first statement in the subclass constructor, and it provides the values needed to initialize the inherited instance variables. By following these rules and best practices, you can create class hierarchies where objects are correctly initialized with all the necessary data, maintaining the integrity of your object-oriented designs.

Key Terms to Review (10)

Area() method: The area() method is a function defined within a class that calculates and returns the area of an object, such as a shape or figure.
Constructors: Constructors are special methods within classes that are used to initialize objects when they are created. They have the same name as the class and are called automatically when an object is instantiated.
Extends: In Java, the keyword "extends" is used to create a subclass that inherits properties and methods from a superclass. It establishes an "is-a" relationship between classes.
IsEquivalent() method: The isEquivalent() method is used to compare two objects or values to determine if they are equivalent or equal in some way. It typically returns true if they are equivalent, and false otherwise.
Object class: The object class is a blueprint or template for creating objects in object-oriented programming. It defines the properties and behaviors that an object of that class will have.
Quadrilateral: A quadrilateral is a polygon with four sides. It is characterized by having four vertices (corners) and four angles.
Rectangle: A rectangle is a specific type of quadrilateral with four right angles (90 degrees). It has opposite sides that are equal in length.
Subclass: A subclass is a class that inherits properties and behaviors from another class, called the superclass. It can add new features or modify existing ones.
Super keyword: The super keyword is used in Java to refer to the superclass of a subclass. It allows access to the superclass's methods, constructors, and instance variables.
Superclass: A superclass, also known as a parent class or base class, is a class that is extended by another class (subclass). It provides common attributes and behaviors that can be inherited by its subclasses.