# Introduction to Inheritance in Java (including interfaces and abstract classes)

Imagine you’re building software for different types of vehicles: cars, trucks, and motorcycles. Each of these vehicles shares common attributes like *speed*, *color*, and *fuel capacity*, as well as behaviors like *accelerate*, *brake*, and *refuel*. Instead of writing the same code repeatedly for each type of vehicle, inheritance allows you to define these common attributes and behaviors once in a **base class** (or **parent class**). You can then create specialized versions (child classes) that extend the base class and add their own unique features.

This approach promotes **code reuse** (avoiding redundancy) and **extensibility** (easily adding new features or types).

### **What is Inheritance?**

Inheritance is an **object-oriented programming concept** where one class (called the **child class** or **subclass**) can acquire the properties (fields) and behaviors (methods) of another class (called the **parent class** or **superclass**).

In Java, inheritance helps in:

1. **Code Reusability**: Avoid rewriting code by reusing functionality defined in a parent class.
    
2. **Polymorphism**: Treat different classes uniformly if they share a common superclass.
    
3. **Extensibility**: Extend existing classes to introduce new functionalities without modifying the parent class.
    

### **Syntax of Inheritance in Java**

In Java, inheritance is implemented using the `extends` keyword. Here's the basic syntax:

```java
class ParentClass {
    // Fields (attributes)
    int speed;
    
    // Methods (behaviors)
    void displaySpeed() {
        System.out.println("Speed: " + speed);
    }
}

class ChildClass extends ParentClass {
    // Additional fields
    String model;

    // Additional methods
    void displayModel() {
        System.out.println("Model: " + model);
    }
}
```

#### **Example of Inheritance in Action**

```java
// Parent class
class Vehicle {
    int speed = 60;
    
    void displaySpeed() {
        System.out.println("Speed: " + speed + " km/h");
    }
}

// Child class inheriting from Vehicle
class Car extends Vehicle {
    String model = "Sedan";

    void displayDetails() {
        System.out.println("Car Model: " + model);
        displaySpeed(); // Reusing method from the parent class
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Car myCar = new Car();
        myCar.displayDetails();
    }
}
```

**Output:**

```java
Car Model: Sedan
Speed: 60 km/h
```

In this example, the `Car` class inherits the `speed` attribute and `displaySpeed()` method from the `Vehicle` class, demonstrating code reuse.

### **The Role of the** `super` Keyword

In Java, the `super` keyword refers to the parent class (superclass) of the current object. It is used for:

1. **Accessing parent class methods** that are overridden in the child class.
    
2. **Accessing parent class fields** when the child class has a field with the same name.
    
3. **Calling the parent class constructor** to initialize fields in the parent class.
    

#### **Example of** `super` Keyword

```java
class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void sound() {
        super.sound(); // Calls the parent class method
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        myDog.sound();
    }
}
```

**Output:**

```java
Animal makes a sound
Dog barks
```

#### **Key Points:**

1. `super.sound()` calls the `sound()` method from the `Animal` class.
    
2. This allows the child class (`Dog`) to reuse and extend the behavior of the parent class.
    

## **Types of Inheritance**

Different software design problems require different approaches to inheritance. Depending on how your classes relate to one another, you might need a single direct relationship or more complex hierarchies where one class builds upon another, or multiple classes share common behavior. Understanding the different inheritance models in Java helps you choose the most effective structure for your code, promoting clarity, reuse, and maintainability.

Java supports **three types of inheritance**:

1. **Single Inheritance**
    
2. **Multilevel Inheritance**
    
3. **Hierarchical Inheritance**
    

**Note:** Java does not support **multiple inheritance** (a class inheriting from more than one class) to avoid ambiguity, but similar functionality can be achieved using **interfaces**.

### **1\. Single Inheritance**

In **single inheritance**, a class inherits from just one parent class. This is the simplest form of inheritance.

#### **Example of Single Inheritance**

```java
// Parent class
class Animal {
    void eat() {
        System.out.println("This animal can eat");
    }
}

// Child class inheriting from Animal
class Dog extends Animal {
    void bark() {
        System.out.println("The dog barks");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        myDog.eat();  // Inherited method
        myDog.bark(); // Method specific to Dog
    }
}
```

**Output:**

```java
This animal can eat
The dog barks
```

#### **Explanation:**

* `Dog` inherits the `eat()` method from `Animal` and defines its own method `bark()`.
    

### **2\. Multilevel Inheritance**

In **multilevel inheritance**, a class inherits from a child class, making it a grandchild class. This forms a chain of inheritance.

#### **Example of Multilevel Inheritance**

```java
// Grandparent class
class Animal {
    void eat() {
        System.out.println("This animal can eat");
    }
}

// Parent class inheriting from Animal
class Dog extends Animal {
    void bark() {
        System.out.println("The dog barks");
    }
}

// Child class inheriting from Dog
class Puppy extends Dog {
    void weep() {
        System.out.println("The puppy weeps");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Puppy myPuppy = new Puppy();
        myPuppy.eat();   // Inherited from Animal
        myPuppy.bark();  // Inherited from Dog
        myPuppy.weep();  // Method specific to Puppy
    }
}
```

**Output:**

```java
This animal can eat
The dog barks
The puppy weeps
```

#### **Explanation:**

* `Puppy` inherits methods from both `Dog` and `Animal` classes, forming a chain of inheritance.
    

### **3\. Hierarchical Inheritance**

In **hierarchical inheritance**, multiple child classes inherit from the same parent class.

#### **Example of Hierarchical Inheritance**

```java
// Parent class
class Animal {
    void eat() {
        System.out.println("This animal can eat");
    }
}

// Child class 1
class Dog extends Animal {
    void bark() {
        System.out.println("The dog barks");
    }
}

// Child class 2
class Cat extends Animal {
    void meow() {
        System.out.println("The cat meows");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        Cat myCat = new Cat();

        myDog.eat();  // Inherited from Animal
        myDog.bark(); // Dog-specific method

        myCat.eat();  // Inherited from Animal
        myCat.meow(); // Cat-specific method
    }
}
```

**Output:**

```java
This animal can eat
The dog barks
This animal can eat
The cat meows
```

#### **Explanation:**

* Both `Dog` and `Cat` inherit the `eat()` method from the `Animal` class, but each has its own behavior (`bark()` for `Dog`, `meow()` for `Cat`).
    

## **Method Overriding**

Imagine you are designing a software system with a parent class `Animal` that has a method `sound()` to represent the sound an animal makes. If you create a child class `Dog`, you would want the `sound()` method to produce a dog-specific sound (like barking). Instead of modifying the `sound()` method in the parent class (which would affect all other child classes), you can **redefine** it in the `Dog` class. This is known as **method overriding**.

Method overriding allows you to:

1. **Customize behavior** of inherited methods for specific subclasses.
    
2. Enable **runtime polymorphism** where the appropriate method is called based on the actual object type.
    
3. Enhance **code flexibility** and maintainability without altering the parent class.
    

### **What is Method Overriding?**

**Method overriding** occurs when a child class provides a specific implementation of a method that is already defined in its parent class. The method in the child class must have:

1. The **same name** as the method in the parent class.
    
2. The **same parameters** (number and type).
    
3. The **same return type** (or a subtype).
    

### **Rules of Method Overriding**

1. **Same Method Signature**: The overriding method must have the same name, return type, and parameters as the method in the parent class.
    
2. **Inheritance Requirement**: Method overriding requires a parent-child relationship (inheritance).
    
3. **Access Modifier**: The overriding method cannot have a more restrictive access modifier than the method in the parent class:
    
    * If the parent method is `public`, the overriding method must also be `public`.
        
    * If the parent method is `protected`, the overriding method can be `protected` or `public`.
        
4. **Static Methods**: Static methods cannot be overridden (they can only be hidden by static methods in child classes).
    
5. **Final Methods**: Methods declared as `final` in the parent class cannot be overridden.
    
6. **Private Methods**: Private methods are not visible to child classes and thus cannot be overridden.
    
7. **Constructor Rules**: Constructors cannot be overridden.
    

### **The Significance of the** `@Override` Annotation

The `@Override` annotation is used to indicate that a method is intended to override a method in the parent class. It has several benefits:

1. **Compile-Time Checking**: If the method signature does not match any method in the parent class, the compiler will generate an error.
    
2. **Code Clarity**: It makes the code more readable and clearly indicates the developer's intent to override a method.
    
3. **Reduces Bugs**: Helps catch mistakes, such as typos in the method name or incorrect parameters, which might otherwise lead to subtle bugs.
    

#### **Example of** `@Override` in Action

```java
// Parent class
class Animal {
    void sound() {
        System.out.println("Some generic animal sound");
    }
}

// Child class overriding the sound method
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("The dog barks");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        Animal myDog = new Dog();

        myAnimal.sound();  // Calls the parent class method
        myDog.sound();     // Calls the overridden method in the Dog class
    }
}
```

**Output:**

```java
Some generic animal sound
The dog barks
```

### **Explanation of the Example**

1. **Parent Class (**`Animal`):
    
    * Contains a `sound()` method with generic behavior.
        
2. **Child Class (**`Dog`):
    
    * Overrides the `sound()` method to provide dog-specific behavior.
        
    * The `@Override` annotation ensures that the compiler checks for correctness.
        
3. **Main Class**:
    
    * Demonstrates **runtime polymorphism**. Even though `myDog` is declared as type `Animal`, it calls the overridden method in the `Dog` class because its actual object is a `Dog`.
        

## **Runtime Polymorphism (Dynamic Method Dispatch)**

In software development, flexibility and maintainability are essential for building scalable and adaptable applications. Imagine a scenario where you have a class hierarchy with different types of shapes like `Circle`, `Rectangle`, and `Triangle`. You want to write code that processes these shapes uniformly without knowing their specific types. **Polymorphism** allows you to achieve this by enabling a single interface (e.g., a parent class `Shape`) to represent multiple types of objects (e.g., `Circle`, `Rectangle`, `Triangle`).

**Runtime polymorphism**, also known as **dynamic method dispatch**, allows method calls to be resolved at runtime rather than compile time. This means that the appropriate method implementation is chosen based on the actual object being referenced, not the reference type. This flexibility allows for cleaner, more maintainable, and extensible code.

### **Explanation of Dynamic Method Dispatch**

**Dynamic method dispatch** is a mechanism by which a call to an overridden method is resolved at runtime. It works as follows:

1. When a method in a child class overrides a method in a parent class, the overridden method is invoked based on the **actual type of the object** (the runtime type), even if the reference is of the parent class type.
    
2. This allows the same reference type to call different method implementations depending on the actual object it refers to.
    

**Key Points**:

* The reference type determines what methods can be called.
    
* The object type (the actual instance) determines which version of the method is executed.
    

### **How Method Calls are Resolved at Runtime**

1. **Compile Time**: The compiler checks whether the method exists in the reference type (usually the parent class).
    
2. **Runtime**: The JVM determines the actual object type and calls the appropriate overridden method.
    

### **Code Example: Demonstrating Runtime Polymorphism**

Let's illustrate dynamic method dispatch with an example involving animals making different sounds.

```java
// Parent class
class Animal {
    void sound() {
        System.out.println("Some generic animal sound");
    }
}

// Child class 1
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("The dog barks");
    }
}

// Child class 2
class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("The cat meows");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        // Parent class reference, different child class objects
        Animal myAnimal;
        
        myAnimal = new Dog();
        myAnimal.sound();  // Output: The dog barks
        
        myAnimal = new Cat();
        myAnimal.sound();  // Output: The cat meows
    }
}
```

**Output:**

```java
The dog barks
The cat meows
```

### **Explanation of the Example**

1. **Parent Class (**`Animal`):
    
    * Defines a generic `sound()` method.
        
2. **Child Classes (**`Dog` and `Cat`):
    
    * Each class overrides the `sound()` method to provide specific behavior.
        
3. **Main Class**:
    
    * The reference `myAnimal` is of type `Animal`, but it is assigned different child class objects (`Dog` and `Cat`).
        
    * **Dynamic Method Dispatch** ensures that the correct overridden method (`Dog`'s `sound()` or `Cat`'s `sound()`) is called based on the actual object type at runtime.
        

### **Benefits of Runtime Polymorphism**

1. **Flexibility**: Code can work with different types of objects through a common interface.
    
2. **Maintainability**: Adding new subclasses doesn't require changing existing code.
    
3. **Extensibility**: New behaviors can be introduced by creating new subclasses without modifying existing logic.
    

#### **Example of Flexibility**

Imagine processing a list of different animal types:

```java
public class Main {
    public static void main(String[] args) {
        Animal[] animals = { new Dog(), new Cat(), new Animal() };

        for (Animal animal : animals) {
            animal.sound();  // Each object calls its specific sound() method
        }
    }
}
```

**Output:**

```java
The dog barks
The cat meows
Some generic animal sound
```

## **Abstract Classes**

In software development, we often encounter situations where we need to model a general concept while leaving some details to be specified by more specialized classes. For example, consider the concept of a **Shape**: all shapes have common characteristics, such as a method to calculate the area, but the implementation of calculating the area differs for different shapes like circles, rectangles, and triangles.

**Abstract classes** provide a way to define a blueprint for a group of related classes. They allow you to declare methods that must be implemented by any class that extends the abstract class, enforcing a consistent design while allowing flexibility in the specific implementations.

Use abstract classes when:

* You want to provide a **common base class** with some shared behavior but allow subclasses to provide specific implementations.
    
* You want to enforce that certain methods **must be implemented** by subclasses.
    
* You need to include both **defined methods (with bodies)** and **undefined methods (abstract methods)** in the base class.
    

### **Defining Abstract Classes and Abstract Methods**

#### **What is an Abstract Class?**

An **abstract class** is a class that cannot be instantiated on its own. It can contain:

1. **Abstract Methods**: Methods without a body (implementation), which must be implemented by subclasses.
    
2. **Concrete Methods**: Methods with a body that provide default behavior.
    

An abstract class is declared using the `abstract` keyword:

```java
abstract class ClassName {
    abstract void abstractMethod(); // Abstract method (no body)
    void concreteMethod() {         // Concrete method (has a body)
        System.out.println("This is a concrete method.");
    }
}
```

#### **What is an Abstract Method?**

An **abstract method** is a method declared in an abstract class without a body. Subclasses that extend the abstract class must provide an implementation for the abstract method:

```java
abstract void abstractMethod(); // No implementation
```

### **Differences Between Abstract Classes and Concrete Classes**

| **Feature** | **Abstract Class** | **Concrete Class** |
| --- | --- | --- |
| **Instantiation** | Cannot be instantiated directly. | Can be instantiated directly. |
| **Abstract Methods** | Can contain abstract methods (methods without a body). | Cannot contain abstract methods. |
| **Concrete Methods** | Can contain concrete methods (methods with a body). | Only contains concrete methods. |
| **Usage** | Used to define a common blueprint for subclasses. | Used for creating fully-defined objects. |

### **Code Example: Using an Abstract Class to Model Real-World Scenarios**

Let's model different types of shapes using an abstract class `Shape` that defines a method to calculate the area. Subclasses `Circle` and `Rectangle` will provide specific implementations of the `calculateArea()` method.

```java
// Abstract class
abstract class Shape {
    // Abstract method
    abstract double calculateArea();

    // Concrete method
    void displayShape() {
        System.out.println("This is a shape.");
    }
}

// Subclass 1: Circle
class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double calculateArea() {
        return Math.PI * radius * radius;
    }
}

// Subclass 2: Rectangle
class Rectangle extends Shape {
    double length;
    double width;

    Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    @Override
    double calculateArea() {
        return length * width;
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        Shape rectangle = new Rectangle(4.0, 6.0);

        circle.displayShape();
        System.out.println("Circle Area: " + circle.calculateArea());

        rectangle.displayShape();
        System.out.println("Rectangle Area: " + rectangle.calculateArea());
    }
}
```

#### **Output:**

```java
This is a shape.
Circle Area: 78.53981633974483
This is a shape.
Rectangle Area: 24.0
```

### **Explanation of the Code**

1. **Abstract Class (**`Shape`):
    
    * Defines an abstract method `calculateArea()` that must be implemented by any subclass.
        
    * Contains a concrete method `displayShape()`.
        
2. **Subclasses (**`Circle` and `Rectangle`):
    
    * Provide specific implementations of the `calculateArea()` method.
        
    * Each subclass has fields specific to its shape (e.g., `radius` for `Circle` and `length`/`width` for `Rectangle`).
        
3. **Main Class**:
    
    * Demonstrates **polymorphism** by referencing `Shape` and calling methods on different shape objects.
        
    * The `displayShape()` method is reused for both shapes, while `calculateArea()` is customized for each type.
        

### **When to Use Abstract Classes**

* When you have a **common concept** (e.g., `Shape`, `Animal`) with **shared behavior** that needs to be partially implemented.
    
* When you want to **enforce a contract** (certain methods must be implemented) but also provide some **default behavior**.
    
* When you need to work with **hierarchies of related classes**.
    

## **Interfaces**

Java supports **single inheritance** of classes to avoid ambiguity. However, real-world design problems often require a class to inherit behavior from multiple sources. For example, a `RobotDog` might need the behaviors of both a `Dog` and a `Machine`. This is where **interfaces** come into play.

Interfaces provide a mechanism for achieving **multiple inheritance** in Java. They allow a class to inherit behavior from multiple sources while maintaining design flexibility. Additionally, interfaces help in defining **contracts** that classes must follow, ensuring consistency across different implementations.

### **Defining and Implementing Interfaces**

An **interface** in Java is a collection of **abstract methods** and **constants**. Interfaces cannot contain method bodies (except default and static methods). A class implements an interface using the `implements` keyword and must provide implementations for all the abstract methods declared in the interface.

#### **Defining an Interface**

```java
interface Animal {
    // Abstract method (no implementation)
    void sound();

    // Constant (public, static, and final by default)
    int LEGS = 4; // Equivalent to: public static final int LEGS = 4;
}
```

#### **Implementing an Interface**

```java
class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("The dog barks");
    }
}
```

### **Differences Between Interfaces and Abstract Classes**

| **Feature** | **Interface** | **Abstract Class** |
| --- | --- | --- |
| **Instantiation** | Cannot be instantiated. | Cannot be instantiated. |
| **Method Types** | Only abstract methods (default and static methods allowed). | Can have abstract and concrete methods. |
| **Fields** | Only constants (`public static final`). | Can have instance variables and constants. |
| **Inheritance** | A class can implement multiple interfaces. | A class can extend only one abstract class. |
| **Access Modifiers** | Methods are `public` by default. | Can have any access modifier. |
| **Use Case** | Used for defining a contract (behavior). | Used for defining a blueprint with partial behavior. |

### **Example: Implementing Multiple Interfaces**

In this example, a `RobotDog` class implements both `Animal` and `Machine` interfaces, demonstrating multiple inheritance.

```java
// First interface
interface Animal {
    void sound();
    int LEGS = 4; // Constant
}

// Second interface
interface Machine {
    void recharge();
}

// Class implementing both interfaces
class RobotDog implements Animal, Machine {
    @Override
    public void sound() {
        System.out.println("The robot dog makes a mechanical bark");
    }

    @Override
    public void recharge() {
        System.out.println("Recharging robot dog");
    }

    void displayLegs() {
        System.out.println("Robot dog has " + LEGS + " legs");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        RobotDog myRobotDog = new RobotDog();
        myRobotDog.sound();
        myRobotDog.recharge();
        myRobotDog.displayLegs();
    }
}
```

**Output:**

```java
The robot dog makes a mechanical bark
Recharging robot dog
Robot dog has 4 legs
```

#### **Explanation:**

* The `RobotDog` class implements two interfaces, `Animal` and `Machine`.
    
* It provides implementations for the `sound()` and `recharge()` methods.
    
* The constant `LEGS` is accessed from the `Animal` interface.
    

### **Reference Type and Method Execution**

In Java, a reference type can be:

1. **An Interface**
    
2. **An Abstract Class**
    
3. **A Parent Class**
    

The reference type dictates **what methods can be called** (methods available in the reference type). However, the **actual method executed** is determined by the **type of object** (the instance created on the right side with `new`).

#### **Example Demonstrating Different Reference Types**

```java
interface Animal {
    void sound();
}

abstract class Pet {
    abstract void sleep();
}

class Dog extends Pet implements Animal {
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }

    @Override
    void sleep() {
        System.out.println("Dog sleeps");
    }

    void wagTail() {
        System.out.println("Dog wags tail");
    }
}

public class Main {
    public static void main(String[] args) {
        // Reference type is Animal (Interface)
        Animal myAnimal = new Dog();
        myAnimal.sound(); // Allowed by Animal interface
        // myAnimal.sleep(); // Not allowed (sleep() not in Animal)

        // Reference type is Pet (Abstract Class)
        Pet myPet = new Dog();
        myPet.sleep();  // Allowed by Pet abstract class
        // myPet.sound(); // Not allowed (sound() not in Pet)

        // Reference type is Dog (Concrete Class)
        Dog myDog = new Dog();
        myDog.sound();   // Allowed
        myDog.sleep();   // Allowed
        myDog.wagTail(); // Allowed
    }
}
```

**Output:**

```java
Dog barks
Dog sleeps
Dog barks
Dog sleeps
Dog wags tail
```

#### **Explanation:**

1. `Animal myAnimal = new Dog();`:
    
    * The reference type `Animal` allows calling the `sound()` method defined in the `Animal` interface.
        
    * The actual method that runs is `Dog`'s `sound()`.
        
2. `Pet myPet = new Dog();`:
    
    * The reference type `Pet` allows calling the `sleep()` method defined in the `Pet` abstract class.
        
    * The actual method that runs is `Dog`'s `sleep()`.
        
3. `Dog myDog = new Dog();`:
    
    * The reference type `Dog` allows calling all methods specific to `Dog`.
        

### **Interfaces and Constants**

Interfaces can contain **constants**, which are implicitly `public`, `static`, and `final`. These constants are accessible via the interface name and cannot be changed once assigned.

#### **Example with Constants in an Interface**

```java
interface GameSettings {
    int MAX_PLAYERS = 4;         // public static final by default
    String GAME_NAME = "Chess";  // public static final by default
}

class ChessGame implements GameSettings {
    void displaySettings() {
        System.out.println("Game: " + GAME_NAME);
        System.out.println("Max Players: " + MAX_PLAYERS);
    }
}

public class Main {
    public static void main(String[] args) {
        ChessGame game = new ChessGame();
        game.displaySettings();
    }
}
```

**Output:**

```java
Game: Chess
Max Players: 4
```

#### **Explanation:**

* The constants `MAX_PLAYERS` and `GAME_NAME` are defined in the `GameSettings` interface.
    
* They are accessed in the `ChessGame` class without needing to redefine them.
    

In Java, if a **public abstract class** or a **public interface** is declared, the source file must be named after that class or interface, with the `.java` extension. This rule is the same as for regular public classes. For example, if you declare `public abstract class Animal` or `public interface Animal`, the file must be named [`Animal.java`](http://Animal.java). If multiple classes or interfaces are declared in the same file, only **one** of them can be public, and the filename must match the public class or interface name.

### Implicit Constructor Chaining in Inheritance

When a subclass is instantiated, Java automatically ensures that all constructors in the inheritance hierarchy are called **from the top down** (parent to child). This is called **constructor chaining**.

**How it works:**

1. The subclass constructor implicitly calls the default constructor (`super()`) of its immediate parent.
    
2. This process repeats up the inheritance chain until the `Object` class constructor is reached.
    
3. Only then does the subclass constructor execute its own logic.
    

**Example:**

```java
class Animal {
    public Animal() {
        System.out.println("Animal constructor: Default");
    }
}

class Dog extends Animal {
    public Dog() {
        // Implicit super() call to Animal's default constructor
        System.out.println("Dog constructor: Default");
    }
}

class GermanShepherd extends Dog {
    public GermanShepherd() {
        // Implicit super() call to Dog's default constructor
        System.out.println("GermanShepherd constructor: Default");
    }
}

public class Main {
    public static void main(String[] args) {
        new GermanShepherd();
    }
}
```

**Output:**

```plaintext
Animal constructor: Default
Dog constructor: Default
GermanShepherd constructor: Default
```

**Key Points:**

* Works automatically when no explicit `super` is used
    
* Requires parent classes to have default constructors
    
* Execution order: Root ancestor → ... → Immediate parent → Current class
    

### Explicit Constructor Chaining with `super`

When you need to call specific parent constructors (especially parameterized ones), use the `super` keyword explicitly.

**Rules:**

1. Must be the **first statement** in the subclass constructor
    
2. Can call any overloaded parent constructor
    
3. Required when parent lacks a default constructor
    

**Example:**

```java
class Vehicle {
    private String make;
    private String model;
    
    public Vehicle(String make) {
        this.make = make;
        System.out.println("Vehicle constructor: Make only");
    }

    public Vehicle(String make, String model) {
        this.make = make;
        this.model = model;
        System.out.println("Vehicle constructor: Make & Model");
    }
}

class Car extends Vehicle {
    private int year;

    public Car(String make, String model, int year) {
        // Explicitly choose the two-parameter parent constructor
        super(make, model);
        this.year = year;
        System.out.println("Car constructor: Three parameters");
    }

    public Car(String make, int year) {
        // Chain to sibling constructor using this()
        this(make, "Unknown Model", year);
    }
}

public class Main {
    public static void main(String[] args) {
        Car sedan = new Car("Toyota", "Camry", 2023);
        System.out.println("\n");
        Car suv = new Car("Ford", 2022);
    }
}
```

**Output:**

```plaintext
Vehicle constructor: Make & Model
Car constructor: Three parameters


Vehicle constructor: Make & Model
Car constructor: Three parameters
```

**Key Points:**

* `super(make, model)` explicitly calls `Vehicle`'s two-parameter constructor
    
* The `this()` in the second `Car` constructor demonstrates constructor chaining within the same class
    
* Mixing `super()` and `this()` in the same constructor is illegal - only one can be first
