Skip to main content

The Four Pillars of OOP in Java: A Quick Overview 🧱

Updated 5 min readMarkdown
The Four Pillars of OOP in Java: A Quick Overview 🧱

Object-Oriented Programming (OOP) is the backbone of Java. Every class you write, every Spring bean you inject and every library you use is built on four ideas: encapsulation, inheritance, polymorphism and abstraction. This article walks through each pillar with a short example and the practical reason it matters in real projects.

1. Encapsulation 🔒

Encapsulation means keeping an object's data private and exposing behaviour through methods. Other code can't put the object into an invalid state because it never touches the fields directly.

public class BankAccount {
    private double balance;

    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Deposit must be positive");
        }
        balance += amount;
    }

    public double getBalance() {
        return balance;
    }
}

Why it matters:

  • Validation in one place: the rule "deposits must be positive" lives inside the class, not scattered across callers.
  • Freedom to change internals: you can switch double to BigDecimal later without breaking code that only calls deposit() and getBalance().
  • Safer APIs: in Spring Boot, entities and DTOs with private fields and controlled setters prevent accidental changes.

2. Inheritance 🧬

Inheritance lets a class reuse and extend another class using extends. The child class gets the parent's fields and methods and can add or override behaviour.

public class Employee {
    protected String name;

    public double monthlyPay() {
        return 0;
    }
}

public class FullTimeEmployee extends Employee {
    private double salary;

    @Override
    public double monthlyPay() {
        return salary / 12;
    }
}

Why it matters:

  • Reuse: shared fields and logic are written once in the parent.
  • "Is-a" relationships: a FullTimeEmployee is an Employee, so it can be used anywhere an Employee is expected.

A word of caution: deep inheritance trees become hard to change. Modern Java code often prefers composition (a class has another object) over inheritance, and uses inheritance only for true "is-a" relationships.

3. Polymorphism 🎭

Polymorphism means "many forms": the same method call behaves differently depending on the actual object. Java supports two kinds.

Compile-time polymorphism (method overloading): same method name, different parameters.

public int add(int a, int b) { return a + b; }
public double add(double a, double b) { return a + b; }

Runtime polymorphism (method overriding): a parent reference points to a child object, and Java calls the child's version at runtime.

List<Employee> team = List.of(new FullTimeEmployee(), new Contractor()); // Contractor also extends Employee
for (Employee e : team) {
    System.out.println(e.monthlyPay()); // each object uses its own monthlyPay()
}

Why it matters:

  • Code to the type, not the implementation: the loop above doesn't care what kind of employee it has.
  • Easy to extend: add a new Intern class and the loop keeps working without changes.
  • It powers Spring: when you inject a PaymentService interface, Spring hands you a concrete implementation at runtime. That's runtime polymorphism.

4. Abstraction 🧩

Abstraction hides how something works and shows only what it does. In Java you achieve it with abstract classes and interfaces.

public interface NotificationService {
    void send(String to, String message);
}

public class EmailNotificationService implements NotificationService {
    @Override
    public void send(String to, String message) {
        // SMTP details stay hidden here
    }
}

public class SmsNotificationService implements NotificationService {
    @Override
    public void send(String to, String message) {
        // SMS gateway details stay hidden here
    }
}

Interface vs abstract class, in short:

  • Interface: a contract. A class can implement many interfaces. Since Java 8, interfaces can also have default and static methods.
  • Abstract class: a partial implementation. It can hold state (fields) and constructors, but a class can extend only one.

Why it matters: callers depend on NotificationService, not on email or SMS details, so you can swap or add channels without touching business logic.

How the four pillars work together

Think of a typical Spring Boot service layer:

  • Abstraction: controllers depend on a UserService interface.
  • Polymorphism: Spring injects UserServiceImpl at runtime, and tests can inject a mock instead.
  • Encapsulation: the User entity keeps its fields private and validates changes.
  • Inheritance: a BaseEntity class can hold shared fields like id and createdAt for every entity.

Quick recap

Pillar One-line meaning Java tools
Encapsulation Protect data, expose behaviour private fields, getters and methods
Inheritance Reuse and extend a parent class extends, super, @Override
Polymorphism One call, many behaviours Overloading, overriding, interfaces
Abstraction Show what, hide how interface, abstract class

Conclusion

The four pillars aren't just interview theory. They decide how easy your code is to test, extend and maintain. Encapsulation keeps objects valid, inheritance reuses shared behaviour, polymorphism lets one piece of code handle many types, and abstraction lets you swap implementations without breaking callers. Master these, and frameworks like Spring Boot start to feel natural, because they're built on exactly these ideas. 🚀

No comments yet. Be the first to comment.