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
Single Inheritance
- Explanation: One child class inherits from one parent class.
- Example:
Carinherits fromVehicle.
Multilevel Inheritance
- Explanation: A class is derived from a child class, forming a chain.
- Example:
Doginherits fromMammal, which inherits fromAnimal.
Hierarchical Inheritance
- Explanation: Multiple child classes inherit from a single parent class.
- Example:
CarandBikeinherit fromVehicle.
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:
- Method Overloading – same method name but different parameters in the same class.
- 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)andadd(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
AnimalhasmakeSound(), 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
implementskeyword. - 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-catchor declare them withthrows. - 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:
tryblock- Contains the code that might throw an exception.
catchblock- Handles the exception thrown in the
tryblock. You can have multiplecatchblocks.
- Handles the exception thrown in the
finallyblock- Contains code that always executes, regardless of whether an exception occurred. Typically used to release resources (closing files or connections).
Flow of Execution
- Program enters the try block.
- If no exception occurs → skip
catchblocks → executefinally. - If an exception occurs → skip remaining
trycode → find a matchingcatchblock.
Differentiating throw and throws
In Java, throw and throws are related to exception handling but serve distinct roles:
| Aspect | throw | throws |
|---|---|---|
| Purpose | Used 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 used | Inside the method body. | In the method signature (after method parentheses). |
| Number of exceptions | Can throw only one exception at a time. | Can declare multiple exceptions separated by commas. |
| Keyword type | Statement | Part 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:
- Create a class that extends
Exception(for checked) or extendsRuntimeException(for unchecked). - Provide a constructor to pass a custom error message.
- Use
throwto throw the exception. - Use
try-catchto 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.
