Skip to main content
  1. Resources/
  2. Study Materials/
  3. Information & Communication Technology Engineering/
  4. ICT Semester 4/
  5. Java Programming (4343203)/

20 mins· ·
Milav Dabgar
Author
Milav Dabgar
Experienced lecturer in the electrical and electronic manufacturing industry. Skilled in Embedded Systems, Image Processing, Data Science, MATLAB, Python, STM32. Strong education professional with a Master’s degree in Communication Systems Engineering from L.D. College of Engineering - Ahmedabad.
Lecture 10 - Inheritance and Method Overriding

Java Programming

Lecture 10: Inheritance and Method Overriding

Course: 4343203 - Java Programming

GTU Semester 4 | Unit 2


Learning Objectives:

  • Master inheritance concepts and implementation
  • Understand method overriding and polymorphism
  • Learn super keyword usage and constructor chaining
  • Apply inheritance hierarchies in real applications
  • Handle inheritance-related design patterns

Understanding Inheritance

Inheritance Hierarchy

Inheritance is a mechanism where a new class (subclass/child) acquires the properties and methods of an existing class (superclass/parent), promoting code reusability and establishing an "is-a" relationship.

Key Inheritance Concepts:

  • Superclass/Parent: The class being inherited from
  • Subclass/Child: The class that inherits
  • extends keyword: Used to establish inheritance
  • Is-a relationship: Subclass is a type of superclass
  • Code reusability: Inherit existing functionality

Benefits of Inheritance:

  • Code reusability and reduced redundancy
  • Easier maintenance and updates
  • Polymorphism support
  • Real-world modeling
  • Extensibility for future enhancements

Basic Inheritance Syntax:


// Superclass (Parent)
public class Vehicle {
    protected String brand;
    protected int year;
    protected String color;
    
    public Vehicle(String brand, int year, String color) {
        this.brand = brand;
        this.year = year;
        this.color = color;
    }
    
    public void start() {
        System.out.println("Vehicle is starting...");
    }
    
    public void stop() {
        System.out.println("Vehicle is stopping...");
    }
}

// Subclass (Child)
public class Car extends Vehicle {
    private int doors;
    private String fuelType;
    
    public Car(String brand, int year, String color, 
               int doors, String fuelType) {
        super(brand, year, color);  // Call parent constructor
        this.doors = doors;
        this.fuelType = fuelType;
    }
    
    public void honk() {
        System.out.println("Car is honking: Beep beep!");
    }
}

Types of Inheritance

Single Inheritance:

One subclass inherits from one superclass.


// Single Inheritance Example
class Animal {
    String name;
    
    public void eat() {
        System.out.println(name + " is eating");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println(name + " is barking");
    }
}

// Usage:
Dog dog = new Dog();
dog.name = "Buddy";
dog.eat();   // Inherited method
dog.bark();  // Own method

Multilevel Inheritance:

A chain of inheritance where each class extends another.


// Multilevel Inheritance Example
class Animal {
    public void breathe() {
        System.out.println("Animal is breathing");
    }
}

class Mammal extends Animal {
    public void giveBirth() {
        System.out.println("Mammal gives birth");
    }
}

class Dog extends Mammal {
    public void bark() {
        System.out.println("Dog is barking");
    }
}

// Dog inherits from both Mammal and Animal
Dog dog = new Dog();
dog.breathe();    // From Animal
dog.giveBirth();  // From Mammal
dog.bark();       // Own method

Hierarchical Inheritance:

Multiple subclasses inherit from one superclass.


// Hierarchical Inheritance Example
class Shape {
    protected String color;
    
    public Shape(String color) {
        this.color = color;
    }
    
    public void draw() {
        System.out.println("Drawing a " + color + " shape");
    }
}

class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    public double getArea() {
        return width * height;
    }
}

Note: Java does NOT support multiple inheritance of classes (one class extending multiple classes) to avoid the Diamond Problem. However, it supports multiple inheritance of interfaces.

Method Overriding

What is Method Overriding?

Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass.

Overriding Rules:

  • Method signature must be identical
  • Return type must be same or covariant
  • Access modifier cannot be more restrictive
  • Cannot override static, final, or private methods
  • Use @Override annotation for clarity

Basic Overriding Example:


class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
    
    public void move() {
        System.out.println("Animal moves");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks: Woof!");
    }
    
    @Override
    public void move() {
        System.out.println("Dog runs on four legs");
    }
}

Runtime Polymorphism:


public class PolymorphismDemo {
    public static void main(String[] args) {
        // Reference variable of parent type
        Animal animal1 = new Animal();
        Animal animal2 = new Dog();    // Polymorphism
        Animal animal3 = new Cat();    // Polymorphism
        
        // Method calls resolved at runtime
        animal1.makeSound(); // "Animal makes a sound"
        animal2.makeSound(); // "Dog barks: Woof!"
        animal3.makeSound(); // "Cat meows: Meow!"
        
        // Array of different animal types
        Animal[] zoo = {
            new Dog(),
            new Cat(),
            new Bird(),
            new Fish()
        };
        
        System.out.println("Zoo sounds:");
        for (Animal animal : zoo) {
            animal.makeSound(); // Different sound for each
        }
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Cat meows: Meow!");
    }
}

class Bird extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bird chirps: Tweet!");
    }
}

The super Keyword

Uses of super Keyword:

  1. Access parent's variables
  2. Call parent's methods
  3. Call parent's constructors

1. Accessing Parent Variables:


class Parent {
    protected String name = "Parent";
}

class Child extends Parent {
    private String name = "Child";
    
    public void displayNames() {
        System.out.println("Child name: " + this.name);
        System.out.println("Parent name: " + super.name);
    }
}

2. Calling Parent Methods:


class Vehicle {
    public void start() {
        System.out.println("Vehicle engine starting...");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        super.start();  // Call parent method first
        System.out.println("Car engine started successfully!");
    }
}

3. Constructor Chaining with super:


class Employee {
    protected String name;
    protected int id;
    protected double salary;
    
    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
        this.salary = 30000.0;  // Default salary
        System.out.println("Employee constructor called");
    }
    
    public Employee(String name, int id, double salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
        System.out.println("Employee parameterized constructor");
    }
}

class Manager extends Employee {
    private String department;
    private double bonus;
    
    public Manager(String name, int id, String department) {
        super(name, id);  // Call parent constructor
        this.department = department;
        this.bonus = 5000.0;
        System.out.println("Manager constructor called");
    }
    
    public Manager(String name, int id, double salary, 
                  String department, double bonus) {
        super(name, id, salary);  // Call specific parent constructor
        this.department = department;
        this.bonus = bonus;
        System.out.println("Manager full constructor called");
    }
    
    public double getTotalSalary() {
        return salary + bonus;  // Access protected variable
    }
}

Important: super() must be the first statement in a constructor. If not explicitly called, Java automatically inserts super() to call the parent's default constructor.

Access Modifiers and Inheritance

Access Modifier Visibility:

ModifierSame ClassSame PackageSubclassDifferent Package
private
default❌*
protected
public

*Only if subclass is in same package

Overriding Access Rules:

  • Cannot reduce visibility when overriding
  • Can increase visibility
  • protected → public ✅
  • public → protected ❌
  • private methods cannot be overridden

Access Modifier Examples:


public class Parent {
    private int privateVar = 10;      // Not inherited
    int defaultVar = 20;              // Package visibility
    protected int protectedVar = 30;  // Inherited
    public int publicVar = 40;        // Inherited
    
    private void privateMethod() {
        System.out.println("Private method");
    }
    
    protected void protectedMethod() {
        System.out.println("Protected method");
    }
    
    public void publicMethod() {
        System.out.println("Public method");
    }
}

public class Child extends Parent {
    public void accessParentMembers() {
        // System.out.println(privateVar);     // ❌ Error!
        System.out.println(defaultVar);        // ✅ If same package
        System.out.println(protectedVar);      // ✅ Always accessible
        System.out.println(publicVar);         // ✅ Always accessible
        
        // privateMethod();     // ❌ Error!
        protectedMethod();      // ✅ Accessible
        publicMethod();         // ✅ Accessible
    }
    
    // Method overriding with access modifier changes
    @Override
    public void protectedMethod() {  // Increased visibility: protected → public
        super.protectedMethod();
        System.out.println("Child's enhanced protected method");
    }
}

Real-world Inheritance Example

Banking System Hierarchy:


// Base Account class
public abstract class Account {
    protected String accountNumber;
    protected String holderName;
    protected double balance;
    protected String accountType;
    
    public Account(String accountNumber, String holderName, 
                  String accountType) {
        this.accountNumber = accountNumber;
        this.holderName = holderName;
        this.accountType = accountType;
        this.balance = 0.0;
    }
    
    // Common methods for all accounts
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: $" + amount);
        }
    }
    
    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
            return true;
        }
        return false;
    }
    
    // Abstract method - must be implemented by subclasses
    public abstract double calculateInterest();
    
    public void displayInfo() {
        System.out.println("Account: " + accountNumber);
        System.out.println("Holder: " + holderName);
        System.out.println("Type: " + accountType);
        System.out.println("Balance: $" + balance);
    }
    
    // Getters
    public String getAccountNumber() { return accountNumber; }
    public double getBalance() { return balance; }
}

Specialized Account Types:


// Savings Account
public class SavingsAccount extends Account {
    private double interestRate;
    private int minBalance;
    
    public SavingsAccount(String accountNumber, String holderName, 
                         double interestRate) {
        super(accountNumber, holderName, "Savings");
        this.interestRate = interestRate;
        this.minBalance = 1000;
    }
    
    @Override
    public boolean withdraw(double amount) {
        if (balance - amount >= minBalance) {
            return super.withdraw(amount);
        }
        System.out.println("Insufficient balance! Minimum balance: $" + minBalance);
        return false;
    }
    
    @Override
    public double calculateInterest() {
        return balance * interestRate / 100;
    }
    
    public void applyInterest() {
        double interest = calculateInterest();
        balance += interest;
        System.out.println("Interest applied: $" + interest);
    }
}

// Current Account
public class CurrentAccount extends Account {
    private double overdraftLimit;
    
    public CurrentAccount(String accountNumber, String holderName, 
                         double overdraftLimit) {
        super(accountNumber, holderName, "Current");
        this.overdraftLimit = overdraftLimit;
    }
    
    @Override
    public boolean withdraw(double amount) {
        if (amount > 0 && (balance + overdraftLimit) >= amount) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
            if (balance < 0) {
                System.out.println("Account overdrawn by: $" + Math.abs(balance));
            }
            return true;
        }
        System.out.println("Withdrawal exceeds overdraft limit!");
        return false;
    }
    
    @Override
    public double calculateInterest() {
        return 0.0;  // No interest for current account
    }
}

Method Overriding vs Method Overloading

Method Overriding:

  • Same method signature in parent and child
  • Runtime polymorphism
  • Uses inheritance
  • @Override annotation
  • Method resolution at runtime

class Animal {
    public void makeSound() {
        System.out.println("Animal sound");
    }
}

class Dog extends Animal {
    @Override  // Same signature
    public void makeSound() {
        System.out.println("Woof!");
    }
}

// Usage:
Animal animal = new Dog();
animal.makeSound();  // Prints "Woof!" - Runtime decision

Method Overloading:

  • Same method name, different parameters
  • Compile-time polymorphism
  • Within same class
  • No special annotation
  • Method resolution at compile time

class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {  // Different parameters
        return a + b;
    }
    
    public int add(int a, int b, int c) {    // Different parameter count
        return a + b + c;
    }
}

// Usage:
Calculator calc = new Calculator();
calc.add(5, 3);        // Calls int version
calc.add(5.5, 3.2);    // Calls double version
calc.add(1, 2, 3);     // Calls three-parameter version
AspectMethod OverridingMethod Overloading
PurposeProvide specific implementationMultiple ways to call method
InheritanceRequired (parent-child)Not required (same class)
Method SignatureMust be identicalMust be different
Polymorphism TypeRuntimeCompile-time
PerformanceSlightly slower (dynamic binding)Faster (static binding)

Previous Year Exam Questions

Q1. (GTU Summer 2022) Explain inheritance in Java with its types. Write a program to demonstrate method overriding with a practical example.

Solution:

Inheritance in Java:

Inheritance is a fundamental OOP concept where a new class (subclass) acquires properties and methods from an existing class (superclass). It promotes code reusability and establishes an "is-a" relationship.

Types of Inheritance:

1. Single Inheritance:

One class extends another class directly.

2. Multilevel Inheritance:

A class extends another class which itself extends another class, forming a chain.

3. Hierarchical Inheritance:

Multiple classes inherit from a single superclass.

Method Overriding Program - Employee Management System:

// Base class
class Employee {
    protected String name;
    protected int empId;
    protected double basicSalary;
    protected String department;
    
    public Employee(String name, int empId, double basicSalary, String department) {
        this.name = name;
        this.empId = empId;
        this.basicSalary = basicSalary;
        this.department = department;
    }
    
    // Method to be overridden
    public double calculateSalary() {
        return basicSalary;
    }
    
    // Method to be overridden
    public void displayDetails() {
        System.out.println("=== Employee Details ===");
        System.out.println("Name: " + name);
        System.out.println("ID: " + empId);
        System.out.println("Department: " + department);
        System.out.println("Basic Salary: $" + basicSalary);
        System.out.println("Total Salary: $" + calculateSalary());
    }
    
    // Common method for all employees
    public void work() {
        System.out.println(name + " is working in " + department + " department");
    }
}

// Derived class 1 - Manager
class Manager extends Employee {
    private double allowance;
    private int teamSize;
    
    public Manager(String name, int empId, double basicSalary, 
                  String department, double allowance, int teamSize) {
        super(name, empId, basicSalary, department);  // Call parent constructor
        this.allowance = allowance;
        this.teamSize = teamSize;
    }
    
    // Override calculateSalary method
    @Override
    public double calculateSalary() {
        double teamBonus = teamSize * 500;  // $500 per team member
        return basicSalary + allowance + teamBonus;
    }
    
    // Override displayDetails method
    @Override
    public void displayDetails() {
        super.displayDetails();  // Call parent method
        System.out.println("Position: Manager");
        System.out.println("Team Size: " + teamSize);
        System.out.println("Allowance: $" + allowance);
        System.out.println("Team Bonus: $" + (teamSize * 500));
        System.out.println("========================");
    }
    
    // Manager-specific method
    public void conductMeeting() {
        System.out.println("Manager " + name + " is conducting a team meeting");
    }
}

// Derived class 2 - Developer
class Developer extends Employee {
    private String programmingLanguage;
    private int projectsCompleted;
    
    public Developer(String name, int empId, double basicSalary, 
                    String department, String programmingLanguage, int projectsCompleted) {
        super(name, empId, basicSalary, department);
        this.programmingLanguage = programmingLanguage;
        this.projectsCompleted = projectsCompleted;
    }
    
    // Override calculateSalary method
    @Override
    public double calculateSalary() {
        double projectBonus = projectsCompleted * 1000;  // $1000 per project
        double skillBonus = 2000;  // Fixed skill bonus
        return basicSalary + projectBonus + skillBonus;
    }
    
    // Override displayDetails method
    @Override
    public void displayDetails() {
        super.displayDetails();
        System.out.println("Position: Developer");
        System.out.println("Programming Language: " + programmingLanguage);
        System.out.println("Projects Completed: " + projectsCompleted);
        System.out.println("Project Bonus: $" + (projectsCompleted * 1000));
        System.out.println("Skill Bonus: $2000");
        System.out.println("========================");
    }
    
    // Developer-specific method
    public void writeCode() {
        System.out.println("Developer " + name + " is writing code in " + programmingLanguage);
    }
}

// Derived class 3 - Intern
class Intern extends Employee {
    private int duration;  // in months
    private String university;
    
    public Intern(String name, int empId, double basicSalary, 
                 String department, int duration, String university) {
        super(name, empId, basicSalary, department);
        this.duration = duration;
        this.university = university;
    }
    
    // Override calculateSalary method
    @Override
    public double calculateSalary() {
        // Interns get basic salary + completion bonus if duration > 6 months
        double completionBonus = (duration > 6) ? 1500 : 0;
        return basicSalary + completionBonus;
    }
    
    // Override displayDetails method
    @Override
    public void displayDetails() {
        super.displayDetails();
        System.out.println("Position: Intern");
        System.out.println("University: " + university);
        System.out.println("Duration: " + duration + " months");
        if (duration > 6) {
            System.out.println("Completion Bonus: $1500");
        }
        System.out.println("========================");
    }
    
    // Intern-specific method
    public void attendTraining() {
        System.out.println("Intern " + name + " is attending training sessions");
    }
}

// Main class to demonstrate method overriding
public class InheritanceDemo {
    public static void main(String[] args) {
        System.out.println("=== Inheritance and Method Overriding Demo ===\n");
        
        // Create different types of employees
        Manager manager = new Manager("Alice Johnson", 101, 60000, "IT", 15000, 8);
        Developer developer = new Developer("Bob Smith", 102, 50000, "Development", "Java", 12);
        Intern intern = new Intern("Charlie Brown", 103, 15000, "IT", 8, "State University");
        
        // Store in array for polymorphic behavior
        Employee[] employees = {manager, developer, intern};
        
        System.out.println("=== Employee Information ===\n");
        
        // Demonstrate polymorphism and method overriding
        for (Employee emp : employees) {
            emp.displayDetails();  // Different implementation for each type
            emp.work();           // Common method
            System.out.println();
        }
        
        // Demonstrate specific methods
        System.out.println("=== Specific Employee Actions ===");
        manager.conductMeeting();
        developer.writeCode();
        intern.attendTraining();
        
        // Calculate total payroll
        System.out.println("\n=== Payroll Summary ===");
        double totalPayroll = 0;
        
        for (Employee emp : employees) {
            double salary = emp.calculateSalary();  // Overridden method called
            totalPayroll += salary;
            System.out.printf("%s (ID: %d): $%.2f\n", emp.name, emp.empId, salary);
        }
        
        System.out.printf("\nTotal Company Payroll: $%.2f\n", totalPayroll);
        
        // Demonstrate runtime polymorphism
        System.out.println("\n=== Runtime Polymorphism Demo ===");
        Employee emp1 = new Manager("David Wilson", 104, 55000, "Sales", 12000, 5);
        Employee emp2 = new Developer("Eva Davis", 105, 48000, "Development", "Python", 8);
        
        // Reference type is Employee, but actual object determines method called
        System.out.println("emp1 salary: $" + emp1.calculateSalary());  // Manager's method
        System.out.println("emp2 salary: $" + emp2.calculateSalary());  // Developer's method
    }
}

Q2. (GTU Winter 2021) What is the use of 'super' keyword in Java? Explain with examples including constructor chaining.

Solution:

The 'super' Keyword in Java:

The 'super' keyword is a reference variable that refers to the immediate parent class object. It is used to access parent class members from child class.

Uses of 'super' Keyword:

1. Access Parent Class Variables:

class Vehicle {
    protected String brand = "Generic Vehicle";
    protected int maxSpeed = 100;
}

class Car extends Vehicle {
    private String brand = "Toyota";  // Hides parent's brand
    private int maxSpeed = 180;       // Hides parent's maxSpeed
    
    public void displayInfo() {
        System.out.println("Car brand: " + this.brand);        // Child's brand
        System.out.println("Vehicle brand: " + super.brand);   // Parent's brand
        System.out.println("Car max speed: " + this.maxSpeed);    // Child's maxSpeed
        System.out.println("Vehicle max speed: " + super.maxSpeed); // Parent's maxSpeed
    }
}

2. Call Parent Class Methods:

class Animal {
    public void eat() {
        System.out.println("Animal is eating food");
    }
    
    public void sleep() {
        System.out.println("Animal is sleeping");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        super.eat();  // Call parent's eat method first
        System.out.println("Dog is eating dog food");
    }
    
    public void displayBehavior() {
        super.sleep();  // Call parent's sleep method
        this.eat();     // Call overridden eat method
    }
}

3. Constructor Chaining with 'super':

// Base class
class Person {
    protected String name;
    protected int age;
    protected String address;
    
    // Default constructor
    public Person() {
        this.name = "Unknown";
        this.age = 0;
        this.address = "Not specified";
        System.out.println("Person default constructor called");
    }
    
    // Parameterized constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        this.address = "Not specified";
        System.out.println("Person constructor (name, age) called");
    }
    
    // Full constructor
    public Person(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
        System.out.println("Person full constructor called");
    }
    
    public void displayPersonInfo() {
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
        System.out.println("Address: " + address);
    }
}

// Derived class
class Student extends Person {
    private String studentId;
    private String course;
    private double gpa;
    
    // Constructor 1: Default
    public Student() {
        super();  // Call parent's default constructor (optional - called automatically)
        this.studentId = "STU000";
        this.course = "General";
        this.gpa = 0.0;
        System.out.println("Student default constructor called");
    }
    
    // Constructor 2: Basic info
    public Student(String name, int age, String studentId) {
        super(name, age);  // Call parent's parameterized constructor
        this.studentId = studentId;
        this.course = "General";
        this.gpa = 0.0;
        System.out.println("Student basic constructor called");
    }
    
    // Constructor 3: Full info
    public Student(String name, int age, String address, 
                  String studentId, String course, double gpa) {
        super(name, age, address);  // Call parent's full constructor
        this.studentId = studentId;
        this.course = course;
        this.gpa = gpa;
        System.out.println("Student full constructor called");
    }
    
    @Override
    public void displayPersonInfo() {
        super.displayPersonInfo();  // Call parent method
        System.out.println("Student ID: " + studentId);
        System.out.println("Course: " + course);
        System.out.println("GPA: " + gpa);
    }
    
    public void study() {
        System.out.println(name + " is studying " + course);
    }
}

// Further derived class
class GraduateStudent extends Student {
    private String researchTopic;
    private String advisor;
    
    public GraduateStudent(String name, int age, String address, 
                          String studentId, String course, double gpa, 
                          String researchTopic, String advisor) {
        super(name, age, address, studentId, course, gpa);  // Chain to Student constructor
        this.researchTopic = researchTopic;
        this.advisor = advisor;
        System.out.println("GraduateStudent constructor called");
    }
    
    @Override
    public void displayPersonInfo() {
        super.displayPersonInfo();  // Call Student's method (which calls Person's)
        System.out.println("Research Topic: " + researchTopic);
        System.out.println("Advisor: " + advisor);
    }
    
    @Override
    public void study() {
        super.study();  // Call Student's study method
        System.out.println(name + " is also conducting research on " + researchTopic);
    }
}

// Demo class
public class SuperKeywordDemo {
    public static void main(String[] args) {
        System.out.println("=== Constructor Chaining Demo ===\n");
        
        System.out.println("Creating Student with default constructor:");
        Student student1 = new Student();
        student1.displayPersonInfo();
        System.out.println();
        
        System.out.println("Creating Student with basic constructor:");
        Student student2 = new Student("John Doe", 20, "STU001");
        student2.displayPersonInfo();
        System.out.println();
        
        System.out.println("Creating Student with full constructor:");
        Student student3 = new Student("Jane Smith", 22, "123 Main St", 
                                      "STU002", "Computer Science", 3.8);
        student3.displayPersonInfo();
        System.out.println();
        
        System.out.println("Creating GraduateStudent (multilevel inheritance):");
        GraduateStudent gradStudent = new GraduateStudent(
            "Alice Johnson", 25, "456 Oak Ave", "GRAD001", 
            "Computer Science", 3.9, "Machine Learning", "Dr. Brown");
        
        gradStudent.displayPersonInfo();
        System.out.println();
        
        System.out.println("=== Method Calls with super ===");
        gradStudent.study();
        
        System.out.println("\n=== Summary of 'super' Usage ===");
        System.out.println("1. super() - calls parent constructor (must be first statement)");
        System.out.println("2. super.variable - accesses parent class variable");
        System.out.println("3. super.method() - calls parent class method");
        System.out.println("4. Constructor chaining creates proper initialization hierarchy");
    }
}

Key Points about 'super' Keyword:

  • Constructor Chaining: super() must be the first statement in child constructor
  • Automatic Call: If super() is not explicit, Java automatically calls super()
  • Variable Access: Useful when child hides parent variables
  • Method Enhancement: Call parent method and add additional functionality
  • Multilevel Inheritance: Each level calls its immediate parent

Q3. (GTU Summer 2020) Differentiate between method overriding and method overloading with suitable examples.

Solution:

Method Overriding vs Method Overloading:

AspectMethod OverridingMethod Overloading
DefinitionRedefining parent method in child classMultiple methods with same name, different parameters
Inheritance RequiredYes (parent-child relationship)No (same class)
Method SignatureMust be identicalMust be different (parameters)
Polymorphism TypeRuntime (Dynamic)Compile-time (Static)
BindingLate bindingEarly binding
PerformanceSlightly slowerFaster
Access ModifierCannot reduce visibilityCan have any access modifier
Return TypeSame or covariantCan be different

Method Overriding Example:

// Method Overriding - Runtime Polymorphism
class Shape {
    public void draw() {
        System.out.println("Drawing a generic shape");
    }
    
    public double getArea() {
        return 0.0;
    }
    
    public void displayInfo() {
        System.out.println("Shape information");
        System.out.println("Area: " + getArea());  // Will call overridden version
    }
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override  // Method Overriding
    public void draw() {
        System.out.println("Drawing a circle with radius " + radius);
    }
    
    @Override  // Method Overriding
    public double getArea() {
        return Math.PI * radius * radius;
    }
    
    // Additional method specific to Circle
    public double getCircumference() {
        return 2 * Math.PI * radius;
    }
}

class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override  // Method Overriding
    public void draw() {
        System.out.println("Drawing a rectangle " + width + "x" + height);
    }
    
    @Override  // Method Overriding
    public double getArea() {
        return width * height;
    }
    
    // Additional method specific to Rectangle
    public double getPerimeter() {
        return 2 * (width + height);
    }
}

Method Overloading Example:

// Method Overloading - Compile-time Polymorphism
class Calculator {
    
    // Method Overloading - Different number of parameters
    public int add(int a, int b) {
        System.out.println("Adding two integers");
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        System.out.println("Adding three integers");
        return a + b + c;
    }
    
    // Method Overloading - Different parameter types
    public double add(double a, double b) {
        System.out.println("Adding two doubles");
        return a + b;
    }
    
    public String add(String a, String b) {
        System.out.println("Concatenating two strings");
        return a + b;
    }
    
    // Method Overloading - Different parameter order
    public void display(int a, String b) {
        System.out.println("Integer: " + a + ", String: " + b);
    }
    
    public void display(String a, int b) {
        System.out.println("String: " + a + ", Integer: " + b);
    }
    
    // Method Overloading with varargs
    public int add(int... numbers) {
        System.out.println("Adding " + numbers.length + " integers using varargs");
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
}

Demonstration Program:

public class OverridingVsOverloadingDemo {
    public static void main(String[] args) {
        System.out.println("=== Method Overriding Demo ===");
        
        // Runtime Polymorphism - Method determined at runtime
        Shape shape1 = new Circle(5.0);
        Shape shape2 = new Rectangle(4.0, 6.0);
        Shape shape3 = new Shape();
        
        Shape[] shapes = {shape1, shape2, shape3};
        
        for (Shape shape : shapes) {
            shape.draw();           // Different method called for each object
            shape.displayInfo();    // Different area calculation
            System.out.println("---");
        }
        
        System.out.println("\n=== Method Overloading Demo ===");
        
        Calculator calc = new Calculator();
        
        // Method determined at compile time based on parameters
        System.out.println("Result: " + calc.add(5, 3));           // Two int
        System.out.println("Result: " + calc.add(5, 3, 2));        // Three int
        System.out.println("Result: " + calc.add(5.5, 3.2));       // Two double
        System.out.println("Result: " + calc.add("Hello ", "World")); // Two String
        
        calc.display(10, "Test");      // int, String
        calc.display("Test", 10);      // String, int
        
        System.out.println("Result: " + calc.add(1, 2, 3, 4, 5));  // Varargs
        
        System.out.println("\n=== Key Differences Summary ===");
        System.out.println("Method Overriding:");
        System.out.println("- Requires inheritance");
        System.out.println("- Same method signature");
        System.out.println("- Runtime decision (dynamic binding)");
        System.out.println("- Enables runtime polymorphism");
        
        System.out.println("\nMethod Overloading:");
        System.out.println("- Same class");
        System.out.println("- Different method signature");
        System.out.println("- Compile-time decision (static binding)");
        System.out.println("- Enables compile-time polymorphism");
    }
}

Hands-on Lab Exercises

Exercise 1: Vehicle Hierarchy System

  1. Create a comprehensive vehicle inheritance hierarchy:
    • Base Vehicle class with common properties and methods
    • LandVehicle, WaterVehicle, AirVehicle subclasses
    • Specific vehicles: Car, Truck, Boat, Ship, Airplane, Helicopter
    • Implement method overriding for start(), stop(), move() methods
  2. Include proper constructor chaining and super keyword usage
  3. Demonstrate polymorphism with arrays of different vehicle types

Exercise 2: Educational Institution Management

  1. Design a complete educational system:
    • Person base class with common attributes
    • Student, Teacher, Administrator subclasses
    • Specialized classes: GraduateStudent, Professor, Principal
    • Override methods like displayInfo(), calculateFees(), getSalary()
  2. Implement method overloading for multiple constructors and utility methods
  3. Create interactive system for managing different person types

Exercise 3: Media Library System

  1. Build a media management system:
    • Media base class with common properties
    • Book, Movie, Music, Game subclasses
    • Override methods: play(), display(), calculateRentalCost()
    • Implement different rental pricing strategies
  2. Include method overloading for search and filter operations
  3. Demonstrate inheritance benefits in code maintenance

Lecture Summary

Key Concepts Covered:

  • Inheritance fundamentals and types
  • Method overriding and runtime polymorphism
  • Super keyword for parent access
  • Constructor chaining in inheritance
  • Access modifiers in inheritance hierarchy
  • Method overriding vs method overloading
  • Real-world inheritance design patterns

Learning Outcomes Achieved:

  • ✅ Master inheritance concept and implementation
  • ✅ Apply method overriding for polymorphism
  • ✅ Use super keyword effectively
  • ✅ Design inheritance hierarchies properly
  • ✅ Understand access control in inheritance
  • ✅ Distinguish overriding from overloading
  • ✅ Build scalable object-oriented systems

Next Lecture: Polymorphism and Abstract Classes

Topics: Runtime polymorphism, abstract classes, abstract methods, interface introduction

Thank You!

Questions & Discussion


Next: Lecture 11 - Polymorphism and Abstract Classes


Course: 4343203 Java Programming
Unit 2: Object-Oriented Programming
GTU Semester 4