Java Unit - 3
Inheritance and Polymorphism
Inheritance
Inheritance is a fundamental concept of Object-Oriented Programming (OOP) in Java that
allows a class to inherit properties (fields) and behavior (methods) from another class. It
helps in code reusability, method overriding, and creating a hierarchical classification of
objects.
The class that inherits from another class is called the subclass (child class), and the class
from which it inherits is called the superclass (parent class).
Key Benefits of Inheritance
● Code Reusability: Common properties and methods can be defined in a parent class
and reused in child classes.
● Method Overriding: Allows a subclass to provide a specific implementation of a method
already defined in the parent class.
● Polymorphism Support: Helps in achieving dynamic method dispatch (runtime
polymorphism).
● Better Organization: Creates a logical hierarchy of classes in a program.
Types of Inheritance in Java
Java supports different types of inheritance:
1. Single Inheritance
2. Multilevel Inheritance
3. Hierarchical Inheritance
4. Multiple Inheritance using Interfaces
Note: Java does not support Multiple Inheritance using classes to avoid ambiguity/diamond
problem , but it can be achieved using interfaces.
1. Single Inheritance
Definition: In Single Inheritance, a child class inherits from a single parent class.
Example:
class Vehicle { Ummed Singh
String brand = "Toyota"; ByteXL
void honk() {
[Link]("Vehicle is honking...");
}
}
// Child class (Sub class)
class Car extends Vehicle {
int speed = 100;
void display() {
[Link]("Brand: " + brand);
[Link]("Speed: " + speed + " km/h");
}
}
// Main class
public class SingleInheritanceExample {
public static void main(String[] args) {
Car myCar = new Car();
[Link]();
[Link]();
}
}
Output:
Brand: Toyota
Speed: 100 km/h
Vehicle is honking...
Real-World Use Case:
💡 Banking System (User Account Inheritance)
● Parent Class: BankAccount (common properties like balance, account number)
● Child Class: SavingsAccount, CurrentAccount
class BankAccount { Ummed Singh
String accountHolder;
double balance;
BankAccount(String accountHolder, double balance) {
[Link] = accountHolder;
[Link] = balance;
}
void deposit(double amount) {
balance += amount;
[Link](amount + " deposited. New balance: " +
balance);
}
}
// Child class inheriting from BankAccount
class SavingsAccount extends BankAccount {
SavingsAccount(String accountHolder, double balance) {
super(accountHolder, balance);
}
void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
[Link](amount + " withdrawn. Remaining
balance: " + balance);
} else {
[Link]("Insufficient balance.");
}
}
}
// Main class to test the implementation
public class BankingSystem {
public static void main(String[] args) {
SavingsAccount myAccount = new SavingsAccount("Ummed
Singh", 500.0);
[Link](200);
[Link](100);
[Link](700);
}
}
Problem Without Inheritance
If every account type (SavingsAccount, CurrentAccount) defines balance and deposit
separately, it leads to duplicate code.
Solution Through Inheritance
By making BankAccount a superclass, all account types can inherit common properties,
avoiding redundancy.
2. Multilevel Inheritance
Definition: In Multilevel Inheritance, a class inherits from another class, which in turn is
inherited by another class.
Example:
class Animal { Ummed Singh
void eat() {
[Link]("This animal eats food.");
}
}
// Derived class
class Mammal extends Animal {
void walk() {
[Link]("Mammals can walk.");
}
}
// Derived from Mammal
class Human extends Mammal {
void speak() {
[Link]("Humans can speak.");
}
}
// Main class
public class MultilevelInheritanceExample {
public static void main(String[] args) {
Human human = new Human();
[Link]();
[Link]();
[Link]();
}
}
Output:
This animal eats food.
Mammals can walk.
Humans can speak.
Real-World Use Case:
💡 Educational Management System
● Parent Class: Person (common properties like name, age)
● Child Class: Student (adds roll number)
● Grandchild Class: EngineeringStudent (specializes further)
Problem Without Inheritance
Redundant fields for each role like name, age, gender.
Solution Through Inheritance
By inheriting Person, Student, and EngineeringStudent, we create a structured hierarchy.
class Person { Ummed Singh
String name;
Person(String name) {
[Link] = name;
}
void displayPersonInfo() {
[Link]("Name: " + name);
// Child class inheriting from Person
class Student extends Person {
int rollNumber;
Student(String name, int rollNumber) {
super(name);
[Link] = rollNumber;
void displayStudentInfo() {
displayPersonInfo(); // Calling parent class method
[Link]("Roll Number: " + rollNumber);
// Grandchild class inheriting from Student
class EngineeringStudent extends Student {
String specialization;
EngineeringStudent(String name, int rollNumber, String
specialization) {
super(name, rollNumber);
[Link] = specialization;
void displayEngineeringStudentInfo() {
displayStudentInfo(); // Calling student class method
[Link]("Specialization: " + specialization);
// Main class to test the hierarchy
public class EducationSystem {
public static void main(String[] args) {
EngineeringStudent engStudent = new
EngineeringStudent("Ummed Singh", 1001, "Computer Science");
[Link]();
3. Hierarchical Inheritance
Definition: In Hierarchical Inheritance, multiple child classes inherit from a single parent class.
Example:
// Parent class Ummed Singh
class Shape {
void area() {
[Link]("Calculating area...");
}
}
// Child class 1
class Circle extends Shape {
double radius = 5;
void area() {
[Link]("Area of Circle: " + (3.14 * radius *
radius));
}
}
// Child class 2
class Rectangle extends Shape {
double length = 4, width = 6;
void area() {
[Link]("Area of Rectangle: " + (length *
width));
}
}
// Main class
public class HierarchicalInheritanceExample {
public static void main(String[] args) {
Circle c = new Circle();
[Link]();
Rectangle r = new Rectangle();
[Link]();
}
}
Real-World Use Case:
💡 University Management System
● Parent Class: LpuCollege(common variable like director name)
● Child Classes: EngDpart, ManagementDepart
Problem Without Inheritance
Each display method must implement directorName separately.
Solution Through Inheritance
Create a LpuCollege base class to enforce consistency.
class LpuCollege{ Ummed Singh
String directorName = "Mr [Link] Kumar Mittal Sir";
class EngineeringDepart extends LpuCollege{
String hod = "";
void display(){
[Link](directorName);
class ManagementDepartment extends LpuCollege{
void display(){
[Link](directorName);
public class LpuManagementSystem {
public static void main(String[] args) {
ManagementDepartment obj = new ManagementDepartment();
[Link]();
}
}
4. Multiple Inheritance using Interfaces
Java does not support multiple inheritance with classes to avoid ambiguity but allows it with
interfaces.
Example:
interface Printable { Ummed Singh
void print();
}
interface Showable {
void show();
}
// Multiple inheritance through interfaces
class Document implements Printable, Showable {
public void print() {
[Link]("Printing document...");
}
public void show() {
[Link]("Showing document...");
}
}
public class MultipleInheritanceExample {
public static void main(String[] args) {
Document doc = new Document();
[Link]();
[Link]();
}
}
Real-World Use Case:
💡 Messaging System
● Interface 1: Sendable (defines send())
● Interface 2: Receivable (defines receive())
● Class: Email (implements both interfaces)
Problem Without Inheritance
Emails and SMS need to implement send() and receive() separately.
Solution Through Inheritance
A single class implements both interfaces for code reusability.
// Interface for sending messages Ummed Singh
interface Sendable {
void send(String message);
// Interface for receiving messages
interface Receivable {
void receive();
// Class implementing both interfaces
class Email implements Sendable, Receivable {
private String inboxMessage;
@Override
public void send(String message) {
[Link]("Sending Email: " + message);
inboxMessage = message; // Simulating sending an email
}
@Override
public void receive() {
if (inboxMessage != null) {
[Link]("Received Email: " +
inboxMessage);
} else {
[Link]("No new emails.");
// Main class to test the implementation
public class MessagingSystem {
public static void main(String[] args) {
Email email = new Email();
[Link]("Hello, this is a test email.");
[Link]();
Diamond Problem in Java and Its Solution
The diamond problem occurs in programming languages that support multiple inheritance,
leading to ambiguity when a class inherits from multiple classes that have methods with the
same name.
Java avoids this problem by not allowing multiple inheritance with classes but allows it
with interfaces.
Diamond Problem in Multiple Inheritance (Not Allowed in
Java)
Problem Example (If Java Allowed Multiple Inheritance)
// Parent Class 1 Ummed Singh
class A {
void show() {
[Link]("A's show method");
}
}
// Parent Class 2
class B {
void show() {
[Link]("B's show method");
}
}
// Child Class trying to inherit both A and B (Not Allowed in
❌
Java)
class C extends A, B { // ERROR: Java does not support
multiple class inheritance
public static void main(String[] args) {
❓
C obj = new C();
[Link](); // Ambiguity: Which show() method to call?
A's or B's?
}
}
Problem:
● If Java allowed multiple inheritance with classes, class C would inherit the show()
method from both A and B, causing ambiguity.
Solution: Use Interfaces in Java
Java solves the diamond problem by allowing multiple inheritance using interfaces, where
ambiguity is resolved explicitly.
Example Using Interfaces (Allowed in Java)
// Interface 1 Ummed Singh
interface A {
default void show() {
[Link]("A's show method");
}
}
// Interface 2
interface B {
default void show() {
[Link]("B's show method");
}
}
// Class implementing both interfaces
class C implements A, B {
// Overriding show() method to resolve ambiguity
@Override
public void show() {
[Link]("Resolving ambiguity...");
[Link](); // Explicitly calling A's show()
[Link](); // Explicitly calling B's show()
}
public static void main(String[] args) {
C obj = new C();
[Link]();
}
}
Output:
Resolving ambiguity...
A's show method
B's show method
How This Works in Java
1. Interfaces allow default methods (Java 8+) to define behavior.
2. If multiple interfaces have the same method, Java forces the child class to override it
and specify which version to use.
3. The child class can use [Link]() or [Link]() to
resolve ambiguity explicitly.
1. What is Polymorphism?
Polymorphism is a core concept in Object-Oriented Programming (OOP) that allows a single
interface or a method to be used for different types. It enables code reusability and flexibility,
making it easier to manage and extend.
Types of Polymorphism in Java:
1. Compile-Time Polymorphism/static type (Method Overloading): Method Overloading
allows multiple methods in the same class with the same name but different parameters.
The compiler determines which method to invoke based on the method signature.
2. Run-Time Polymorphism/dynamic type (Method Overriding)
Run-Time Polymorphism (Method Overriding)
Method Overriding allows a subclass to provide a specific implementation of a method that is
already defined in its superclass. This is achieved using dynamic method dispatch, where the
method call is resolved at runtime.
Example of Method Overriding in a Real-World Software (Ride Booking App
like Uber):
// Parent class Ummed Singh
class Ride {
void calculateFare(int distance) {
[Link]("Ride fare for " + distance + " km is
calculated.");
}
}
// Child class for Economy ride
class EconomyRide extends Ride {
@Override
void calculateFare(int distance) {
[Link]("Economy ride fare for " + distance +
" km: $" + (distance * 2));
}
}
// Child class for Luxury ride
class LuxuryRide extends Ride {
@Override
void calculateFare(int distance) {
[Link]("Luxury ride fare for " + distance + "
km: $" + (distance * 5));
}
}
// Main application class
public class RideBookingAppOverriding {
public static void main(String[] args) {
Ride myRide; // Reference variable of parent class
myRide = new EconomyRide();
[Link](10); // Calls EconomyRide's method
myRide = new LuxuryRide();
[Link](10); // Calls LuxuryRide's method
}
}
Advantages of Polymorphism in Java
✅ Code Reusability – We can write a common method with multiple implementations.
✅ Scalability – Easy to add new features (new ride types in the Uber example).
✅ Maintainability – Clean and structured code with better separation of concerns.
super Keyword
The super keyword in Java is used to refer to the parent class (superclass) of a subclass. It is
primarily used for the following purposes:
1. Access Parent Class Methods (Method Overriding): When a subclass overrides a
method from its parent class, the overridden method can still be accessed using
[Link]().
2. Call Parent Class Constructor: When a subclass object is created, the parent class
constructor runs before the subclass constructor or It must be the first statement in the
subclass constructor. The super() keyword allows us to explicitly call a specific parent
constructor.
3. Access Parent Class Variables (When Hidden by Subclass): If a subclass defines a
variable with the same name as its parent class, it hides the parent’s variable. The
super keyword allows access to the parent class variable.
super is commonly used to eliminate ambiguity between parent and child class
members when they have the same name.
Example:
class ParentSuper { Ummed Singh
String name = "Parent";
ParentSuper(){
[Link]("Executed the ParentClass
constructor");
}
void display() {
[Link]("This is the parent class.");
}
}
class ChildSuper extends ParentSuper {
String name = "Child";
ChildSuper(){
super();
void display() {
[Link]("This is the child class.");
}
void show() {
[Link]("Name in Child class: " + name);
[Link]("Name in Parent class: " +
[Link]);
display();
// Calls Child's display method
[Link](); // Calls Parent's display method
}
}
public class SuperDemo {
public static void main(String[] args) {
ChildSuper child = new ChildSuper();
[Link]();
}
}
Introduction to Object Class
In Java, the Object class is the superclass of all classes. Every class in Java implicitly
extends the Object class (except when explicitly extending another class).
Key Points:
● The Object class is part of [Link] package.
● It provides basic methods that all Java objects inherit.
● It allows Java to implement key object-oriented programming concepts like
polymorphism and inheritance.
2. Methods of Object Class
The Object class provides several important methods that can be overridden in subclasses for
customization.
List of Important Methods in the Object Class
Method Description
equals(Object obj) Checks if two objects are equal.
hashCode() Returns a hash code value for the object.
toString() Returns a string representation of the object.
clone() Creates a duplicate (shallow copy) of an object.
finalize() Called by the garbage collector before destroying an
object.
getClass() Returns the runtime class of the object.
wait(), notify(), Used in thread synchronization.
notifyAll()
Understanding Object Class Methods with Examples
(i) equals() Method
The equals() method compares two objects for equality. By default, it uses the == operator,
which checks memory address (reference equality).
Example Without Overriding equals()
class Car { Ummed Singh
String model;
Car(String model) {
[Link] = model;
}
}
public class ObjectEqualsExample {
public static void main(String[] args) {
Car car1 = new Car("Tesla");
Car car2 = new Car("Tesla");
[Link]([Link](car2)); // false
(different memory locations)
}
}
Overriding equals() for Logical Comparison
class Bike { Ummed Singh
String model;
Bike(String model) {
[Link] = model;
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // Same reference
if (obj == null || getClass() != [Link]()) return
false;
Bike bike = (Bike) obj;
return [Link]([Link]);
public class ObjectEqualsExample1 {
public static void main(String[] args) {
Bike bike1 = new Bike("Tesla");
Bike bike2 = new Bike("Tesla");
[Link]([Link](bike2)); // true (based
on model name)
}
}
(ii) hashCode() Method
The hashCode() method returns an integer that represents the memory address or a logical
value. It is important when storing objects in hash-based collections (e.g., HashMap,
HashSet).
Example Without Overriding hashCode()
class BMW { Ummed Singh
String model;
BMW(String model) {
[Link] = model;
public class ObjectHashCodeExample {
public static void main(String[] args) {
BMW bmw1 = new BMW("BMW");
BMW bmw2 = new BMW("BMW");
[Link]([Link]()); // Different hash
codes
[Link]([Link]()); // Different hash
codes
}
Overriding hashCode() for Consistent Hashing
class Car3 { Ummed Singh
String model;
Car3(String model) {
[Link] = model;
@Override
public int hashCode() {
return [Link]();
public class ObjectHashCodeExample1 {
public static void main(String[] args) {
Car3 car1 = new Car3("BMW");
Car3 car2 = new Car3("BMW");
[Link]([Link]()); // Same hash code
[Link]([Link]()); // Same hash code
}
Relation Between equals() and hashCode()
● If equals() returns true, then hashCode() must return the same value.
● If equals() returns false, hashCode() can be different.
(iii) toString() Method
The toString() method returns a string representation of an object.
Default toString() Implementation
class Thar { Ummed Singh
public class ObjectToStringExample {
public static void main(String[] args) {
Thar thar = new Thar();
[Link]([Link]()); // Output:
Thar@1b28cdfa (memory reference)
Overriding toString() for Better Output
class Mahindra { Ummed Singh
String model;
Mahindra(String model) {
[Link] = model;
@Override
public String toString() {
return "Car Model: " + model;
public class ObjectToStringExample1 {
public static void main(String[] args) {
Mahindra mahindra = new Mahindra("Mahindra");
[Link]([Link]()); // Output: Car
Model: Mahindra
(iv) clone() Method (Shallow Copy)
The clone() method creates a copy of an object. It requires the Cloneable interface.
Example of clone()
class Car implements Cloneable { Ummed Singh
String model;
Car(String model) {
[Link] = model;
@Override
protected Object clone() throws CloneNotSupportedException {
return [Link]();
public class ObjectCloneExample {
public static void main(String[] args) throws
CloneNotSupportedException {
Car car1 = new Car("Tesla");
Car car2 = (Car) [Link]();
[Link]([Link]); // Tesla
[Link]([Link]); // Tesla
}
(v) finalize() Method
The finalize() method is called before an object is garbage collected.
Example of finalize()
class Car { Ummed Singh
@Override
protected void finalize() throws Throwable {
[Link]("Car object is destroyed");
public class ObjectFinalizeExample {
public static void main(String[] args) {
Car car = new Car();
car = null;
[Link](); // Suggest garbage collection
instanceof Operator in Java
The instanceof operator is a binary operator in Java used to test whether an object is an
instance of a specific class or a subclass of that class. It is used to check type
compatibility at runtime.
Syntax:
object instanceof ClassName
● Returns true → If the object is an instance of the specified class or its subclass.
● Returns false → If the object is not an instance of the specified class.
Example of instanceof Operator:
class Animal1 {} Ummed Singh
class Dog extends Animal1 {}
public class InstanceofExample {
public static void main(String[] args) {
Dog dog = new Dog();
[Link](dog instanceof Dog); // true
[Link](dog instanceof Animal1); // true
[Link](dog instanceof Object); // true
}
}
✅ instanceof confirms that dog is an instance of Dog, Animal, and Object.
Use instanceof Operator
● Avoid ClassCastException → Prevents invalid type casting.
● Supports Runtime Type Checking → Useful when dealing with polymorphism.
● Ensures Safe Downcasting → Checks before performing a downcast.
Final Keyword
The final keyword in Java is used to apply restrictions on variables, methods, and classes. It
ensures that the variable’s value cannot be changed, the method cannot be overridden, and the
class cannot be inherited.
1. Final Variable
A final variable in Java is a constant; once assigned, its value cannot be changed.
Syntax:
final int MAX_USERS = 100;
Real-World Example: Database Connection URL
In a web application, we often define database connection details as constants since they
should not be modified.
public class DatabaseConfig { Ummed Singh
public static final String DB_URL =
"jdbc:mysql://localhost:3306/mydb";
public static final String USERNAME = "root";
public static final String PASSWORD = "password";
Here, DB_URL, USERNAME, and PASSWORD should remain unchanged throughout the
application.
2. Final Method
A final method cannot be overridden by subclasses. This is useful when we want to prevent
changes to the core logic of a method.
Syntax:
class Parent { Ummed Singh
public final void display() {
[Link]("This is a final method.");
class Child extends Parent {
// Cannot override display() method
3. Final Class
A final class cannot be inherited. This is useful for security and utility classes where
modification is undesirable.
Syntax:
final class Utility { Ummed Singh
public static void printMessage(String msg) {
[Link](msg);
class ExtendedUtility extends Utility { } // This would cause an
error
Real-World Example: Java’s Built-in String Class
In Java, the String class is final to prevent subclassing and potential security risks.
public final class String { Ummed Singh
// Internal implementation
If String were not final, anyone could extend it and modify its behavior, which could lead to
security vulnerabilities.
Use case of final in Software Development
● final variables: For constants like database URLs, API keys, or mathematical
constants (PI = 3.14159).
● final methods: When you want to ensure that core logic (e.g., logging, authentication)
remains unchanged.
● final classes: For utility or security-sensitive classes (e.g., String, Math).
Abstract class and Interface
Abstraction in Java
Abstraction is one of the core concepts of Object-Oriented Programming (OOP) in Java. It is the
process of hiding implementation details and exposing only the necessary parts to the
user. Abstraction helps in reducing complexity and increasing code maintainability.
Java achieves abstraction using:
1. Abstract Classes
2. Interfaces
1. Abstract Classes
An abstract class in Java is a class that cannot be instantiated. It can contain both abstract
methods (methods without implementation) and concrete methods (methods with
implementation).
Example of Abstract Class
abstract class Vehicle { Ummed Singh
abstract void start(); // Abstract method (no implementation)
void stop() { // Concrete method (with implementation)
[Link]("Vehicle is stopping.");
}
}
class Car extends Vehicle {
@Override
void start() {
[Link]("Car is starting with a key.");
}
}
class Bike extends Vehicle {
@Override
void start() {
[Link]("Bike is starting with a self-start
button.");
}
}
public class AbstractionExample {
public static void main(String[] args) {
Vehicle myCar = new Car();
[Link](); // Outputs: Car is starting with a key.
[Link](); // Outputs: Vehicle is stopping.
Vehicle myBike = new Bike();
[Link](); // Outputs: Bike is starting with a
self-start button.
}
}
2. Interfaces
An interface in Java is a completely abstract class that only contains abstract methods (before
Java 8). From Java 8 onwards, interfaces can also have default and static methods.
Example of Interface
interface Payment { Ummed Singh
void makePayment(); // Abstract method
}
class CreditCardPayment implements Payment {
@Override
public void makePayment() {
[Link]("Payment made using Credit Card.");
}
}
class PayPalPayment implements Payment {
@Override
public void makePayment() {
[Link]("Payment made using PayPal.");
}
}
public class InterfaceExample {
public static void main(String[] args) {
Payment payment1 = new CreditCardPayment();
[Link](); // Outputs: Payment made using
Credit Card.
Payment payment2 = new PayPalPayment();
[Link](); // Outputs: Payment made using
PayPal.
}
}
Real-World Software Example: Banking System
Scenario: Online Payment System
Imagine an Online Banking System where different payment methods (Credit Card, PayPal,
UPI) are available. Instead of implementing separate payment logic in multiple places, we define
an interface (Abstraction) called PaymentGateway. Any new payment method can implement
this interface without affecting existing code.
Implementation:
// Abstracting the payment system using an interface
interface PaymentGateway {
void processPayment(double amount);
}
// Concrete implementations
class CreditCard implements PaymentGateway {
@Override
public void processPayment(double amount) {
[Link]("Processing Credit Card Payment of $"
+ amount);
}
}
class PayPal implements PaymentGateway {
@Override
public void processPayment(double amount) {
[Link]("Processing PayPal Payment of $" +
amount);
}
}
class UPI implements PaymentGateway {
@Override
public void processPayment(double amount) {
[Link]("Processing UPI Payment of $" +
amount);
}
}
// Main Class
public class PaymentSystem {
public static void main(String[] args) { Ummed Singh
PaymentGateway payment1 = new CreditCard();
[Link](250.00);
PaymentGateway payment2 = new PayPal();
[Link](100.50);
PaymentGateway payment3 = new UPI();
[Link](50.00);
}
}
Another Example:
abstract class PaymentPoly {
double amount; Ummed Singh
PaymentPoly(double amount) {
[Link] = amount;
}
abstract void processPayment(); // Abstract method to be
implemented by subclasses
// Child class: CreditCardPayment
class CreditCardPaymentPoly extends PaymentPoly {
String cardNumber;
CreditCardPaymentPoly(double amount, String cardNumber) {
super(amount);
[Link] = cardNumber;
}
@Override
void processPayment() {
[Link]("Processing Credit Card Payment of $"
+ amount + " using card: " + cardNumber);
}
}
// Child class: UPIPayment
class UPIPaymentPoly extends PaymentPoly {
String upiId;
UPIPaymentPoly(double amount, String upiId) {
super(amount);
[Link] = upiId;
}
@Override
void processPayment() {
[Link]("Processing UPI Payment of $" + amount
+ " using UPI ID: " + upiId);
}
}
// Main class to test the implementation
public class ECommercePaymentSystemPoly extends Object {
public static void main(String[] args) {
// Using a list of Payment objects instead of separate
object creation
List<PaymentPoly> payments = new ArrayList<>();
[Link](new CreditCardPaymentPoly(100.0,
"1234-5678-9876-5432"));
[Link](new UPIPaymentPoly(50.0, "ummed@upi"));
// Processing all payments dynamically using a loop
for (PaymentPoly payment : payments) {
[Link]();
}
}
}
Advantages of Abstraction in This Example
1. Code Maintainability: New payment methods can be added without modifying existing
code.
2. Scalability: If a new payment method like "Crypto Payment" is introduced, we can
create a new class implementing PaymentGateway without changing existing classes.
3. Security: The internal logic of each payment method is hidden from the users,
preventing unauthorized changes.
● Abstraction in Java allows us to define a blueprint and hide unnecessary details.
● Abstract classes allow partial abstraction, while interfaces provide full abstraction.
● In real-world applications like banking, e-commerce, and authentication systems,
abstraction helps improve modularity and maintainability.
Difference Between Abstract Class and Interface
Both Abstract Classes and Interfaces are used to achieve abstraction, but they have key
differences in how they work and when they should be used.
Feature Abstract Class Interface
Definition A class that cannot be A completely abstract type that only
instantiated and may contain defines method signatures (until
abstract and concrete methods. Java 8) and allows multiple
implementations.
Method Types Can have both abstract methods Can have only abstract methods
(without body) and concrete (before Java 8). From Java 8, can
methods (with body). have default and static methods.
Method Can provide partial Cannot provide method
Implementation implementation. implementation (except default/static
methods in Java 8+).
Access Modifiers Methods can have any access Methods are public by default.
modifier (public, private,
protected, default).
Variable Types Can have instance variables Only public, static, final constants
(fields) with any access modifier. (no instance variables).
Constructors Can have constructors. Cannot have constructors.
Multiple Supports single inheritance only Supports multiple inheritance (a
Inheritance (a class can extend only one class can implement multiple
abstract class). interfaces).
Use Case Used when classes share Used when different classes need to
common behavior, but require follow the same contract but may
some methods to be implemented have completely different
by subclasses. implementations.
Static and Default Methods in Abstraction (Java 8 and Above)
In Java, abstraction is mainly achieved using abstract classes and interfaces. However,
before Java 8, interfaces could only have abstract methods (methods without implementations).
Java 8 introduced default and static methods in interfaces to enhance flexibility and backward
compatibility.
1. Static Methods in Interfaces
A static method in an interface belongs to the interface itself, not to the instances of the
implementing classes. These methods cannot be overridden by implementing classes.
Why Are Static Methods Introduced?
● To avoid utility/helper method duplication across implementing classes.
● To provide utility functions related to an interface without requiring an implementing
class.
Example
Let's say we have an E-commerce application where multiple payment gateways (HDFC, SBI
card, etc.) are integrated. Each payment gateway implements a common
PaymentGatewayDemo interface. A utility method for validating credit card numbers can be
implemented as a static method inside the interface.
interface PaymentGatewayDemo { Ummed Singh
void processPayment(double amount);
// Static method for card validation
static boolean validateCard(String cardNumber) {
return [Link]("\\d{16}"); // Checks if card
number is 16 digits
}
}
class HDFC implements PaymentGateway {
@Override
public void processPayment(double amount) {
[Link]("Processing payment through HDFC: $" +
amount);
}
}
class SBI implements PaymentGateway {
@Override
public void processPayment(double amount) {
[Link]("Processing payment through SBI: $" +
amount);
}
}
// Testing static method
public class PaymentTestStatic {
public static void main(String[] args) {
boolean isValid =
[Link]("1234567812345678");
[Link]("Is card valid? " + isValid);
}
}
📌 Key Benefit: The validateCard() method can be used directly without needing an
instance of PaymentGatewayDemo, preventing duplicate validation logic in multiple classes.
2. Default Methods in Interfaces
A default method provides a default implementation inside an interface. It allows new methods
to be added to interfaces without breaking existing implementations.
Why Are Default Methods Introduced?
● To extend interfaces without breaking existing classes that implement them.
● To provide common behavior that can be overridden when necessary.
Example
Consider an E-commerce Discount System where different vendors apply different discount
rules. Instead of forcing every vendor to implement a discount calculation method, we can
provide a default discount calculation in the interface and allow vendors to override it when
needed.
interface DiscountService { Ummed Singh
// Default method providing a common discount calculation
default double calculateDiscount(double price) {
return price * 0.10; // Default 10% discount
}
}
class Pnb implements DiscountService {
// Uses default discount calculation
}
class Icici implements DiscountService {
@Override
public double calculateDiscount(double price) {
return price * 0.15; // Walmart gives a 15% discount
}
}
public class DiscountTestDefault {
public static void main(String[] args) {
DiscountService pnb = new Pnb();
DiscountService icici = new Icici();
[Link]("PNB Discounted Price: $" +
[Link](100));
[Link]("ICICI Discounted Price: $" +
[Link](100));
}
}
📌 Key Benefit:
● Pnb uses the default 10% discount without extra code.
● Icici overrides the method to apply a different discount.