Java OOP Inheritance Tutorial
Core Concept

Java Inheritance - Complete Tutorial

Master Java Inheritance: Learn single, multilevel, hierarchical inheritance, super keyword, method overriding, constructors in inheritance, access modifiers, and real-world examples.

Code Reuse

Inherit properties & methods

extends Keyword

Create inheritance relationship

super Keyword

Access parent class members

Method Overriding

Provide specific implementation

1. Introduction to Inheritance

Inheritance is a fundamental concept in Object-Oriented Programming (OOP) that allows a class to inherit properties and methods from another class. The class that inherits is called the subclass (child class), and the class being inherited from is called the superclass (parent class).

Key Terminology
  • Superclass/Parent Class: Class being inherited from
  • Subclass/Child Class: Class that inherits
  • extends Keyword: Creates inheritance relationship
  • IS-A Relationship: Subclass IS-A type of superclass
  • Code Reusability: Reuse existing code
  • Method Overriding: Subclass provides specific implementation
Real-World Analogy
  • Vehicle → Car: Car inherits properties from Vehicle
  • Animal → Dog: Dog inherits characteristics from Animal
  • Employee → Manager: Manager inherits from Employee
  • Shape → Circle: Circle is a specific type of Shape
  • Device → Smartphone: Smartphone inherits from Device
  • Person → Student: Student is a type of Person

The Power of Inheritance

Inheritance promotes code reusability and establishes a natural hierarchy between classes. When you create a subclass, it automatically gets all the non-private fields and methods of the superclass. You can then add new features or modify existing ones in the subclass.

InheritanceDemo.java
// Superclass (Parent Class)
class Vehicle {
    // Instance variables
    protected String brand;
    protected String model;
    protected int year;
    protected double price;
    
    // Constructor
    public Vehicle(String brand, String model, int year, double price) {
        this.brand = brand;
        this.model = model;
        this.year = year;
        this.price = price;
        System.out.println("Vehicle constructor called");
    }
    
    // Methods
    public void start() {
        System.out.println(brand + " " + model + " is starting...");
    }
    
    public void stop() {
        System.out.println(brand + " " + model + " is stopping...");
    }
    
    public void displayInfo() {
        System.out.println("Brand: " + brand);
        System.out.println("Model: " + model);
        System.out.println("Year: " + year);
        System.out.println("Price: $" + price);
    }
    
    // Getters
    public String getBrand() { return brand; }
    public String getModel() { return model; }
    public int getYear() { return year; }
    public double getPrice() { return price; }
}

// Subclass (Child Class) - Car inherits from Vehicle
class Car extends Vehicle {
    // Additional instance variables specific to Car
    private int numberOfDoors;
    private String fuelType;
    private boolean hasSunroof;
    
    // Constructor
    public Car(String brand, String model, int year, double price,
               int numberOfDoors, String fuelType, boolean hasSunroof) {
        // Call superclass constructor using super()
        super(brand, model, year, price);
        
        // Initialize Car-specific properties
        this.numberOfDoors = numberOfDoors;
        this.fuelType = fuelType;
        this.hasSunroof = hasSunroof;
        System.out.println("Car constructor called");
    }
    
    // Additional methods specific to Car
    public void honk() {
        System.out.println(brand + " " + model + " is honking: Beep Beep!");
    }
    
    public void openSunroof() {
        if (hasSunroof) {
            System.out.println(brand + " " + model + " sunroof is opening...");
        } else {
            System.out.println(brand + " " + model + " doesn't have a sunroof");
        }
    }
    
    // Override displayInfo() method from Vehicle
    @Override
    public void displayInfo() {
        // Call parent class method using super
        super.displayInfo();
        
        // Add Car-specific information
        System.out.println("Number of Doors: " + numberOfDoors);
        System.out.println("Fuel Type: " + fuelType);
        System.out.println("Has Sunroof: " + (hasSunroof ? "Yes" : "No"));
    }
    
    // Getters for Car-specific properties
    public int getNumberOfDoors() { return numberOfDoors; }
    public String getFuelType() { return fuelType; }
    public boolean hasSunroof() { return hasSunroof; }
}

// Another Subclass - Motorcycle inherits from Vehicle
class Motorcycle extends Vehicle {
    // Additional instance variables
    private boolean hasSidecar;
    private String motorcycleType;
    
    // Constructor
    public Motorcycle(String brand, String model, int year, double price,
                      boolean hasSidecar, String motorcycleType) {
        super(brand, model, year, price);
        this.hasSidecar = hasSidecar;
        this.motorcycleType = motorcycleType;
        System.out.println("Motorcycle constructor called");
    }
    
    // Additional methods
    public void wheelie() {
        System.out.println(brand + " " + model + " is doing a wheelie!");
    }
    
    // Override start() method
    @Override
    public void start() {
        System.out.println(brand + " " + model + " motorcycle engine is roaring to life!");
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Has Sidecar: " + (hasSidecar ? "Yes" : "No"));
        System.out.println("Type: " + motorcycleType);
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        System.out.println("=== Inheritance Demonstration ===\n");
        
        // Create a Car object
        System.out.println("=== Creating Car Object ===");
        Car myCar = new Car("Toyota", "Camry", 2023, 25000.0, 4, "Gasoline", true);
        
        System.out.println("\n=== Car Methods ===");
        myCar.start();          // Inherited from Vehicle
        myCar.honk();           // Car-specific method
        myCar.openSunroof();    // Car-specific method
        myCar.stop();           // Inherited from Vehicle
        
        System.out.println("\n=== Car Information ===");
        myCar.displayInfo();    // Overridden method
        
        System.out.println("\n=== Creating Motorcycle Object ===");
        Motorcycle myBike = new Motorcycle("Harley-Davidson", "Sportster", 2022, 15000.0, false, "Cruiser");
        
        System.out.println("\n=== Motorcycle Methods ===");
        myBike.start();         // Overridden method
        myBike.wheelie();       // Motorcycle-specific method
        myBike.stop();          // Inherited from Vehicle
        
        System.out.println("\n=== Motorcycle Information ===");
        myBike.displayInfo();   // Overridden method
        
        System.out.println("\n=== Benefits of Inheritance ===");
        System.out.println("1. Code Reusability: Common code in Vehicle class");
        System.out.println("2. Extensibility: Easy to add new vehicle types");
        System.out.println("3. Maintainability: Changes in Vehicle affect all subclasses");
        System.out.println("4. Polymorphism: Vehicles can be treated uniformly");
        
        System.out.println("\n=== IS-A Relationship ===");
        System.out.println("Car IS-A Vehicle: " + (myCar instanceof Vehicle));
        System.out.println("Motorcycle IS-A Vehicle: " + (myBike instanceof Vehicle));
        System.out.println("Car IS-A Car: " + (myCar instanceof Car));
        System.out.println("Vehicle IS-A Car: " + (new Vehicle("Generic", "Model", 2020, 10000) instanceof Car));
    }
}
Inheritance Hierarchy Visualization
Vehicle
Car
ElectricCar
Motorcycle
Truck

Vehicle is the superclass. Car, Motorcycle, Truck are subclasses. ElectricCar is a subclass of Car (multilevel inheritance).

2. Types of Inheritance in Java

Java supports several types of inheritance through class extension. Understanding these types helps you design better class hierarchies.

Single Inheritance
  • One subclass extends one superclass
  • Most common type in Java
  • Simple and straightforward
  • Example: Class B extends Class A
  • Prevents diamond problem
  • Direct parent-child relationship
Multilevel Inheritance
  • Chain of inheritance
  • Class C extends Class B, which extends Class A
  • Grandparent → Parent → Child relationship
  • Example: Animal → Mammal → Dog
  • Supports code reuse across generations
  • Can access grandparent members via parent
Hierarchical Inheritance
  • One superclass, multiple subclasses
  • Multiple classes extend the same parent
  • Example: Shape → Circle, Square, Triangle
  • Promotes code sharing among siblings
  • Common interface for related classes
  • Enables polymorphism
Multiple Inheritance (NOT Supported)
  • One class extends multiple classes
  • NOT supported in Java
  • Causes diamond problem ambiguity
  • Alternative: Use interfaces
  • Java 8+: Default methods in interfaces
  • Example: Interface A, Interface B

Types of Inheritance Comparison

Type Description Diagram Supported in Java? Example
Single One subclass extends one superclass A → B Yes Car extends Vehicle
Multilevel Chain of inheritance (A → B → C) A → B → C Yes Animal → Mammal → Dog
Hierarchical Multiple subclasses extend one superclass A → B, A → C, A → D Yes Shape → Circle, Square, Triangle
Multiple One class extends multiple classes A → C ← B No Not supported (use interfaces)
Hybrid Combination of multiple types Complex Partial Through interfaces only
InheritanceTypes.java
public class InheritanceTypes {
    
    // === SINGLE INHERITANCE ===
    static class Employee {
        private String name;
        private int id;
        private double salary;
        
        public Employee(String name, int id, double salary) {
            this.name = name;
            this.id = id;
            this.salary = salary;
        }
        
        public void work() {
            System.out.println(name + " is working");
        }
        
        public double calculateBonus() {
            return salary * 0.10;
        }
    }
    
    // Single Inheritance: Manager extends Employee
    static class Manager extends Employee {
        private int teamSize;
        
        public Manager(String name, int id, double salary, int teamSize) {
            super(name, id, salary);
            this.teamSize = teamSize;
        }
        
        public void conductMeeting() {
            System.out.println(getName() + " is conducting a meeting with " + teamSize + " team members");
        }
        
        @Override
        public double calculateBonus() {
            return super.calculateBonus() + (teamSize * 500);
        }
        
        public String getName() {
            // Accessing parent's private field through getter (not shown)
            return "Manager: Access needed";
        }
    }
    
    // === MULTILEVEL INHERITANCE ===
    static class Animal {
        protected String species;
        
        public Animal(String species) {
            this.species = species;
        }
        
        public void eat() {
            System.out.println(species + " is eating");
        }
        
        public void sleep() {
            System.out.println(species + " is sleeping");
        }
    }
    
    static class Mammal extends Animal {
        protected boolean hasFur;
        
        public Mammal(String species, boolean hasFur) {
            super(species);
            this.hasFur = hasFur;
        }
        
        public void giveBirth() {
            System.out.println(species + " is giving birth to live young");
        }
    }
    
    // Multilevel Inheritance: Dog extends Mammal which extends Animal
    static class Dog extends Mammal {
        private String breed;
        
        public Dog(String breed, boolean hasFur) {
            super("Dog", hasFur);
            this.breed = breed;
        }
        
        public void bark() {
            System.out.println(breed + " dog is barking: Woof! Woof!");
        }
        
        @Override
        public void eat() {
            System.out.println(breed + " dog is eating dog food");
        }
        
        // Can access grandparent methods
        public void animalBehavior() {
            super.eat();  // Calls Mammal's eat() which might call Animal's eat()
            sleep();      // From Animal class
        }
    }
    
    // === HIERARCHICAL INHERITANCE ===
    static class Shape {
        protected String color;
        
        public Shape(String color) {
            this.color = color;
        }
        
        public double calculateArea() {
            return 0.0;
        }
        
        public double calculatePerimeter() {
            return 0.0;
        }
        
        public void display() {
            System.out.println("Shape color: " + color);
        }
    }
    
    // Multiple classes extending the same parent (Hierarchical)
    static class Circle extends Shape {
        private double radius;
        
        public Circle(String color, double radius) {
            super(color);
            this.radius = radius;
        }
        
        @Override
        public double calculateArea() {
            return Math.PI * radius * radius;
        }
        
        @Override
        public double calculatePerimeter() {
            return 2 * Math.PI * radius;
        }
        
        @Override
        public void display() {
            super.display();
            System.out.println("Shape: Circle, Radius: " + radius);
        }
    }
    
    static class Rectangle extends Shape {
        private double width;
        private double height;
        
        public Rectangle(String color, double width, double height) {
            super(color);
            this.width = width;
            this.height = height;
        }
        
        @Override
        public double calculateArea() {
            return width * height;
        }
        
        @Override
        public double calculatePerimeter() {
            return 2 * (width + height);
        }
        
        @Override
        public void display() {
            super.display();
            System.out.println("Shape: Rectangle, Width: " + width + ", Height: " + height);
        }
    }
    
    static class Triangle extends Shape {
        private double base;
        private double height;
        
        public Triangle(String color, double base, double height) {
            super(color);
            this.base = base;
            this.height = height;
        }
        
        @Override
        public double calculateArea() {
            return 0.5 * base * height;
        }
        
        @Override
        public double calculatePerimeter() {
            // For simplicity, assuming right triangle
            double hypotenuse = Math.sqrt(base * base + height * height);
            return base + height + hypotenuse;
        }
        
        @Override
        public void display() {
            super.display();
            System.out.println("Shape: Triangle, Base: " + base + ", Height: " + height);
        }
    }
    
    // === WHY NO MULTIPLE INHERITANCE? ===
    /*
    // This would cause the Diamond Problem
    class A {
        void display() { System.out.println("A"); }
    }
    
    class B extends A {
        @Override
        void display() { System.out.println("B"); }
    }
    
    class C extends A {
        @Override
        void display() { System.out.println("C"); }
    }
    
    // Which display() should D inherit? B's or C's?
    // class D extends B, C { } // NOT ALLOWED in Java
    
    // Solution: Use interfaces
    interface BInterface {
        default void display() { System.out.println("B Interface"); }
    }
    
    interface CInterface {
        default void display() { System.out.println("C Interface"); }
    }
    
    class D implements BInterface, CInterface {
        // Must override to resolve conflict
        @Override
        public void display() {
            BInterface.super.display(); // Choose which to call
        }
    }
    */
    
    public static void main(String[] args) {
        System.out.println("=== Types of Inheritance Demonstration ===\n");
        
        System.out.println("=== SINGLE INHERITANCE ===");
        Manager mgr = new Manager("Alice", 101, 80000, 5);
        mgr.work();                   // Inherited from Employee
        mgr.conductMeeting();         // Manager-specific
        System.out.println("Bonus: $" + mgr.calculateBonus()); // Overridden method
        
        System.out.println("\n=== MULTILEVEL INHERITANCE ===");
        Dog myDog = new Dog("Golden Retriever", true);
        myDog.eat();                  // Overridden in Dog
        myDog.sleep();                // From Animal (grandparent)
        myDog.giveBirth();            // From Mammal (parent)
        myDog.bark();                 // Dog-specific
        myDog.animalBehavior();       // Demonstrates multi-level access
        
        System.out.println("\n=== HIERARCHICAL INHERITANCE ===");
        Shape[] shapes = {
            new Circle("Red", 5.0),
            new Rectangle("Blue", 4.0, 6.0),
            new Triangle("Green", 3.0, 4.0)
        };
        
        for (Shape shape : shapes) {
            shape.display();
            System.out.println("Area: " + shape.calculateArea());
            System.out.println("Perimeter: " + shape.calculatePerimeter());
            System.out.println();
        }
        
        System.out.println("=== Inheritance Chain Analysis ===");
        System.out.println("Dog instance of Dog: " + (myDog instanceof Dog));
        System.out.println("Dog instance of Mammal: " + (myDog instanceof Mammal));
        System.out.println("Dog instance of Animal: " + (myDog instanceof Animal));
        System.out.println("Dog instance of Object: " + (myDog instanceof Object));
        
        System.out.println("\n=== Java's Inheritance Limitations ===");
        System.out.println("1. Single Class Inheritance: A class can extend only ONE class");
        System.out.println("2. Multiple Interface Inheritance: A class can implement MULTIPLE interfaces");
        System.out.println("3. All classes implicitly extend Object class");
        System.out.println("4. Final classes cannot be extended");
        
        System.out.println("\n=== Best Practices ===");
        System.out.println("1. Use inheritance for IS-A relationships");
        System.out.println("2. Keep inheritance hierarchies shallow (2-3 levels)");
        System.out.println("3. Favor composition over inheritance when appropriate");
        System.out.println("4. Use abstract classes for partial implementations");
        System.out.println("5. Use interfaces for defining contracts");
    }
}

3. super Keyword - Complete Guide

The super keyword in Java is used to refer to the immediate parent class. It has three main uses: calling parent class constructor, accessing parent class methods, and accessing parent class fields.

super() - Constructor Call
  • Must be first statement in constructor
  • Calls parent class constructor
  • If not specified, super() is implicitly called
  • Can pass parameters to parent constructor
  • Only one super() call per constructor
  • Chains constructors up the hierarchy
super.method() - Method Access
  • Calls overridden parent method
  • Extends rather than replaces parent behavior
  • Useful in method overriding
  • Can call any accessible parent method
  • Maintains parent functionality
  • Avoids code duplication
SuperKeywordDemo.java
public class SuperKeywordDemo {
    
    // === PARENT CLASS ===
    static class Person {
        protected String name;
        protected int age;
        protected String gender;
        protected String nationality;
        
        // Constructor 1: Default
        public Person() {
            this("Unknown", 0, "Unknown", "Unknown");
            System.out.println("Person() constructor called");
        }
        
        // Constructor 2: With parameters
        public Person(String name, int age, String gender, String nationality) {
            this.name = name;
            this.age = age;
            this.gender = gender;
            this.nationality = nationality;
            System.out.println("Person(String, int, String, String) constructor called");
        }
        
        // Constructor 3: Copy constructor
        public Person(Person other) {
            this(other.name, other.age, other.gender, other.nationality);
            System.out.println("Person(Person) copy constructor called");
        }
        
        // Methods
        public void introduce() {
            System.out.println("Hello, I'm " + name + ", " + age + " years old from " + nationality);
        }
        
        public void work() {
            System.out.println(name + " is working as a person");
        }
        
        public void celebrateBirthday() {
            age++;
            System.out.println(name + " is now " + age + " years old!");
        }
        
        // Getters
        public String getName() { return name; }
        public int getAge() { return age; }
        public String getGender() { return gender; }
        public String getNationality() { return nationality; }
    }
    
    // === CHILD CLASS 1: Student ===
    static class Student extends Person {
        private String studentId;
        private String major;
        private double gpa;
        
        // Constructor 1: Using super() to call specific parent constructor
        public Student(String name, int age, String gender, String nationality,
                      String studentId, String major, double gpa) {
            // Call parent constructor with super()
            super(name, age, gender, nationality);
            
            this.studentId = studentId;
            this.major = major;
            this.gpa = gpa;
            System.out.println("Student constructor called");
        }
        
        // Constructor 2: Using default parent constructor
        public Student(String studentId, String major) {
            // super() is called implicitly (calls Person() default constructor)
            this.studentId = studentId;
            this.major = major;
            this.gpa = 0.0;
            System.out.println("Student(String, String) constructor called");
        }
        
        // Constructor 3: Copy another student
        public Student(Student other) {
            // Call parent copy constructor
            super(other);
            
            this.studentId = other.studentId;
            this.major = other.major;
            this.gpa = other.gpa;
            System.out.println("Student(Student) copy constructor called");
        }
        
        // Override introduce() method
        @Override
        public void introduce() {
            // Call parent's introduce() using super
            super.introduce();
            
            // Add Student-specific information
            System.out.println("I'm a student studying " + major + " with GPA: " + gpa);
        }
        
        // Override work() method
        @Override
        public void work() {
            // Extend parent behavior instead of replacing
            super.work();  // Person's work() method
            System.out.println(name + " is also studying for exams");
        }
        
        // Student-specific method
        public void takeExam(String course) {
            System.out.println(name + " (ID: " + studentId + ") is taking " + course + " exam");
        }
        
        // Method that uses super to access parent's private field through getter
        public void displayFullInfo() {
            System.out.println("=== Student Information ===");
            System.out.println("Name: " + super.getName());  // Using parent's getter
            System.out.println("Age: " + super.getAge());
            System.out.println("Student ID: " + studentId);
            System.out.println("Major: " + major);
            System.out.println("GPA: " + gpa);
        }
        
        // Getters
        public String getStudentId() { return studentId; }
        public String getMajor() { return major; }
        public double getGpa() { return gpa; }
    }
    
    // === CHILD CLASS 2: Employee (demonstrating different super usage) ===
    static class Employee extends Person {
        private String employeeId;
        private String department;
        private double salary;
        private String position;
        
        // Constructor with super() call
        public Employee(String name, int age, String gender, String nationality,
                       String employeeId, String department, double salary, String position) {
            super(name, age, gender, nationality);  // Must be first statement
            this.employeeId = employeeId;
            this.department = department;
            this.salary = salary;
            this.position = position;
            System.out.println("Employee constructor called");
        }
        
        // Constructor chaining with this() and super()
        public Employee(String name, String employeeId, String department) {
            this(name, 25, "Unknown", "Unknown", employeeId, department, 50000.0, "Junior");
            System.out.println("Employee(String, String, String) constructor called");
        }
        
        // Override introduce() without calling super (completely new behavior)
        @Override
        public void introduce() {
            System.out.println("Professional introduction:");
            System.out.println("Hello, I'm " + name + ", " + position + " in " + department);
            System.out.println("Employee ID: " + employeeId);
        }
        
        // Override work() calling super and adding new behavior
        @Override
        public void work() {
            System.out.println(name + " is working as a " + position);
            super.work();  // Still calls Person's work() method
            System.out.println("Department: " + department + ", Salary: $" + salary);
        }
        
        // Employee-specific method that uses super for parent method
        public void requestVacation(int days) {
            System.out.println(name + " is requesting " + days + " days vacation");
            super.celebrateBirthday();  // Using parent's method
        }
        
        // Method showing when super is NOT needed
        public void displayInfo() {
            // Can directly access protected fields from parent
            System.out.println("Employee: " + name);
            System.out.println("Age: " + age);  // age is protected in Person
            
            // Using super is only needed when there's name conflict
            // System.out.println("Name from parent: " + super.name); // Same as name
        }
    }
    
    // === GRANDCHILD CLASS: Demonstrating multilevel super ===
    static class WorkingStudent extends Student {
        private String jobTitle;
        private double hourlyRate;
        
        public WorkingStudent(String name, int age, String gender, String nationality,
                             String studentId, String major, double gpa,
                             String jobTitle, double hourlyRate) {
            // Call Student constructor
            super(name, age, gender, nationality, studentId, major, gpa);
            this.jobTitle = jobTitle;
            this.hourlyRate = hourlyRate;
            System.out.println("WorkingStudent constructor called");
        }
        
        @Override
        public void introduce() {
            // Call Student's introduce() which calls Person's introduce()
            super.introduce();
            
            // Add WorkingStudent-specific info
            System.out.println("I also work as a " + jobTitle + " ($" + hourlyRate + "/hr)");
        }
        
        @Override
        public void work() {
            // Can only call immediate parent's method with super
            super.work();  // Calls Student's work()
            
            // To call Person's work() directly, need to go through Student
            System.out.println("And working part-time as " + jobTitle);
        }
        
        public void calculateWeeklyPay(int hours) {
            double pay = hours * hourlyRate;
            System.out.println(name + " will earn $" + pay + " this week");
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== super Keyword Demonstration ===\n");
        
        System.out.println("=== Creating Student Object ===");
        Student student = new Student("John Doe", 20, "Male", "USA", 
                                     "S12345", "Computer Science", 3.8);
        
        System.out.println("\n=== Student Methods ===");
        student.introduce();      // Calls overridden method (which calls super.introduce())
        student.work();           // Calls overridden method (which calls super.work())
        student.takeExam("Java Programming");
        student.displayFullInfo();
        student.celebrateBirthday();  // Inherited from Person
        
        System.out.println("\n=== Creating Employee Object ===");
        Employee employee = new Employee("Alice Smith", 30, "Female", "Canada",
                                        "E67890", "Engineering", 85000.0, "Senior Developer");
        
        System.out.println("\n=== Employee Methods ===");
        employee.introduce();     // Completely overridden (no super call)
        employee.work();          // Extends parent behavior
        employee.requestVacation(10);
        
        System.out.println("\n=== Creating WorkingStudent (Multilevel) ===");
        WorkingStudent workingStudent = new WorkingStudent("Bob Johnson", 22, "Male", "UK",
                                                          "S54321", "Business", 3.5,
                                                          "Teaching Assistant", 25.0);
        
        System.out.println("\n=== WorkingStudent Methods ===");
        workingStudent.introduce();  // Calls Student.introduce() → Person.introduce()
        workingStudent.work();       // Calls Student.work() → Person.work()
        workingStudent.calculateWeeklyPay(20);
        
        System.out.println("\n=== Important Rules for super ===");
        System.out.println("1. super() must be the first statement in constructor");
        System.out.println("2. super can be used to call parent constructors");
        System.out.println("3. super can be used to call parent methods");
        System.out.println("4. super cannot be used in static methods");
        System.out.println("5. Each constructor can have only one super() call");
        System.out.println("6. If no super() is written, compiler adds super()");
        
        System.out.println("\n=== When to Use super ===");
        System.out.println("1. When overriding methods but want parent behavior too");
        System.out.println("2. When parent has multiple constructors");
        System.out.println("3. When child has field with same name as parent");
        System.out.println("4. When implementing constructor chaining");
        System.out.println("5. When extending abstract class implementations");
        
        System.out.println("\n=== Common Mistakes ===");
        System.out.println("1. Forgetting super() call when parent has no default constructor");
        System.out.println("2. Calling super() after other statements (compile error)");
        System.out.println("3. Using super in static context (not allowed)");
        System.out.println("4. Overusing super when not needed");
    }
}

4. Constructors in Inheritance

Constructors play a crucial role in inheritance. When an object of a subclass is created, constructors are called in a specific order from top to bottom of the inheritance hierarchy.

Constructor Chaining Order
  • Top-down execution: Parent → Child → Grandchild
  • Parent constructor executes first
  • Implicit super() call if not specified
  • Default constructor added by compiler if needed
  • Each class responsible for its own initialization
  • Final step: Object class constructor
Important Rules
  • super() must be first statement in constructor
  • If no constructor, compiler adds default
  • If parent has no default constructor, must call super() explicitly
  • Cannot use both this() and super() in same constructor
  • Private constructors prevent inheritance
  • Final classes cannot have subclasses
ConstructorInheritance.java
public class ConstructorInheritance {
    
    // === BASE CLASS WITH DIFFERENT CONSTRUCTORS ===
    static class A {
        private String name;
        private int value;
        
        // Constructor 1: Default
        public A() {
            this("Default", 0);  // Call another constructor in same class
            System.out.println("A() default constructor");
        }
        
        // Constructor 2: With parameters
        public A(String name, int value) {
            // Implicit call to Object() constructor happens here
            this.name = name;
            this.value = value;
            System.out.println("A(String, int) constructor: " + name + ", " + value);
        }
        
        // Constructor 3: Copy constructor
        public A(A other) {
            this(other.name, other.value);
            System.out.println("A(A) copy constructor");
        }
        
        // No default constructor if we comment out A() - try it!
        // Only parameterized constructor exists
    }
    
    // === SUBCLASS B ===
    static class B extends A {
        private double data;
        
        // Constructor 1: Default - will cause error if A has no default constructor
        public B() {
            // super() is implicitly called here
            // If A has no default constructor, this will cause compile error
            this.data = 0.0;
            System.out.println("B() default constructor");
        }
        
        // Constructor 2: With parameters - explicitly calls parent constructor
        public B(String name, int value, double data) {
            // Must call parent constructor explicitly
            super(name, value);  // Calls A(String, int)
            this.data = data;
            System.out.println("B(String, int, double) constructor: data=" + data);
        }
        
        // Constructor 3: Using this() for constructor chaining
        public B(double data) {
            this("B-default", 100, data);  // Calls B(String, int, double)
            System.out.println("B(double) constructor");
        }
    }
    
    // === SUBCLASS C (extends B) ===
    static class C extends B {
        private boolean flag;
        
        // Constructor 1
        public C(String name, int value, double data, boolean flag) {
            // Must call B constructor (which calls A constructor)
            super(name, value, data);
            this.flag = flag;
            System.out.println("C(...) constructor: flag=" + flag);
        }
        
        // Constructor 2: Demonstrating full constructor chain
        public C() {
            // Implicit super() calls B() which calls A()
            // This only works if all parents have default constructors
            this.flag = false;
            System.out.println("C() default constructor");
        }
    }
    
    // === CLASS WITH NO DEFAULT CONSTRUCTOR ===
    static class ParentNoDefault {
        private int x;
        
        // Only parameterized constructor - NO default constructor
        public ParentNoDefault(int x) {
            this.x = x;
            System.out.println("ParentNoDefault(int) constructor: x=" + x);
        }
        
        // If we don't provide default constructor, compiler won't add one
        // because we have a parameterized constructor
    }
    
    static class ChildMustCallSuper extends ParentNoDefault {
        private int y;
        
        public ChildMustCallSuper(int x, int y) {
            // MUST call super() explicitly because parent has no default constructor
            super(x);  // This is required!
            this.y = y;
            System.out.println("ChildMustCallSuper(int, int) constructor: y=" + y);
        }
        
        /*
        // This would cause COMPILE ERROR because no super() call
        public ChildMustCallSuper(int y) {
            this.y = y;  // ERROR: constructor ParentNoDefault() is undefined
        }
        */
    }
    
    // === FINAL CLASS EXAMPLE ===
    static final class FinalClass {
        // Final class cannot be extended
        public FinalClass() {
            System.out.println("FinalClass constructor");
        }
    }
    
    /*
    // This would cause COMPILE ERROR
    static class TryToExtendFinal extends FinalClass {
        // Cannot extend final class
    }
    */
    
    // === CLASS WITH PRIVATE CONSTRUCTOR ===
    static class PrivateConstructor {
        private static int instanceCount = 0;
        
        // Private constructor - cannot be instantiated or extended
        private PrivateConstructor() {
            instanceCount++;
            System.out.println("PrivateConstructor created, total: " + instanceCount);
        }
        
        // Factory method pattern
        public static PrivateConstructor createInstance() {
            return new PrivateConstructor();
        }
    }
    
    /*
    // This would cause COMPILE ERROR
    static class TryToExtendPrivate extends PrivateConstructor {
        // Cannot extend class with private constructor
    }
    */
    
    // === ABSTRACT CLASS CONSTRUCTORS ===
    abstract class AbstractBase {
        protected String type;
        
        // Abstract classes CAN have constructors
        public AbstractBase(String type) {
            this.type = type;
            System.out.println("AbstractBase constructor: " + type);
        }
        
        // Abstract method
        public abstract void performAction();
        
        // Concrete method
        public void displayType() {
            System.out.println("Type: " + type);
        }
    }
    
    static class ConcreteChild extends AbstractBase {
        private int value;
        
        public ConcreteChild(String type, int value) {
            // Must call parent constructor
            super(type);
            this.value = value;
            System.out.println("ConcreteChild constructor: value=" + value);
        }
        
        @Override
        public void performAction() {
            System.out.println("ConcreteChild performing action with value: " + value);
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Constructor Inheritance Demonstration ===\n");
        
        System.out.println("=== Simple Inheritance Chain ===");
        System.out.println("\nCreating C object with parameters:");
        C c1 = new C("Test", 42, 3.14, true);
        
        System.out.println("\nCreating C object with default constructor:");
        C c2 = new C();
        
        System.out.println("\n=== Constructor with No Default Parent ===");
        ChildMustCallSuper child = new ChildMustCallSuper(10, 20);
        
        System.out.println("\n=== Abstract Class Constructor ===");
        ConcreteChild concrete = new ConcreteChild("CustomType", 100);
        concrete.performAction();
        concrete.displayType();
        
        System.out.println("\n=== Factory Pattern (Private Constructor) ===");
        PrivateConstructor p1 = PrivateConstructor.createInstance();
        PrivateConstructor p2 = PrivateConstructor.createInstance();
        
        System.out.println("\n=== Constructor Execution Order ===");
        System.out.println("When creating new C(\"Test\", 42, 3.14, true):");
        System.out.println("1. Object() constructor (implicit)");
        System.out.println("2. A(String, int) constructor");
        System.out.println("3. B(String, int, double) constructor");
        System.out.println("4. C(...) constructor");
        
        System.out.println("\n=== Key Rules Demonstrated ===");
        System.out.println("1. Constructors execute from top to bottom (parent to child)");
        System.out.println("2. super() must be first statement in constructor");
        System.out.println("3. If no constructor is defined, compiler adds default");
        System.out.println("4. If parent has no default constructor, child MUST call super()");
        System.out.println("5. Cannot use both this() and super() in same constructor");
        System.out.println("6. Abstract classes can have constructors");
        System.out.println("7. Final classes cannot be extended");
        System.out.println("8. Private constructors prevent inheritance");
        
        System.out.println("\n=== Common Issues and Solutions ===");
        System.out.println("Problem: 'constructor X in class Y cannot be applied to given types'");
        System.out.println("Solution: Call correct parent constructor with super(...)");
        
        System.out.println("\nProblem: 'constructor call must be first statement'");
        System.out.println("Solution: Move super() or this() to beginning of constructor");
        
        System.out.println("\nProblem: 'class Z has private constructor'");
        System.out.println("Solution: Use factory method or change constructor visibility");
        
        System.out.println("\n=== Best Practices ===");
        System.out.println("1. Provide default constructors in base classes");
        System.out.println("2. Use super() explicitly for clarity");
        System.out.println("3. Document constructor requirements");
        System.out.println("4. Consider making classes final if not designed for extension");
        System.out.println("5. Use factory methods for complex object creation");
    }
}

5. Method Overriding in Inheritance

Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass. This is a key feature that enables polymorphism in Java.

MethodOverridingInheritance.java
import java.util.ArrayList;
import java.util.List;

public class MethodOverridingInheritance {
    
    // === BASE BANK ACCOUNT CLASS ===
    static class BankAccount {
        protected String accountNumber;
        protected String accountHolder;
        protected double balance;
        
        public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
            this.accountNumber = accountNumber;
            this.accountHolder = accountHolder;
            this.balance = initialBalance;
        }
        
        // Methods to be potentially overridden
        public void deposit(double amount) {
            if (amount > 0) {
                balance += amount;
                System.out.println(accountHolder + ": Deposited $" + amount);
            } else {
                System.out.println("Invalid deposit amount");
            }
        }
        
        public boolean withdraw(double amount) {
            if (amount > 0 && amount <= balance) {
                balance -= amount;
                System.out.println(accountHolder + ": Withdrew $" + amount);
                return true;
            } else {
                System.out.println(accountHolder + ": Withdrawal failed - insufficient funds");
                return false;
            }
        }
        
        public double calculateInterest() {
            // Base class provides default implementation
            return 0.0;  // No interest by default
        }
        
        public void displayAccountInfo() {
            System.out.println("=== Account Information ===");
            System.out.println("Account Holder: " + accountHolder);
            System.out.println("Account Number: " + accountNumber);
            System.out.println("Balance: $" + balance);
            System.out.println("Interest Earned: $" + calculateInterest());
        }
        
        // Final method - cannot be overridden
        public final String getAccountNumber() {
            return accountNumber;
        }
        
        // Getters
        public String getAccountHolder() { return accountHolder; }
        public double getBalance() { return balance; }
    }
    
    // === SAVINGS ACCOUNT (Overrides calculateInterest) ===
    static class SavingsAccount extends BankAccount {
        private double interestRate;
        private double minimumBalance;
        
        public SavingsAccount(String accountNumber, String accountHolder, 
                             double initialBalance, double interestRate, double minimumBalance) {
            super(accountNumber, accountHolder, initialBalance);
            this.interestRate = interestRate;
            this.minimumBalance = minimumBalance;
        }
        
        // Override withdraw() to enforce minimum balance
        @Override
        public boolean withdraw(double amount) {
            if (balance - amount >= minimumBalance) {
                return super.withdraw(amount);  // Call parent's withdraw
            } else {
                System.out.println(accountHolder + ": Withdrawal denied - below minimum balance");
                return false;
            }
        }
        
        // Override calculateInterest() with savings-specific calculation
        @Override
        public double calculateInterest() {
            return balance * (interestRate / 100);
        }
        
        // Override displayAccountInfo() to show savings-specific info
        @Override
        public void displayAccountInfo() {
            super.displayAccountInfo();  // Call parent's display
            System.out.println("Account Type: Savings");
            System.out.println("Interest Rate: " + interestRate + "%");
            System.out.println("Minimum Balance: $" + minimumBalance);
        }
        
        // Cannot override final method getAccountNumber()
        // @Override public String getAccountNumber() { return "SAV-" + accountNumber; } // ERROR
    }
    
    // === CHECKING ACCOUNT (Overrides differently) ===
    static class CheckingAccount extends BankAccount {
        private int freeTransactions;
        private int transactionCount;
        private double transactionFee;
        
        public CheckingAccount(String accountNumber, String accountHolder,
                              double initialBalance, int freeTransactions, double transactionFee) {
            super(accountNumber, accountHolder, initialBalance);
            this.freeTransactions = freeTransactions;
            this.transactionFee = transactionFee;
            this.transactionCount = 0;
        }
        
        // Override deposit() to count transactions
        @Override
        public void deposit(double amount) {
            super.deposit(amount);
            transactionCount++;
            applyTransactionFee();
        }
        
        // Override withdraw() to count transactions
        @Override
        public boolean withdraw(double amount) {
            boolean success = super.withdraw(amount);
            if (success) {
                transactionCount++;
                applyTransactionFee();
            }
            return success;
        }
        
        private void applyTransactionFee() {
            if (transactionCount > freeTransactions) {
                balance -= transactionFee;
                System.out.println(accountHolder + ": Transaction fee charged: $" + transactionFee);
            }
        }
        
        // Override displayAccountInfo()
        @Override
        public void displayAccountInfo() {
            super.displayAccountInfo();
            System.out.println("Account Type: Checking");
            System.out.println("Free Transactions: " + freeTransactions);
            System.out.println("Transactions Used: " + transactionCount);
            System.out.println("Transaction Fee: $" + transactionFee);
        }
        
        // New method specific to CheckingAccount
        public void resetTransactionCount() {
            transactionCount = 0;
            System.out.println(accountHolder + ": Transaction count reset");
        }
    }
    
    // === FIXED DEPOSIT ACCOUNT ===
    static class FixedDepositAccount extends BankAccount {
        private int termMonths;
        private double fixedInterestRate;
        private boolean prematureWithdrawalAllowed;
        
        public FixedDepositAccount(String accountNumber, String accountHolder,
                                  double initialBalance, int termMonths, 
                                  double fixedInterestRate, boolean prematureWithdrawalAllowed) {
            super(accountNumber, accountHolder, initialBalance);
            this.termMonths = termMonths;
            this.fixedInterestRate = fixedInterestRate;
            this.prematureWithdrawalAllowed = prematureWithdrawalAllowed;
        }
        
        // Override withdraw() with term restrictions
        @Override
        public boolean withdraw(double amount) {
            if (!prematureWithdrawalAllowed) {
                System.out.println(accountHolder + ": Premature withdrawal not allowed");
                return false;
            }
            
            // Apply penalty for premature withdrawal
            double penalty = amount * 0.05;  // 5% penalty
            if (super.withdraw(amount + penalty)) {
                System.out.println(accountHolder + ": Premature withdrawal penalty: $" + penalty);
                return true;
            }
            return false;
        }
        
        // Override calculateInterest() with fixed deposit calculation
        @Override
        public double calculateInterest() {
            return balance * (fixedInterestRate / 100) * (termMonths / 12.0);
        }
        
        // Cannot override - method is private in parent (not inherited)
        // @Override private void somePrivateMethod() { } // Not possible
        
        // Static method - not overridden, but hidden
        public static String getBankName() {
            return "Fixed Deposit Bank";
        }
    }
    
    // === CREDIT ACCOUNT (Demonstrating covariant return types) ===
    static class CreditAccount extends BankAccount {
        private double creditLimit;
        private double creditUsed;
        
        public CreditAccount(String accountNumber, String accountHolder,
                           double creditLimit) {
            super(accountNumber, accountHolder, 0);  // Balance starts at 0
            this.creditLimit = creditLimit;
            this.creditUsed = 0;
        }
        
        // Override getBalance() with covariant return type (same return type)
        @Override
        public double getBalance() {
            // For credit account, balance is negative (amount owed)
            return creditUsed;
        }
        
        // Get available credit
        public double getAvailableCredit() {
            return creditLimit - creditUsed;
        }
        
        // Override withdraw() for credit account (increases debt)
        @Override
        public boolean withdraw(double amount) {
            if (creditUsed + amount <= creditLimit) {
                creditUsed += amount;
                System.out.println(accountHolder + ": Credit used: $" + amount);
                System.out.println("Available credit: $" + getAvailableCredit());
                return true;
            } else {
                System.out.println(accountHolder + ": Credit limit exceeded");
                return false;
            }
        }
        
        // Override deposit() to reduce debt
        @Override
        public void deposit(double amount) {
            if (amount > 0) {
                creditUsed = Math.max(0, creditUsed - amount);
                System.out.println(accountHolder + ": Credit paid: $" + amount);
                System.out.println("Remaining debt: $" + creditUsed);
            }
        }
        
        @Override
        public void displayAccountInfo() {
            System.out.println("=== Credit Account ===");
            System.out.println("Account Holder: " + accountHolder);
            System.out.println("Account Number: " + accountNumber);
            System.out.println("Credit Limit: $" + creditLimit);
            System.out.println("Credit Used: $" + creditUsed);
            System.out.println("Available Credit: $" + getAvailableCredit());
        }
    }
    
    // === BANK SYSTEM DEMONSTRATING POLYMORPHISM ===
    static class Bank {
        private List accounts;
        
        public Bank() {
            accounts = new ArrayList<>();
        }
        
        public void addAccount(BankAccount account) {
            accounts.add(account);
        }
        
        public void processMonthlyInterest() {
            System.out.println("\n=== Processing Monthly Interest ===");
            for (BankAccount account : accounts) {
                double interest = account.calculateInterest();
                if (interest > 0) {
                    account.deposit(interest);
                    System.out.println(account.getAccountHolder() + 
                                     ": Interest added: $" + interest);
                }
            }
        }
        
        public void displayAllAccounts() {
            System.out.println("\n=== All Bank Accounts ===");
            for (BankAccount account : accounts) {
                account.displayAccountInfo();
                System.out.println();
            }
        }
        
        public double getTotalBankAssets() {
            double total = 0;
            for (BankAccount account : accounts) {
                total += account.getBalance();
            }
            return total;
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Method Overriding in Inheritance ===\n");
        
        // Create bank
        Bank bank = new Bank();
        
        // Create different types of accounts
        SavingsAccount savings = new SavingsAccount("SAV001", "Alice Johnson", 
                                                   5000.0, 3.5, 1000.0);
        CheckingAccount checking = new CheckingAccount("CHK001", "Bob Smith", 
                                                      2500.0, 5, 2.0);
        FixedDepositAccount fixedDeposit = new FixedDepositAccount("FD001", "Charlie Brown", 
                                                                  10000.0, 12, 5.0, false);
        CreditAccount credit = new CreditAccount("CRD001", "Diana Miller", 5000.0);
        
        // Add accounts to bank
        bank.addAccount(savings);
        bank.addAccount(checking);
        bank.addAccount(fixedDeposit);
        bank.addAccount(credit);
        
        // Demonstrate polymorphic behavior
        System.out.println("=== Transaction Demonstrations ===");
        
        System.out.println("\n--- Savings Account Transactions ---");
        savings.deposit(1000.0);
        savings.withdraw(4500.0);  // Should succeed
        savings.withdraw(1000.0);   // Should fail (below minimum)
        
        System.out.println("\n--- Checking Account Transactions ---");
        checking.deposit(500.0);
        checking.withdraw(200.0);
        checking.withdraw(100.0);   // 3rd transaction (still free)
        checking.withdraw(50.0);    // 4th transaction (still free)
        checking.withdraw(50.0);    // 5th transaction (free limit reached)
        checking.withdraw(50.0);    // 6th transaction (should charge fee)
        checking.resetTransactionCount();
        
        System.out.println("\n--- Fixed Deposit Account ---");
        fixedDeposit.withdraw(1000.0);  // Should fail (not allowed)
        
        System.out.println("\n--- Credit Account ---");
        credit.withdraw(2000.0);   // Use credit
        credit.withdraw(3500.0);   // Use more credit
        credit.withdraw(1000.0);   // Should fail (exceeds limit)
        credit.deposit(1000.0);    // Pay down credit
        
        // Process interest for all accounts (polymorphic call)
        bank.processMonthlyInterest();
        
        // Display all accounts (polymorphic call)
        bank.displayAllAccounts();
        
        System.out.println("=== Total Bank Assets: $" + bank.getTotalBankAssets() + " ===");
        
        System.out.println("\n=== Method Overriding Rules Demonstrated ===");
        System.out.println("1. Same method signature (name + parameters)");
        System.out.println("2. Return type should be same or covariant (subtype)");
        System.out.println("3. Access modifier cannot be more restrictive");
        System.out.println("4. Cannot override final, static, or private methods");
        System.out.println("5. Can throw same, subclass, or no exception (not broader)");
        
        System.out.println("\n=== @Override Annotation Benefits ===");
        System.out.println("1. Compile-time checking of override correctness");
        System.out.println("2. Clearly indicates intent to override");
        System.out.println("3. Prevents accidental method hiding");
        System.out.println("4. Improves code readability");
        
        System.out.println("\n=== When to Override Methods ===");
        System.out.println("1. When subclass needs different behavior");
        System.out.println("2. When extending framework classes");
        System.out.println("3. When implementing template method pattern");
        System.out.println("4. When providing specific implementations in inheritance hierarchy");
        
        System.out.println("\n=== When NOT to Override ===");
        System.out.println("1. When method is marked final");
        System.out.println("2. When method is static (it's hidden, not overridden)");
        System.out.println("3. When you want to completely replace without calling super");
        System.out.println("4. When parent implementation is sufficient");
    }
}

6. Real-World Example: E-commerce System

Complete E-commerce Inheritance Hierarchy
// Complete e-commerce system demonstrating inheritance
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class ECommerceInheritance {
    
    // === BASE CLASS: Product ===
    static abstract class Product {
        protected String id;
        protected String name;
        protected String description;
        protected double price;
        protected int stockQuantity;
        protected String category;
        protected double weight;
        
        public Product(String id, String name, String description, 
                      double price, int stockQuantity, String category, double weight) {
            this.id = id;
            this.name = name;
            this.description = description;
            this.price = price;
            this.stockQuantity = stockQuantity;
            this.category = category;
            this.weight = weight;
        }
        
        // Abstract methods - must be implemented by subclasses
        public abstract double calculateShippingCost();
        public abstract int getWarrantyMonths();
        public abstract String getProductType();
        
        // Concrete methods - common to all products
        public boolean isAvailable() {
            return stockQuantity > 0;
        }
        
        public boolean purchase(int quantity) {
            if (quantity <= stockQuantity) {
                stockQuantity -= quantity;
                return true;
            }
            return false;
        }
        
        public void restock(int quantity) {
            stockQuantity += quantity;
        }
        
        public double calculateTotalCost(int quantity) {
            return (price * quantity) + calculateShippingCost();
        }
        
        public void displayProductInfo() {
            System.out.println("=== Product Information ===");
            System.out.println("ID: " + id);
            System.out.println("Name: " + name);
            System.out.println("Type: " + getProductType());
            System.out.println("Category: " + category);
            System.out.println("Price: $" + price);
            System.out.println("Stock: " + stockQuantity);
            System.out.println("Available: " + (isAvailable() ? "Yes" : "No"));
            System.out.println("Warranty: " + getWarrantyMonths() + " months");
            System.out.println("Shipping Cost: $" + calculateShippingCost());
            System.out.println("Weight: " + weight + " kg");
        }
        
        // Getters
        public String getId() { return id; }
        public String getName() { return name; }
        public double getPrice() { return price; }
        public int getStockQuantity() { return stockQuantity; }
        public String getCategory() { return category; }
    }
    
    // === ELECTRONICS PRODUCTS ===
    static abstract class ElectronicsProduct extends Product {
        protected String brand;
        protected String model;
        protected int powerConsumption; // in watts
        
        public ElectronicsProduct(String id, String name, String description,
                                 double price, int stockQuantity, String category, double weight,
                                 String brand, String model, int powerConsumption) {
            super(id, name, description, price, stockQuantity, category, weight);
            this.brand = brand;
            this.model = model;
            this.powerConsumption = powerConsumption;
        }
        
        @Override
        public double calculateShippingCost() {
            // Electronics have higher shipping cost due to fragility
            return weight * 3.0 + 10.0; // $10 handling fee
        }
        
        @Override
        public void displayProductInfo() {
            super.displayProductInfo();
            System.out.println("Brand: " + brand);
            System.out.println("Model: " + model);
            System.out.println("Power Consumption: " + powerConsumption + "W");
        }
    }
    
    // Smartphone - extends ElectronicsProduct
    static class Smartphone extends ElectronicsProduct {
        private String os;
        private int storageGB;
        private int ramGB;
        private double screenSize;
        
        public Smartphone(String id, String name, String description,
                         double price, int stockQuantity, double weight,
                         String brand, String model, int powerConsumption,
                         String os, int storageGB, int ramGB, double screenSize) {
            super(id, name, description, price, stockQuantity, "Smartphone", weight,
                  brand, model, powerConsumption);
            this.os = os;
            this.storageGB = storageGB;
            this.ramGB = ramGB;
            this.screenSize = screenSize;
        }
        
        @Override
        public int getWarrantyMonths() {
            return 24; // 2-year warranty for smartphones
        }
        
        @Override
        public String getProductType() {
            return "Smartphone";
        }
        
        @Override
        public void displayProductInfo() {
            super.displayProductInfo();
            System.out.println("Operating System: " + os);
            System.out.println("Storage: " + storageGB + "GB");
            System.out.println("RAM: " + ramGB + "GB");
            System.out.println("Screen Size: " + screenSize + " inches");
        }
        
        // Smartphone-specific method
        public void makeCall(String number) {
            System.out.println(name + " calling " + number + "...");
        }
    }
    
    // Laptop - extends ElectronicsProduct
    static class Laptop extends ElectronicsProduct {
        private String processor;
        private int storageGB;
        private int ramGB;
        private boolean hasDedicatedGPU;
        
        public Laptop(String id, String name, String description,
                     double price, int stockQuantity, double weight,
                     String brand, String model, int powerConsumption,
                     String processor, int storageGB, int ramGB, boolean hasDedicatedGPU) {
            super(id, name, description, price, stockQuantity, "Laptop", weight,
                  brand, model, powerConsumption);
            this.processor = processor;
            this.storageGB = storageGB;
            this.ramGB = ramGB;
            this.hasDedicatedGPU = hasDedicatedGPU;
        }
        
        @Override
        public int getWarrantyMonths() {
            return 36; // 3-year warranty for laptops
        }
        
        @Override
        public String getProductType() {
            return "Laptop";
        }
        
        @Override
        public double calculateShippingCost() {
            // Laptops require special packaging
            return super.calculateShippingCost() + 15.0;
        }
        
        @Override
        public void displayProductInfo() {
            super.displayProductInfo();
            System.out.println("Processor: " + processor);
            System.out.println("Storage: " + storageGB + "GB");
            System.out.println("RAM: " + ramGB + "GB");
            System.out.println("Dedicated GPU: " + (hasDedicatedGPU ? "Yes" : "No"));
        }
        
        // Laptop-specific method
        public void runBenchmark() {
            System.out.println(name + " running performance benchmark...");
        }
    }
    
    // === CLOTHING PRODUCTS ===
    static abstract class ClothingProduct extends Product {
        protected String size;
        protected String color;
        protected String material;
        
        public ClothingProduct(String id, String name, String description,
                              double price, int stockQuantity, String category, double weight,
                              String size, String color, String material) {
            super(id, name, description, price, stockQuantity, category, weight);
            this.size = size;
            this.color = color;
            this.material = material;
        }
        
        @Override
        public double calculateShippingCost() {
            // Clothing has lower shipping cost
            return weight * 1.5;
        }
        
        @Override
        public void displayProductInfo() {
            super.displayProductInfo();
            System.out.println("Size: " + size);
            System.out.println("Color: " + color);
            System.out.println("Material: " + material);
        }
    }
    
    // TShirt - extends ClothingProduct
    static class TShirt extends ClothingProduct {
        private String sleeveType;
        private String neckType;
        
        public TShirt(String id, String name, String description,
                     double price, int stockQuantity, double weight,
                     String size, String color, String material,
                     String sleeveType, String neckType) {
            super(id, name, description, price, stockQuantity, "T-Shirt", weight,
                  size, color, material);
            this.sleeveType = sleeveType;
            this.neckType = neckType;
        }
        
        @Override
        public int getWarrantyMonths() {
            return 0; // No warranty for clothing
        }
        
        @Override
        public String getProductType() {
            return "T-Shirt";
        }
        
        @Override
        public void displayProductInfo() {
            super.displayProductInfo();
            System.out.println("Sleeve Type: " + sleeveType);
            System.out.println("Neck Type: " + neckType);
        }
        
        // TShirt-specific method
        public void washInstructions() {
            System.out.println("Wash in cold water, tumble dry low");
        }
    }
    
    // Jeans - extends ClothingProduct
    static class Jeans extends ClothingProduct {
        private String fitType;
        private String washType;
        
        public Jeans(String id, String name, String description,
                    double price, int stockQuantity, double weight,
                    String size, String color, String material,
                    String fitType, String washType) {
            super(id, name, description, price, stockQuantity, "Jeans", weight,
                  size, color, material);
            this.fitType = fitType;
            this.washType = washType;
        }
        
        @Override
        public int getWarrantyMonths() {
            return 0; // No warranty for clothing
        }
        
        @Override
        public String getProductType() {
            return "Jeans";
        }
        
        @Override
        public double calculateShippingCost() {
            // Jeans are heavier than other clothing
            return weight * 2.0;
        }
        
        @Override
        public void displayProductInfo() {
            super.displayProductInfo();
            System.out.println("Fit Type: " + fitType);
            System.out.println("Wash Type: " + washType);
        }
    }
    
    // === BOOK PRODUCTS ===
    static class Book extends Product {
        private String author;
        private String isbn;
        private int numberOfPages;
        private String publisher;
        
        public Book(String id, String name, String description,
                   double price, int stockQuantity, double weight,
                   String author, String isbn, int numberOfPages, String publisher) {
            super(id, name, description, price, stockQuantity, "Book", weight);
            this.author = author;
            this.isbn = isbn;
            this.numberOfPages = numberOfPages;
            this.publisher = publisher;
        }
        
        @Override
        public double calculateShippingCost() {
            // Books have very low shipping cost
            return weight * 1.0;
        }
        
        @Override
        public int getWarrantyMonths() {
            return 0; // No warranty for books
        }
        
        @Override
        public String getProductType() {
            return "Book";
        }
        
        @Override
        public void displayProductInfo() {
            super.displayProductInfo();
            System.out.println("Author: " + author);
            System.out.println("ISBN: " + isbn);
            System.out.println("Pages: " + numberOfPages);
            System.out.println("Publisher: " + publisher);
        }
        
        // Book-specific method
        public void readSample() {
            System.out.println("Reading sample from '" + name + "' by " + author);
        }
    }
    
    // === E-COMMERCE STORE ===
    static class ECommerceStore {
        private List products;
        private String storeName;
        
        public ECommerceStore(String storeName) {
            this.storeName = storeName;
            products = new ArrayList<>();
        }
        
        public void addProduct(Product product) {
            products.add(product);
        }
        
        public Product findProductById(String id) {
            for (Product product : products) {
                if (product.getId().equals(id)) {
                    return product;
                }
            }
            return null;
        }
        
        public void displayAllProducts() {
            System.out.println("\n=== " + storeName + " - All Products ===");
            for (Product product : products) {
                product.displayProductInfo();
                System.out.println();
            }
        }
        
        public void displayProductsByCategory(String category) {
            System.out.println("\n=== Products in Category: " + category + " ===");
            for (Product product : products) {
                if (product.getCategory().equals(category)) {
                    product.displayProductInfo();
                    System.out.println();
                }
            }
        }
        
        public double calculateOrderTotal(List productIds, List quantities) {
            double total = 0;
            for (int i = 0; i < productIds.size(); i++) {
                Product product = findProductById(productIds.get(i));
                if (product != null && product.isAvailable()) {
                    total += product.calculateTotalCost(quantities.get(i));
                }
            }
            return total;
        }
        
        public boolean processOrder(List productIds, List quantities) {
            System.out.println("\n=== Processing Order ===");
            for (int i = 0; i < productIds.size(); i++) {
                Product product = findProductById(productIds.get(i));
                if (product == null || !product.purchase(quantities.get(i))) {
                    System.out.println("Failed to purchase: " + productIds.get(i));
                    return false;
                }
                System.out.println("Purchased " + quantities.get(i) + " of " + product.getName());
            }
            return true;
        }
        
        public double getTotalInventoryValue() {
            double total = 0;
            for (Product product : products) {
                total += product.getPrice() * product.getStockQuantity();
            }
            return total;
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== E-Commerce System with Inheritance ===\n");
        
        // Create store
        ECommerceStore store = new ECommerceStore("TechFashion Books");
        
        // Create products using inheritance hierarchy
        Smartphone phone = new Smartphone("PHN001", "Galaxy S24", "Flagship smartphone",
                                         999.99, 50, 0.2, "Samsung", "S24", 15,
                                         "Android", 256, 12, 6.7);
        
        Laptop laptop = new Laptop("LPT001", "MacBook Pro", "Professional laptop",
                                   2499.99, 25, 2.1, "Apple", "M3 Pro", 65,
                                   "M3 Pro", 1000, 36, true);
        
        TShirt tshirt = new TShirt("TSH001", "Cotton T-Shirt", "100% cotton t-shirt",
                                   29.99, 100, 0.3, "L", "Black", "Cotton",
                                   "Short Sleeve", "Crew Neck");
        
        Jeans jeans = new Jeans("JNS001", "Slim Fit Jeans", "Denim jeans",
                                89.99, 75, 0.8, "32x32", "Blue", "Denim",
                                "Slim Fit", "Stone Wash");
        
        Book book = new Book("BOK001", "Clean Code", "Software development book",
                             39.99, 200, 0.5, "Robert C. Martin", "978-0132350884",
                             464, "Prentice Hall");
        
        // Add products to store
        store.addProduct(phone);
        store.addProduct(laptop);
        store.addProduct(tshirt);
        store.addProduct(jeans);
        store.addProduct(book);
        
        // Display all products
        store.displayAllProducts();
        
        // Display by category
        store.displayProductsByCategory("Smartphone");
        store.displayProductsByCategory("T-Shirt");
        
        // Demonstrate polymorphic behavior
        System.out.println("=== Product-Specific Behaviors ===");
        phone.makeCall("123-456-7890");
        laptop.runBenchmark();
        tshirt.washInstructions();
        book.readSample();
        
        // Process an order
        List orderProductIds = List.of("PHN001", "TSH001", "BOK001");
        List orderQuantities = List.of(1, 2, 1);
        
        double orderTotal = store.calculateOrderTotal(orderProductIds, orderQuantities);
        System.out.println("\nOrder Total (including shipping): $" + orderTotal);
        
        boolean orderSuccess = store.processOrder(orderProductIds, orderQuantities);
        System.out.println("Order Status: " + (orderSuccess ? "Success" : "Failed"));
        
        // Display updated inventory
        System.out.println("\n=== Updated Inventory ===");
        store.displayProductsByCategory("Smartphone");
        store.displayProductsByCategory("T-Shirt");
        
        System.out.println("=== Total Inventory Value: $" + store.getTotalInventoryValue() + " ===");
        
        System.out.println("\n=== Inheritance Benefits Demonstrated ===");
        System.out.println("1. Code Reuse: Common product functionality in base class");
        System.out.println("2. Extensibility: Easy to add new product types");
        System.out.println("3. Polymorphism: Store works with Product interface");
        System.out.println("4. Maintainability: Changes in base class affect all products");
        System.out.println("5. Type Safety: Compile-time checking of product types");
        
        System.out.println("\n=== Inheritance Hierarchy Summary ===");
        System.out.println("Product (abstract)");
        System.out.println("├── ElectronicsProduct (abstract)");
        System.out.println("│   ├── Smartphone");
        System.out.println("│   └── Laptop");
        System.out.println("├── ClothingProduct (abstract)");
        System.out.println("│   ├── TShirt");
        System.out.println("│   └── Jeans");
        System.out.println("└── Book");
    }
}

7. Best Practices & Common Pitfalls

Common Inheritance Mistakes:
  1. Violating Liskov Substitution Principle: Subclass changes parent behavior unexpectedly
  2. Deep Inheritance Hierarchies: Too many levels make code hard to understand
  3. Inheritance for Code Reuse Only: Using inheritance when composition is better
  4. Ignoring super() Calls: Forgetting to call parent constructor
  5. Overusing Protected: Exposing too much implementation detail
  6. Circular Dependencies: Classes depending on each other in hierarchy
Inheritance Best Practices
  • Use inheritance only for true IS-A relationships
  • Keep inheritance hierarchies shallow (2-3 levels)
  • Favor composition over inheritance when possible
  • Make classes final unless designed for extension
  • Use abstract classes for partial implementations
  • Document intended extension points
  • Test subclasses independently
Design Principles
  • Liskov Substitution: Subclasses should be substitutable
  • Open/Closed: Open for extension, closed for modification
  • Interface Segregation: Small, focused interfaces
  • Dependency Inversion: Depend on abstractions
  • Single Responsibility: One reason to change
  • Composition over Inheritance: More flexible design

Inheritance vs Composition

Use Inheritance when: True IS-A relationship exists, you need polymorphism, framework requires extension
Use Composition when: You want to reuse code, relationship is HAS-A, need runtime flexibility
Remember: "Favor composition over inheritance" - Design Patterns, Gang of Four

InheritanceChecklist.java
public class InheritanceChecklist {
    
    public static void inheritanceChecklist() {
        System.out.println("=== Inheritance Implementation Checklist ===\n");
        
        System.out.println("✅ 1. IS-A Relationship Check");
        System.out.println("   - Does Subclass IS-A Superclass?");
        System.out.println("   - Example: Dog IS-A Animal (YES)");
        System.out.println("   - Example: Engine IS-A Car (NO - use composition)");
        
        System.out.println("\n✅ 2. Liskov Substitution Principle");
        System.out.println("   - Can subclass replace superclass without issues?");
        System.out.println("   - No strengthened preconditions");
        System.out.println("   - No weakened postconditions");
        System.out.println("   - Preserves invariants");
        
        System.out.println("\n✅ 3. Constructor Design");
        System.out.println("   - Does parent have appropriate constructors?");
        System.out.println("   - Are super() calls correct?");
        System.out.println("   - Consider making classes final if not for extension");
        
        System.out.println("\n✅ 4. Access Modifier Review");
        System.out.println("   - Use private for implementation details");
        System.out.println("   - Use protected only for intended extension points");
        System.out.println("   - Public methods define the API");
        
        System.out.println("\n✅ 5. Method Overriding");
        System.out.println("   - Use @Override annotation");
        System.out.println("   - Maintain method contracts");
        System.out.println("   - Call super.method() when extending behavior");
        
        System.out.println("\n✅ 6. Testing Considerations");
        System.out.println("   - Test subclasses independently");
        System.out.println("   - Test polymorphic behavior");
        System.out.println("   - Test edge cases in inheritance chain");
        
        System.out.println("\n✅ 7. Documentation");
        System.out.println("   - Document extension points");
        System.out.println("   - Note any restrictions or requirements");
        System.out.println("   - Provide examples of proper usage");
    }
    
    // Example: Proper inheritance design
    abstract class Notification {
        protected String recipient;
        protected String message;
        
        public Notification(String recipient, String message) {
            this.recipient = recipient;
            this.message = message;
        }
        
        // Template method pattern
        public final void send() {
            validate();
            prepare();
            deliver();
            log();
        }
        
        protected void validate() {
            if (recipient == null || recipient.isEmpty()) {
                throw new IllegalArgumentException("Recipient is required");
            }
            if (message == null || message.isEmpty()) {
                throw new IllegalArgumentException("Message is required");
            }
        }
        
        protected void prepare() {
            System.out.println("Preparing notification for: " + recipient);
        }
        
        // Abstract method - subclasses must implement
        protected abstract void deliver();
        
        protected void log() {
            System.out.println("Notification logged: " + getClass().getSimpleName());
        }
        
        // Factory method
        public static Notification createEmail(String recipient, String message) {
            return new EmailNotification(recipient, message);
        }
    }
    
    static class EmailNotification extends Notification {
        public EmailNotification(String recipient, String message) {
            super(recipient, message);
        }
        
        @Override
        protected void deliver() {
            System.out.println("Sending email to: " + recipient);
            System.out.println("Message: " + message);
        }
        
        @Override
        protected void prepare() {
            super.prepare();
            System.out.println("Setting up SMTP connection");
        }
    }
    
    static class SMSNotification extends Notification {
        public SMSNotification(String recipient, String message) {
            super(recipient, message);
        }
        
        @Override
        protected void deliver() {
            System.out.println("Sending SMS to: " + recipient);
            System.out.println("Message: " + (message.length() > 160 ? message.substring(0, 160) : message));
        }
    }
    
    static class PushNotification extends Notification {
        private String deviceId;
        
        public PushNotification(String recipient, String message, String deviceId) {
            super(recipient, message);
            this.deviceId = deviceId;
        }
        
        @Override
        protected void validate() {
            super.validate();
            if (deviceId == null || deviceId.isEmpty()) {
                throw new IllegalArgumentException("Device ID is required for push notifications");
            }
        }
        
        @Override
        protected void deliver() {
            System.out.println("Sending push to device: " + deviceId);
            System.out.println("Title: " + recipient);
            System.out.println("Body: " + message);
        }
    }
    
    public static void main(String[] args) {
        inheritanceChecklist();
        
        System.out.println("\n=== Example: Well-Designed Inheritance ===");
        
        Notification[] notifications = {
            Notification.createEmail("user@example.com", "Welcome to our service!"),
            new SMSNotification("+1234567890", "Your verification code is 123456"),
            new PushNotification("App Update", "New version available", "device-abc123")
        };
        
        System.out.println("\n=== Sending All Notifications ===");
        for (Notification notification : notifications) {
            try {
                notification.send();
                System.out.println();
            } catch (Exception e) {
                System.out.println("Failed to send: " + e.getMessage());
            }
        }
        
        System.out.println("=== Key Design Principles Demonstrated ===");
        System.out.println("1. Template Method: send() defines algorithm skeleton");
        System.out.println("2. Liskov Substitution: All notifications are substitutable");
        System.out.println("3. Open/Closed: Easy to add new notification types");
        System.out.println("4. Single Responsibility: Each class has one purpose");
        System.out.println("5. Factory Method: createEmail() for object creation");
        
        System.out.println("\n=== When to Use Inheritance ===");
        System.out.println("✅ Modeling IS-A relationships");
        System.out.println("✅ Framework extension points");
        System.out.println("✅ Template method pattern");
        System.out.println("✅ When polymorphism is needed");
        
        System.out.println("\n=== When to Avoid Inheritance ===");
        System.out.println("❌ Just for code reuse (use composition)");
        System.out.println("❌ When relationship is HAS-A, not IS-A");
        System.out.println("❌ When you need multiple inheritance (use interfaces)");
        System.out.println("❌ When classes might change independently");
        
        System.out.println("\n=== Final Recommendations ===");
        System.out.println("1. Start with interfaces for maximum flexibility");
        System.out.println("2. Use abstract classes for shared implementation");
        System.out.println("3. Keep inheritance hierarchies shallow");
        System.out.println("4. Document for extension, design for restriction");
        System.out.println("5. Test inheritance relationships thoroughly");
    }
}