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
- Class – A blueprint for creating objects. It defines attributes and methods.
- Object – An instance of a class.
- Encapsulation – Restricting direct access to some of an object’s data and methods.
- Abstraction – Hiding complex implementation details and showing only the essential features.
- Inheritance – A mechanism for creating a new class from an existing class.
- 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:
- Data Hiding – Fields (variables) are declared as private, meaning they cannot be accessed directly from outside the class.
- Controlled Access – Public getter and setter methods provide controlled access to private fields.
- Improves Security – Prevents unauthorized modifications to sensitive data.
- 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:
- Private Fields:
name
andage
are private to prevent direct modification.
- Getter and Setter Methods:
getName()
andgetAge()
allow reading values.setName(String name)
andsetAge(int age)
allow modifying values.- The
setAge(int age)
method includes validation to prevent negative age.
- 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
- Same Name as Class: A constructor must have the same name as the class.
- No Return Type: Unlike regular methods, constructors do not have a return type (not even
void
). - Called Automatically: It is invoked when an object is created.
- Can Be Overloaded: Multiple constructors can be defined with different parameters.
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:
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"
andyear
to2023
.
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"
and2020
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 ofcar1
.
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:
- Hides Implementation Details – The internal working of the class is hidden, and only relevant functionality is exposed.
- Achieved Using Abstract Classes or Interfaces – Java provides abstract classes and interfaces to implement abstraction.
- Enhances Code Flexibility – Users interact with objects without needing to know their inner workings.
- Improves Maintainability – Reduces dependencies on specific implementations, making future modifications easier.
How to Achieve Abstraction in Java?
Java provides two ways to achieve abstraction:
- Abstract Classes (
abstract
keyword) - 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:
Vehicle
is an abstract class because it contains an abstract methodstart()
.- The
Car
class extendsVehicle
and implements thestart()
method. - Abstract classes cannot be instantiated, meaning you cannot create an object of
Vehicle
. Car
inheritsstop()
fromVehicle
, 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:
Animal
is an interface, meaning all methods declared inside it are abstract by default.- The
Dog
class implements theAnimal
interface and must override themakeSound()
method. - 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
Feature | Abstract Class | Interface |
---|---|---|
Abstraction Level | 0-100% (can have both abstract and concrete methods) | 100% (until Java 8, after which default methods are allowed) |
Instantiation | Cannot be instantiated | Cannot be instantiated |
Constructors | Can have constructors | Cannot have constructors |
Method Implementation | Can have concrete methods | Methods are abstract by default (unless default/static methods from Java 8) |
Access Modifiers | Can have private , protected , public methods | Methods are public by default |
Multiple Inheritance | Not 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
- Compile-time Polymorphism (Method Overloading)
- 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
Feature | Method Overloading | Method Overriding |
---|---|---|
Definition | Multiple methods in the same class with the same name but different parameters | A subclass provides a different implementation of a method from the parent class |
Binding Time | Compile-time (early binding) | Runtime (late binding) |
Method Signature | Different parameters | Same method signature |
Return Type | Can be different | Must be the same or a subtype |
Access Modifier | Can have any access modifier | Cannot 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.