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
doubletoBigDecimallater without breaking code that only callsdeposit()andgetBalance(). - 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
FullTimeEmployeeis anEmployee, so it can be used anywhere anEmployeeis 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
Internclass and the loop keeps working without changes. - It powers Spring: when you inject a
PaymentServiceinterface, 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
defaultandstaticmethods. - 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
UserServiceinterface. - Polymorphism: Spring injects
UserServiceImplat runtime, and tests can inject a mock instead. - Encapsulation: the
Userentity keeps its fields private and validates changes. - Inheritance: a
BaseEntityclass can hold shared fields likeidandcreatedAtfor 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. 🚀
Comments
Join the discussionNo comments yet. Be the first to comment.