Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects". These objects represent real-world entities and are instances of classes. OOP allows developers to structure software in a way that mimics real-world systems, making code more reusable, scalable, and maintainable.
There are four primary pillars of OOP:
1. Encapsulation
2. Abstraction
3. Inheritance
4. Polymorphism
Each pillar is crucial for designing robust and scalable software.
1. Encapsulation
Definition:
Encapsulation is the bundling of data (variables) and the methods (functions) that operate on the data into a single unit called a class. It also helps restrict access to some of the object’s components, which is known as data hiding.
Real-Time Use:
Consider a bank account system. We can define a class BankAccount where the balance is a private property. We expose only the necessary functionality, such as deposit and withdraw methods, to interact with the balance.
Code Example:
class BankAccount {
private double balance; // Encapsulated field
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
// Getter method (allows reading the balance)
public double getBalance() {
return balance;
}
// Setter method (allows modifying the balance)
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(1000);
account.deposit(500);
account.withdraw(300);
System.out.println("Current Balance: " + account.getBalance());
}
}
Explanation:
balance is private and cannot be accessed directly from outside the class.
The deposit and withdraw methods control how the balance can be modified.
2. Abstraction
Definition:
Abstraction is the concept of hiding the complex implementation details and showing only the essential features of the object. It helps in simplifying complex systems by providing a clear interface.
Real-Time Use:
Consider a Car class. While driving a car, you don't need to know how the engine works internally; you just need to know how to start the car, drive, and stop.
Code Example:
abstract class Vehicle {
// Abstract method (does not have a body)
public abstract void start();
// Regular method
public void stop() {
System.out.println("Vehicle stopped");
}
}
class Car extends Vehicle {
@Override
public void start() {
System.out.println("Car is starting with key ignition.");
}
}
class Bike extends Vehicle {
@Override
public void start() {
System.out.println("Bike is starting with a kickstart.");
}
}
public class Main {
public static void main(String[] args) {
Vehicle car = new Car();
car.start(); // Outputs: Car is starting with key ignition.
car.stop(); // Outputs: Vehicle stopped
Vehicle bike = new Bike();
bike.start(); // Outputs: Bike is starting with a kickstart.
bike.stop(); // Outputs: Vehicle stopped
}
}
Explanation:
The Vehicle class defines an abstract method start() that must be implemented by all subclasses, but does not provide the details of how it starts.
The Car and Bike classes provide their own specific implementations of start().
The stop() method is common across all vehicles, so it is implemented in the Vehicle class.
3. Inheritance
Definition:
Inheritance allows a class (child class) to inherit the properties and methods of another class (parent class). This helps in code reuse and establishing a relationship between the parent and child classes.
Real-Time Use:
Think of a class Employee which has general properties (e.g., name, salary). Now, we can create subclasses Manager and Developer that inherit from Employee but also have their own specific properties (e.g., teamSize for Manager).
Code Example:
class Employee {
String name;
double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public void work() {
System.out.println(name + " is working.");
}
}
class Manager extends Employee {
int teamSize;
public Manager(String name, double salary, int teamSize) {
super(name, salary); // Calling parent class constructor
this.teamSize = teamSize;
}
public void manage() {
System.out.println(name + " is managing a team of " + teamSize + " people.");
}
}
class Developer extends Employee {
String programmingLanguage;
public Developer(String name, double salary, String programmingLanguage) {
super(name, salary);
this.programmingLanguage = programmingLanguage;
}
public void code() {
System.out.println(name + " is coding in " + programmingLanguage);
}
}
public class Main {
public static void main(String[] args) {
Manager manager = new Manager("Alice", 90000, 5);
Developer developer = new Developer("Bob", 70000, "Java");
manager.work(); // Inherited method from Employee
manager.manage(); // Specific to Manager
developer.work(); // Inherited method from Employee
developer.code(); // Specific to Developer
}
}
Explanation:
Manager and Developer inherit the work() method from the Employee class.
Manager has additional functionality (manage()), and Developer has its own functionality (code()).
The super() keyword is used to call the parent class's constructor.
4. Polymorphism
Definition:
Polymorphism allows one interface to be used for different underlying forms. It allows methods to be used in different ways, and it can be categorized into method overloading (compile-time) and method overriding (runtime).
There are 2 types of polymorphism
1. compile time polymorphism
2. Run time polymorphism
Real-Time Use:
Consider the concept of Shape. We have a draw() method that is implemented differently for different shapes like Circle, Rectangle, etc. You can call the same method, but it will behave differently based on the shape.
1. Method Overriding (Runtime Polymorphism)
Code Example:
class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a Rectangle");
}
}
public class Main {
public static void main(String[] args) {
Shape shape1 = new Circle(); // Circle object
Shape shape2 = new Rectangle(); // Rectangle object
shape1.draw(); // Outputs: Drawing a Circle
shape2.draw(); // Outputs: Drawing a Rectangle
}
}
Explanation:
The draw() method is overridden in both Circle and Rectangle.
Even though we refer to them as Shape objects, the method that gets called depends on the actual object type (Circle or Rectangle).
2. Method Overloading (Compile-Time Polymorphism)
class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
System.out.println(calculator.add(5, 10)); // Outputs: 15
System.out.println(calculator.add(5.5, 10.5)); // Outputs: 16.0
}
}
Explanation:
The add() method is overloaded to handle both int and double parameters. This is a compile-time polymorphism, where the method called is determined at compile-time based on the argument types.
Conclusion:
Object-Oriented Programming (OOP) allows you to structure your code logically using real-world concepts. The four core pillars—Encapsulation, Abstraction, Inheritance, and Polymorphism—are the building blocks for creating scalable, maintainable, and flexible software.
1. Encapsulation : hides data and exposes only the necessary methods to interact with the data.
2. Abstraction : hides complex implementation details and shows only essential features.
3. Inheritance: allows the creation of new classes from existing ones.
4. Polymorphism: allows methods to behave differently based on the object that invokes them.
By mastering these principles, you can design robust, real-world systems that are both reusable and easy to maintain.
Comments
Post a Comment