Object-Oriented Programming (OOP)

Object-Oriented programming (OOP) is a programming paradigm based on the concept of “objects,” which contain data in the form of attributes (fields, properties) and behavior in the form of methods (functions). OOP is designed to improve code reusability, modularity, and scalability.

Key Concepts of OOP

  1. Class – A blueprint for creating objects. It defines attributes and methods.
  2. Object – An instance of a class.
  3. Encapsulation – Restricting direct access to some of an object’s data and methods.
  4. Abstraction – Hiding complex implementation details and showing only the essential features.
  5. Inheritance – A mechanism for creating a new class from an existing class.
  6. Polymorphism – The ability to process objects differently based on their data type or class.

Example in Python

# Defining a class
class Car:
    def __init__(self, brand, model, year):
        self.brand = brand  # Attribute
        self.model = model
        self.year = year

    def display_info(self):  # Method
        return f"{self.year} {self.brand} {self.model}"

# Creating an object (Instance of Car class)
car1 = Car("Toyota", "Camry", 2022)
print(car1.display_info())  # Output: 2022 Toyota Camry

Key Features of OOP

  • Modularity: Code is organized into smaller, reusable parts.
  • Code Reusability: Inheritance allows sharing code between classes.
  • Scalability: Easy to extend and modify.Security:
  • Encapsulation restricts direct access to data.

Encapsulation in Object-Oriented Programming (OOP)

Encapsulation is one of the fundamental principles of OOP that restricts direct access to an object’s data and allows controlled access through getter and setter methods. It helps in data hiding and protects the integrity of an object’s internal state.

Key Features of Encapsulation:

  1. Data Hiding – Fields (variables) are declared as private, meaning they cannot be accessed directly from outside the class.
  2. Controlled Access – Public getter and setter methods provide controlled access to private fields.
  3. Improves Security – Prevents unauthorized modifications to sensitive data.
  4. Enhances Maintainability – Easier to modify the implementation without affecting other parts of the code.

Encapsulation Example in Java

// Encapsulation Example
class Person {
    // Private fields (data hiding)
    private String name;
    private int age;

    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter method for name (Provides read access)
    public String getName() {
        return name;
    }

    // Setter method for name (Provides write access)
    public void setName(String name) {
        this.name = name;
    }

    // Getter method for age
    public int getAge() {
        return age;
    }

    // Setter method for age with validation
    public void setAge(int age) {
        if (age > 0) {  // Validation: Age must be positive
            this.age = age;
        } else {
            System.out.println("Invalid age! Age must be positive.");
        }
    }

    // Display method
    public void displayInfo() {
        System.out.println("Name: " + name + ", Age: " + age);
    }
}

// Main class to test encapsulation
public class EncapsulationExample {
    public static void main(String[] args) {
        // Creating an object of Person class
        Person person1 = new Person("Alice", 25);

        // Accessing data through methods (Encapsulation in action)
        person1.displayInfo();  // Output: Name: Alice, Age: 25

        // Modifying values using setter methods
        person1.setAge(30);
        person1.setName("Bob");

        // Getting updated values using getter methods
        System.out.println("Updated Name: " + person1.getName());  // Output: Bob
        System.out.println("Updated Age: " + person1.getAge());    // Output: 30

        // Trying to set an invalid age
        person1.setAge(-5);  // Output: Invalid age! Age must be positive.
    }
}

Explanation of Code:

  1. Private Fields:
    • name and age are private to prevent direct modification.
  2. Getter and Setter Methods:
    • getName() and getAge() allow reading values.
    • setName(String name) and setAge(int age) allow modifying values.
    • The setAge(int age) method includes validation to prevent negative age.
  3. Data Access Control:
    • Values can only be changed through setter methods, ensuring security and integrity.

Benefits of Encapsulation

Better Data Protection: Prevents accidental modification of data.
Improved Code Maintenance: Easy to update implementation without affecting external code.
Enhanced Flexibility: Allows adding validation or additional logic inside setter methods.
Encourages OOP Best Practices: Supports modular design and reusability.

What is a Constructor?

A constructor is a special method used to initialize objects. It is automatically called when an object of a class is created. The constructor’s main job is to set initial values for the object’s attributes.


Key Features of a Constructor

  1. Same Name as Class: A constructor must have the same name as the class.
  2. No Return Type: Unlike regular methods, constructors do not have a return type (not even void).
  3. Called Automatically: It is invoked when an object is created.
  4. Can Be Overloaded: Multiple constructors can be defined with different parameters.

Types of Constructors in Java

Java has three types of constructors:

  1. Default Constructor (No-Argument Constructor)
  2. Parameterized Constructor
  3. Copy Constructor (Not built-in but can be implemented manually)

1. Default Constructor (No-Argument Constructor)

A default constructor does not take any parameters and initializes fields with default values.

Example of a Default Constructor:
Types of Constructors in Java
Java has three types of constructors:

Default Constructor (No-Argument Constructor)
Parameterized Constructor
Copy Constructor (Not built-in but can be implemented manually)
1. Default Constructor (No-Argument Constructor)
A default constructor does not take any parameters and initializes fields with default values.

Example of a Default Constructor:
java
Copy
Edit
class Car {
    String brand;
    int year;

    // Default Constructor (No parameters)
    public Car() {
        this.brand = "Toyota";
        this.year = 2023;
    }

    // Method to display car details
    public void display() {
        System.out.println("Brand: " + brand + ", Year: " + year);
    }
}

public class ConstructorExample {
    public static void main(String[] args) {
        // Creating an object (Constructor is automatically called)
        Car myCar = new Car();
        myCar.display();  // Output: Brand: Toyota, Year: 2023
    }
}
Explanation:
The constructor Car() is called automatically when new Car() is executed.
It initializes brand to "Toyota" and year to 2023.
2. Parameterized Constructor
A parameterized constructor allows passing arguments while creating an object to assign specific values.

Example of a Parameterized Constructor:
java
Copy
Edit
class Car {
    String brand;
    int year;

    // Parameterized Constructor
    public Car(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }

    public void display() {
        System.out.println("Brand: " + brand + ", Year: " + year);
    }
}

public class ConstructorExample {
    public static void main(String[] args) {
        // Creating objects with different values
        Car car1 = new Car("Honda", 2020);
        Car car2 = new Car("BMW", 2022);

        car1.display();  // Output: Brand: Honda, Year: 2020
        car2.display();  // Output: Brand: BMW, Year: 2022
    }
}
Explanation:
The constructor Car(String brand, int year) accepts parameters.
When new Car("Honda", 2020) is called, "Honda" and 2020 are assigned to the object.
Explanation:
  • The constructor Car() is called automatically when new Car() is executed.
  • It initializes brand to "Toyota" and year to 2023.

2. Parameterized Constructor

A parameterized constructor allows passing arguments while creating an object to assign specific values.

Example of a Parameterized Constructor:
class Car {
    String brand;
    int year;

    // Parameterized Constructor
    public Car(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }

    public void display() {
        System.out.println("Brand: " + brand + ", Year: " + year);
    }
}

public class ConstructorExample {
    public static void main(String[] args) {
        // Creating objects with different values
        Car car1 = new Car("Honda", 2020);
        Car car2 = new Car("BMW", 2022);

        car1.display();  // Output: Brand: Honda, Year: 2020
        car2.display();  // Output: Brand: BMW, Year: 2022
    }
}
Explanation:
  • The constructor Car(String brand, int year) accepts parameters.
  • When new Car("Honda", 2020) is called, "Honda" and 2020 are assigned to the object.

3. Copy Constructor

A copy constructor creates a new object by copying values from an existing object.

Example of a Copy Constructor:
class Car {
    String brand;
    int year;

    // Parameterized Constructor
    public Car(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }

    // Copy Constructor
    public Car(Car otherCar) {
        this.brand = otherCar.brand;
        this.year = otherCar.year;
    }

    public void display() {
        System.out.println("Brand: " + brand + ", Year: " + year);
    }
}

public class CopyConstructorExample {
    public static void main(String[] args) {
        Car car1 = new Car("Ford", 2019);  // Original object
        Car car2 = new Car(car1);  // Copy object using copy constructor

        car1.display();  // Output: Brand: Ford, Year: 2019
        car2.display();  // Output: Brand: Ford, Year: 2019
    }
}
Explanation:
  • The copy constructor Car(Car otherCar) takes another object and copies its values.
  • Car car2 = new Car(car1); creates a duplicate of car1.

Constructor Overloading

You can have multiple constructors in the same class with different numbers or types of parameters.

Example:
class Car {
    String brand;
    int year;

    // Default Constructor
    public Car() {
        this.brand = "Unknown";
        this.year = 0;
    }

    // Parameterized Constructor
    public Car(String brand) {
        this.brand = brand;
        this.year = 2023;  // Default year
    }

    // Another Parameterized Constructor
    public Car(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }

    public void display() {
        System.out.println("Brand: " + brand + ", Year: " + year);
    }
}

public class ConstructorOverloadingExample {
    public static void main(String[] args) {
        Car car1 = new Car();  // Calls default constructor
        Car car2 = new Car("Nissan");  // Calls constructor with one parameter
        Car car3 = new Car("Audi", 2021);  // Calls constructor with two parameters

        car1.display();  // Output: Brand: Unknown, Year: 0
        car2.display();  // Output: Brand: Nissan, Year: 2023
        car3.display();  // Output: Brand: Audi, Year: 2021
    }
}
Explanation:
  • Three constructors exist in the Car class.
  • Depending on the number of arguments, the appropriate constructor is called.

Key Takeaways

Constructors initialize objects automatically.
They must have the same name as the class and no return type.
Default constructors set predefined values.
Parameterized constructors allow dynamic initialization.
Copy constructors duplicate objects.
Constructor overloading enables multiple ways to initialize an object.

Abstraction (OOP Concept)

Definition:

Abstraction is an object-oriented programming (OOP) principle that is used to hide implementation details from the user and only expose the essential features of an object.

It allows developers to focus on what an object does rather than how it does it.

Key Features of Abstraction:

  1. Hides Implementation Details – The internal working of the class is hidden, and only relevant functionality is exposed.
  2. Achieved Using Abstract Classes or Interfaces – Java provides abstract classes and interfaces to implement abstraction.
  3. Enhances Code Flexibility – Users interact with objects without needing to know their inner workings.
  4. Improves Maintainability – Reduces dependencies on specific implementations, making future modifications easier.

How to Achieve Abstraction in Java?

Java provides two ways to achieve abstraction:

  1. Abstract Classes (abstract keyword)
  2. Interfaces (interface keyword)

1. Abstraction Using Abstract Classes

An abstract class is a class that cannot be instantiated and may contain abstract methods (methods without implementation) as well as concrete methods (methods with implementation).

Example of an Abstract Class in Java
// Abstract class
abstract class Vehicle {
    // Abstract method (No implementation)
    abstract void start();

    // Concrete method (Has implementation)
    public void stop() {
        System.out.println("Vehicle is stopping...");
    }
}

// Concrete subclass
class Car extends Vehicle {
    // Implementing the abstract method
    @Override
    void start() {
        System.out.println("Car is starting with a key...");
    }
}

// Main class
public class AbstractionExample {
    public static void main(String[] args) {
        // Vehicle v = new Vehicle();  // ERROR: Cannot instantiate an abstract class
        Car myCar = new Car();
        myCar.start();  // Output: Car is starting with a key...
        myCar.stop();   // Output: Vehicle is stopping...
    }
}
Explanation:
  1. Vehicle is an abstract class because it contains an abstract method start().
  2. The Car class extends Vehicle and implements the start() method.
  3. Abstract classes cannot be instantiated, meaning you cannot create an object of Vehicle.
  4. Car inherits stop() from Vehicle, demonstrating partial abstraction.

Abstraction Level: 50%-100% (Abstract classes can have both abstract and concrete methods.)


2. Abstraction Using Interfaces

An interface is a completely abstract type in Java, containing only abstract methods (until Java 8, when default methods were introduced). It provides 100% abstraction.

Example of an Interface in Java
// Interface
interface Animal {
    // Abstract method (No implementation)
    void makeSound();

    // Default method (Has implementation, allowed from Java 8+)
    default void sleep() {
        System.out.println("Animal is sleeping...");
    }
}

// Concrete class implementing the interface
class Dog implements Animal {
    // Implementing the abstract method
    @Override
    public void makeSound() {
        System.out.println("Dog barks: Woof woof!");
    }
}

// Main class
public class InterfaceExample {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        myDog.makeSound(); // Output: Dog barks: Woof woof!
        myDog.sleep();     // Output: Animal is sleeping...
    }
}
Explanation:
  1. Animal is an interface, meaning all methods declared inside it are abstract by default.
  2. The Dog class implements the Animal interface and must override the makeSound() method.
  3. Interfaces achieve 100% abstraction because they cannot have concrete methods (except default methods from Java 8).

Abstraction Level: 100% (Interfaces only contain method declarations, ensuring full abstraction.)


Difference Between Abstract Class & Interface

FeatureAbstract ClassInterface
Abstraction Level0-100% (can have both abstract and concrete methods)100% (until Java 8, after which default methods are allowed)
InstantiationCannot be instantiatedCannot be instantiated
ConstructorsCan have constructorsCannot have constructors
Method ImplementationCan have concrete methodsMethods are abstract by default (unless default/static methods from Java 8)
Access ModifiersCan have private, protected, public methodsMethods are public by default
Multiple InheritanceNot supported (a class can extend only one abstract class)Supported (a class can implement multiple interfaces)

Real-World Example of Abstraction

Consider an ATM Machine:

  • You insert your card, enter a PIN, and withdraw cash.
  • You don’t know how the ATM processes transactions internally (hidden implementation).
  • You only see the user interface (essential features like withdraw, check balance, deposit).

This is abstraction in real life!


Key Takeaways

Hides internal details and exposes only essential features.
✅ Achieved using abstract classes and interfaces.
Abstract classes allow partial abstraction (can have both abstract & concrete methods).
Interfaces provide 100% abstraction (only method declarations).
Improves security, flexibility, and maintainability of code.

Polymorphism(OOP Concept)

Definition:

Polymorphism refers to the ability of an object to take multiple forms. It allows a single interface to be used for different data types, methods, or objects.

The word Polymorphism is derived from Greek:

  • “Poly” = many
  • “Morph” = forms

This means one entity (method, class, or interface) behaves differently in different scenarios.


Types of Polymorphism in Java

  1. Compile-time Polymorphism (Method Overloading)
  2. Run-time Polymorphism (Method Overriding)

1. Compile-time Polymorphism (Method Overloading)

Compile-time polymorphism, also known as method overloading, occurs when multiple methods in the same class have the same name but different parameter lists (different number or type of parameters).

Example: Method Overloading in Java
class Calculator {
    // Method to add two integers
    int add(int a, int b) {
        return a + b;
    }

    // Overloaded method: Adding three integers
    int add(int a, int b, int c) {
        return a + b + c;
    }

    // Overloaded method: Adding two double values
    double add(double a, double b) {
        return a + b;
    }
}

public class MethodOverloadingExample {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 10));         // Output: 15
        System.out.println(calc.add(5, 10, 20));     // Output: 35
        System.out.println(calc.add(5.5, 2.2));      // Output: 7.7
    }
}
Key Points:

Methods have the same name but different parameters (overloaded).
Java determines which method to call based on the arguments.
Happens at compile-time (method binding is done by the compiler).


2. Run-time Polymorphism (Method Overriding)

Run-time polymorphism, also known as method overriding, occurs when a subclass provides a specific implementation of a method that is already defined in its parent class.

Example: Method Overriding in Java
// Parent class
class Animal {
    // Method to be overridden
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

// Child class
class Dog extends Animal {
    // Overriding the method from Animal class
    @Override
    void makeSound() {
        System.out.println("Dog barks: Woof woof!");
    }
}

// Child class
class Cat extends Animal {
    // Overriding the method from Animal class
    @Override
    void makeSound() {
        System.out.println("Cat meows: Meow meow!");
    }
}

// Main class
public class MethodOverridingExample {
    public static void main(String[] args) {
        Animal myAnimal;   // Parent class reference

        myAnimal = new Dog();
        myAnimal.makeSound(); // Output: Dog barks: Woof woof!

        myAnimal = new Cat();
        myAnimal.makeSound(); // Output: Cat meows: Meow meow!
    }
}
Key Points:

The child class overrides the parent class method.
Method signature (name & parameters) must be the same.
Java determines the method to call at runtime.
Happens at runtime (dynamic method dispatch).


Polymorphism Using Interfaces

Polymorphism also applies to interfaces, where different classes can implement the same interface differently.

Example: Polymorphism with Interfaces

// Interface
interface Vehicle {
    void start();  // Abstract method
}

// Class implementing Vehicle
class Car implements Vehicle {
    public void start() {
        System.out.println("Car starts with a key.");
    }
}

// Another class implementing Vehicle
class Bike implements Vehicle {
    public void start() {
        System.out.println("Bike starts with a kick.");
    }
}

// Main class
public class InterfacePolymorphism {
    public static void main(String[] args) {
        Vehicle myVehicle;

        myVehicle = new Car();
        myVehicle.start();  // Output: Car starts with a key.

        myVehicle = new Bike();
        myVehicle.start();  // Output: Bike starts with a kick.
    }
}

The same method (start()) is implemented differently in Car and Bike.
Java calls the correct method at runtime using dynamic binding.


Difference Between Method Overloading & Method Overriding

FeatureMethod OverloadingMethod Overriding
DefinitionMultiple methods in the same class with the same name but different parametersA subclass provides a different implementation of a method from the parent class
Binding TimeCompile-time (early binding)Runtime (late binding)
Method SignatureDifferent parametersSame method signature
Return TypeCan be differentMust be the same or a subtype
Access ModifierCan have any access modifierCannot reduce visibility of the overridden method

Real-world Example of Polymorphism

Imagine you have a payment system that processes different payment methods like credit cards, PayPal, and cryptocurrency. Each payment method has a different way of processing payments, but they all share a common interface.

// Abstract class for payment
abstract class Payment {
    abstract void pay(double amount);
}

// CreditCard Payment
class CreditCard extends Payment {
    void pay(double amount) {
        System.out.println("Paid $" + amount + " using Credit Card.");
    }
}

// PayPal Payment
class PayPal extends Payment {
    void pay(double amount) {
        System.out.println("Paid $" + amount + " using PayPal.");
    }
}

// Main class
public class PaymentExample {
    public static void main(String[] args) {
        Payment paymentMethod;

        paymentMethod = new CreditCard();
        paymentMethod.pay(100.0); // Output: Paid $100.0 using Credit Card.

        paymentMethod = new PayPal();
        paymentMethod.pay(200.0); // Output: Paid $200.0 using PayPal.
    }
}

✔ The same method (pay()) behaves differently based on the payment method used.
✔ This demonstrates runtime polymorphism through method overriding.


Key Takeaways

Polymorphism allows a single method, class, or interface to take multiple forms.
Achieved via method overloading (compile-time) and method overriding (runtime).
Improves code flexibility, maintainability, and reusability.
Allows developers to write more general and scalable code.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top