Object-Oriented Programming in Java
Object-Oriented Programming (OOP) is the foundation of Java. Everything in Java revolves around objects and classes, making it one of the most naturally OOP-friendly languages available.
Core Conceptsโ
1. Class & Objectโ
A class is a blueprint. An object is an instance of that blueprint.
public class Car {
String brand;
int year;
public Car(String brand, int year) {
this.brand = brand;
this.year = year;
}
public void drive() {
System.out.println(brand + " is driving!");
}
}
// Creating an object
Car myCar = new Car("Toyota", 2022);
myCar.drive(); // Toyota is driving!
2. Encapsulationโ
Encapsulation means bundling data (fields) and behavior (methods) together, while restricting direct access to internal state using access modifiers.
Hide the data, expose the behavior.
public class BankAccount {
private double balance; // hidden from outside
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
}
Access Modifiers:
| Modifier | Same Class | Same Package | Subclass | Everywhere |
|---|---|---|---|---|
private | โ | โ | โ | โ |
| (default) | โ | โ | โ | โ |
protected | โ | โ | โ | โ |
public | โ | โ | โ | โ |
Spring beans rely heavily on encapsulation. @Service, @Repository, and @Component classes expose only what's needed through public methods or interfaces.
3. Inheritanceโ
Inheritance allows a class to acquire properties and methods from a parent class using the extends keyword.
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void makeSound() {
System.out.println(name + " makes a sound.");
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name); // call parent constructor
}
@Override
public void makeSound() {
System.out.println(name + " barks!");
}
}
Animal dog = new Dog("Rex");
dog.makeSound(); // Rex barks!
Key rules:
- Java supports single inheritance only (one parent class)
- Use
superto access parent class members @Overrideannotation signals an intentional method override โ always use it!
Avoid deep inheritance chains (more than 2โ3 levels). They make code harder to understand and maintain. Prefer composition over inheritance when possible.
4. Polymorphismโ
Polymorphism means "many forms" โ the same interface can behave differently depending on the actual object.
Compile-time (Method Overloading)โ
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) { // same name, different params
return a + b;
}
}
Runtime (Method Overriding)โ
public class Shape {
public double area() {
return 0;
}
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
// Polymorphic behavior
List<Shape> shapes = List.of(new Circle(5), new Rectangle(4, 6));
shapes.forEach(s -> System.out.println(s.area()));
Polymorphism powers Spring's dependency injection. You program to an interface, and Spring injects the correct implementation at runtime.
5. Abstractionโ
Abstraction hides implementation details and exposes only the essential features. Java achieves this through abstract classes and interfaces.
Abstract Classโ
public abstract class Vehicle {
protected String model;
public Vehicle(String model) {
this.model = model;
}
public abstract void fuelUp(); // must be implemented by subclass
public void startEngine() { // shared behavior
System.out.println(model + " engine started.");
}
}
public class ElectricCar extends Vehicle {
public ElectricCar(String model) {
super(model);
}
@Override
public void fuelUp() {
System.out.println(model + " is charging...");
}
}
Interfaceโ
public interface Payable {
void processPayment(double amount); // implicitly public & abstract
default void printReceipt() { // default method (Java 8+)
System.out.println("Payment processed.");
}
}
public class CreditCardPayment implements Payable {
@Override
public void processPayment(double amount) {
System.out.println("Charging $" + amount + " to credit card.");
}
}
Abstract Class vs Interface:
| Feature | Abstract Class | Interface |
|---|---|---|
| Instantiation | โ Cannot | โ Cannot |
| Multiple inheritance | โ No | โ
Yes (implements A, B) |
| Constructor | โ Yes | โ No |
| Fields | Any type | public static final only |
| Methods | Abstract + concrete | Abstract + default/static |
Interfaces are everywhere in Spring. JpaRepository, ApplicationContext, BeanFactory โ you always code to the interface, letting Spring provide the implementation.
SOLID Principlesโ
These 5 principles guide writing clean, maintainable OOP code.
| Principle | Description |
|---|---|
| Single Responsibility | A class should have only one reason to change |
| Open/Closed | Open for extension, closed for modification |
| Liskov Substitution | Subclasses must be substitutable for their base class |
| Interface Segregation | Prefer small, specific interfaces over large, general ones |
| Dependency Inversion | Depend on abstractions, not concrete implementations |
// โ
Dependency Inversion in Spring
@Service
public class OrderService {
private final PaymentGateway paymentGateway; // interface, not implementation
public OrderService(PaymentGateway paymentGateway) { // injected by Spring
this.paymentGateway = paymentGateway;
}
}
Following SOLID principles naturally leads to better Spring application design โ especially Dependency Inversion, which is the backbone of Spring's IoC container.
Quick Referenceโ
OOP in Java
โโโ Encapsulation โ private fields + public getters/setters
โโโ Inheritance โ extends (single), super keyword
โโโ Polymorphism โ overloading (compile-time), overriding (runtime)
โโโ Abstraction โ abstract class, interface
Further Readingโ
Advanced Editorial Pass: OOP as Boundary Design, Not Boilerplateโ
Architectural Lensโ
- OOP value comes from explicit boundaries, invariants, and change isolation.
- Encapsulation should protect business rules, not merely hide fields.
- Composition strategy should reflect domain volatility and extension pressure.
Common Misuse Patternsโ
- Inheritance used for code reuse instead of semantic substitutability.
- Anemic models that move all behavior into service layers.
- Over-abstracted class hierarchies without real variation.
Senior Heuristicsโ
- Keep domain invariants close to the data they constrain.
- Prefer composition when behavior variation is runtime-driven.
- Audit object responsibilities regularly to prevent god objects.