OOP Concepts: Inheritance, Polymorphism, and Exceptions

Definition of Inheritance

Inheritance is an OOP concept that allows a class (called the child or subclass) to acquire the properties and behaviors (fields and methods) of another class (called the parent or superclass).

  • It promotes code reusability, modularity, and hierarchical classification.
  • The child class can add new features or override existing ones of the parent class.
  • Example Structure:

    class Parent { /* fields and methods */ }

    class Child extends Parent { /* additional fields and methods */ }

Types of Inheritance

  1. Single Inheritance

    • Explanation: One child class inherits from one parent class.
    • Example: Car inherits from Vehicle.
  2. Multilevel Inheritance

    • Explanation: A class is derived from a child class, forming a chain.
    • Example: Dog inherits from Mammal, which inherits from Animal.
  3. Hierarchical Inheritance

    • Explanation: Multiple child classes inherit from a single parent class.
    • Example: Car and Bike inherit from Vehicle.
  4. Multiple Inheritance

    • Explanation: A class inherits from more than one parent class. (In Java, achieved using interfaces.)

The Diamond Problem and Java Solution

The Diamond Problem occurs in multiple inheritance when a class inherits from two classes that share a common parent, creating ambiguity regarding which inherited method implementation to use.

Java does not support multiple inheritance of classes, thus preventing the Diamond Problem entirely with classes.

Java supports multiple inheritance through interfaces. If multiple interfaces define the same method, the implementing class must explicitly define which version to use, removing ambiguity.

Key Points

  • The Diamond Problem arises only with multiple class inheritance.
  • Java avoids it by allowing only single class inheritance.

Polymorphism in OOP

Polymorphism means “many forms”. In OOP, it allows objects of different classes to be treated as objects of a common superclass. It also allows the same method or operator to behave differently in different contexts.

Key Points:

  • Enables flexibility and reusability.
  • Reduces code redundancy.
  • Two main types: Compile-time polymorphism and Run-time polymorphism.

Compile-Time Polymorphism (Static)

  • Definition: The method execution is determined at compile time.
  • Achieved by:
    1. Method Overloading – same method name but different parameters in the same class.
    2. Operator Overloading – same operator behaves differently for different data types (limited support in Java).

Run-Time Polymorphism (Dynamic)

  • Definition: The method execution is determined at runtime, based on the object type.
  • Achieved by: Method Overriding – a subclass provides its own implementation of a method defined in the parent class.

Method Overloading vs. Method Overriding

Method Overloading

  • Definition: Two or more methods in the same class have the same name but different parameters (type or number).
  • Polymorphism Type: Compile-time (Static) polymorphism.
  • Parameters: Must be different (number or type).
  • Inheritance: Not required; can happen in the same class.
  • Example Concept: add(int a, int b) and add(double a, double b).

Method Overriding

  • Definition: A subclass provides its own implementation of a method already defined in the parent class with the same signature.
  • Polymorphism Type: Run-time (Dynamic) polymorphism.
  • Parameters: Must be exactly the same.
  • Inheritance: Required, between parent and child class.
  • Example Concept: Parent class Animal has makeSound(), overridden in a subclass.

Interfaces in Java

What is an Interface?

An interface is a blueprint of a class in Java. It defines a set of abstract methods (methods without a body) that any implementing class must provide.

Key Points:

  • An interface cannot have instance variables (only constants are allowed).
  • All methods in an interface are public and abstract by default.
  • A class implements an interface using the implements keyword.
  • Interfaces are used to achieve abstraction and multiple inheritance in Java.

Real-Life Analogy: Think of an interface as a contract: if a class signs it, it promises to provide all the behaviors defined in the interface (e.g., a “RemoteControl” interface requires turnOn() and turnOff() methods).

Multiple Inheritance using Interface

Java does not support multiple class inheritance due to the Diamond Problem. However, a class can implement multiple interfaces, achieving multiple inheritance of method declarations without ambiguity.


Exceptions in Java

What is an Exception?

An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions (e.g., dividing by zero or invalid input).

  • Java provides a robust mechanism to handle exceptions using try, catch, throw, throws, and finally keywords.

Real-life analogy: If a tire bursts while driving (exception), you handle it by using a spare tire (exception handling) to continue safely.

Types of Exceptions in Java

Java exceptions are broadly classified into two categories:

Checked Exceptions (Compile-Time Exceptions)

  • Definition: Exceptions that are checked by the compiler at compilation time.
  • Handling: The programmer must handle these using try-catch or declare them with throws.
  • Examples: IOException, SQLException, ClassNotFoundException.


What is Exception Handling?

Exception handling is a mechanism to manage runtime errors in a controlled way, preventing the program from crashing and allowing the normal flow to continue after taking appropriate action.

Components of Exception Handling

The primary constructs in Java are:

  1. try block

    • Contains the code that might throw an exception.
  2. catch block

    • Handles the exception thrown in the try block. You can have multiple catch blocks.
  3. finally block

    • Contains code that always executes, regardless of whether an exception occurred. Typically used to release resources (closing files or connections).

Flow of Execution

  1. Program enters the try block.
  2. If no exception occurs → skip catch blocks → execute finally.
  3. If an exception occurs → skip remaining try code → find a matching catch block.

Differentiating throw and throws

In Java, throw and throws are related to exception handling but serve distinct roles:

Aspectthrowthrows
PurposeUsed to explicitly throw an exception from a method or block.Used in a method declaration to indicate that the method might throw certain exceptions.
Where usedInside the method body.In the method signature (after method parentheses).
Number of exceptionsCan throw only one exception at a time.Can declare multiple exceptions separated by commas.
Keyword typeStatementPart of method signature

Example using throw:

public class ThrowExample { static void checkAge(int age) { if (age < 18) { throw new ArithmeticException("Age must be at least 18"); } else { System.out.println("Access granted"); } } public static void main(String[] args) { checkAge(15); // This will throw an exception } }

Output:

Exception in thread "main" java.lang.ArithmeticException: Age must be at least 18

Example of Exception Handling with try-catch-finally

This program demonstrates handling division by zero:

import java.util.Scanner;

public class ExceptionHandlingDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            // Ask user for input
            System.out.print("Enter a numerator: ");
            int numerator = scanner.nextInt();

            System.out.print("Enter a denominator: ");
            int denominator = scanner.nextInt();

            // Perform division
            int result = numerator / denominator;
            System.out.println("Result: " + result);
        } 
        catch (ArithmeticException e) {
            // Handle division by zero
            System.out.println("Error: Cannot divide by zero!");
        } 
        catch (Exception e) {
            // Handle other exceptions
            System.out.println("An unexpected error occurred: " + e.getMessage());
        } 
        finally {
            // This block always executes
            System.out.println("Thank you for using the program.");
            scanner.close();
        }
    }
}

User-Defined Exceptions in Java

You can create custom exceptions when built-in exceptions are insufficient. These are user-defined exceptions.

Steps to create a user-defined exception:

  1. Create a class that extends Exception (for checked) or extends RuntimeException (for unchecked).
  2. Provide a constructor to pass a custom error message.
  3. Use throw to throw the exception.
  4. Use try-catch to handle it.

Example: Age Validation

// Step 1: Create user-defined exception class InvalidAgeException extends Exception { public InvalidAgeException(String message) { super(message); // Pass message to Exception class } } public class UserDefinedExceptionDemo { // Step 2: Method to check age static void checkAge(int age) throws InvalidAgeException { if (age < 18) { // Step 3: Throw custom exception throw new InvalidAgeException("Age must be 18 or older to vote."); } else { System.out.println("You are eligible to vote."); } } public static void main(String[] args) { try { checkAge(15); // Test with invalid age } catch (InvalidAgeException e) { // Step 4: Handle the exception System.out.println("Exception caught: " + e.getMessage()); } } }

Output:

Exception caught: Age must be 18 or older to vote.