The Four Pillars of OOP in Java
Object-Oriented Programming (OOP) is built on four fundamental pillars that help us write organized, reusable, and maintainable code:
- Encapsulation: Wrapping data and methods together, hiding internal details
- Inheritance: Allowing classes to inherit features from other classes
- Abstraction: Showing only essential features, hiding implementation
- Polymorphism: Same method name, different behaviors
1. Encapsulation
Encapsulation is the process of wrapping data and methods together into a single unit. It acts as a protective shield that prevents data from being accessed by code outside this shield.
Key Points:
- Variables inside a class are hidden from other classes
- Data can only be accessed through member functions
- Also known as "data hiding"
Real-Life Analogy: Building Security
A building has private rooms that can only be accessed through the main door with the right key. You can't directly enter secret rooms without proper authorization.
class Building {
// private data (hidden rooms)
private String secretRoom = "Hidden Archive";
private String officeRoom = "Manager Office";
// public method (main door)
public String enter(String key) {
if (key.equals("office")) {
return officeRoom;
} else if (key.equals("secret")) {
return "Access Denied! Secret room is protected.";
} else {
return "Invalid key!";
}
}
}
public class Main {
public static void main(String[] args) {
Building b = new Building();
// trying to enter rooms
System.out.println(b.enter("office")); // allowed
System.out.println(b.enter("secret")); // not allowed
}
}Output:
Manager OfficeAccess Denied! Secret room is protected.2. Inheritance
Inheritance is the mechanism by which one class is allowed to inherit the features (fields and methods) of another class. We achieve inheritance using the extends keyword.
Superclass (Parent)
The class that gives its features to another class
Subclass (Child)
The class that receives features from another class
✔ Reusability
Inheritance supports code reusability. When creating a new class, if an existing class includes some needed code, we can derive the new class from the existing one instead of writing everything from scratch.
// Parent class
class Building {
void showBuilding() {
System.out.println("This is a building with walls and doors.");
}
}
// Child class
class House extends Building {
void showHouse() {
System.out.println("This house has a kitchen and bedrooms.");
}
}
// Another child class
class Office extends Building {
void showOffice() {
System.out.println("This office has meeting rooms and workspaces.");
}
}
public class Main {
public static void main(String[] args) {
House h = new House();
h.showBuilding(); // inherited from Building
h.showHouse(); // its own feature
Office o = new Office();
o.showBuilding(); // inherited
o.showOffice(); // its own feature
}
}3. Abstraction
Abstraction means hiding the internal working details and showing only the important features to the user. It lets us focus on what an object does, not how it does it.
Implementation:
- Abstract classes
- Interfaces (can provide 100% abstraction)
Real-Life Analogy: Light Switch
When you flip a light switch, you don't need to know about the complex electrical wiring behind the wall. You just need to know that flipping the switch turns the light on or off.
// Abstract class (hides how the light works)
abstract class Building {
// abstract method: what the user can do
abstract void turnOnLights();
}
// Concrete class: shows WHAT it does, hides HOW it does it
class House extends Building {
@Override
void turnOnLights() {
System.out.println("Lights are turned on in the house.");
}
}
public class Main {
public static void main(String[] args) {
Building b = new House();
b.turnOnLights(); // user just uses the feature
}
}4. Polymorphism
Polymorphism means "many forms". It allows us to perform a single action in different ways. In Java, there are two main types of polymorphism:
Method Overloading
Compile-time polymorphism
Method Overriding
Runtime polymorphism
Method Overloading (Compile-Time Polymorphism)
Multiple methods share the same name but have different parameters in the same class. The compiler knows which method to call before running the program.
class Building {
// open door with a key
void openDoor(String key) {
System.out.println("Door opened with a key.");
}
// open door with a password
void openDoor(int password) {
System.out.println("Door opened with a password.");
}
// open door with a card
void openDoor(String card, int code) {
System.out.println("Door opened with a card and code.");
}
}
public class Main {
public static void main(String[] args) {
Building b = new Building();
b.openDoor("metalKey"); // chooses key method
b.openDoor(1234); // chooses password method
b.openDoor("accessCard", 99); // chooses card method
}
}Output:
Door opened with a key.Door opened with a password.Door opened with a card and code.Method Overriding (Runtime Polymorphism)
A child class provides its own implementation of a method that already exists in the parent class. The method has the same name, return type, and parameters. The Java compiler can't identify which method to call until the program runs, because objects are created at runtime.
// Parent class
class Building {
void openDoor() {
System.out.println("Opening the building door in a standard way.");
}
}
// Child class 1
class House extends Building {
@Override
void openDoor() {
System.out.println("Opening the house door with a key.");
}
}
// Child class 2
class Office extends Building {
@Override
void openDoor() {
System.out.println("Opening the office door with a password.");
}
}
public class Main {
public static void main(String[] args) {
Building b1 = new House();
Building b2 = new Office();
b1.openDoor(); // calls House version
b2.openDoor(); // calls Office version
}
}Output:
Opening the house door with a key.Opening the office door with a password.Advantages of OOP over Procedural Programming
✔ Reusability
Create reusable components using objects and classes, leading to less duplication
✔ Clear Structure
Provides logical organization, making code easier to understand and maintain
✔ DRY Principle
Don't Repeat Yourself - common functionalities in one place, reducing redundancy
✔ Faster Development
Reuse existing code and create modular components for quicker application development
Disadvantages of OOP
✘ Learning Curve
Concepts like classes, objects, and inheritance can be confusing for beginners
✘ Overhead for Small Programs
Using OOP for simple programs may require more code than necessary
✘ Debugging Complexity
Code divided into classes and layers can make finding and fixing bugs more time-consuming
✘ Memory Usage
Creates many objects, which can use more memory compared to procedural programming
