What Is Encapsulation in Java?
Encapsulation means hiding an object's internal data behind a controlled public interface. You make fields private and expose only the operations (methods) that callers are allowed to perform. This protects internal state from uncontrolled modification and makes the class easier to change later.
Without encapsulation (fragile)
public class BankAccount {
public double balance; // anyone can set this to -1_000_000
}
BankAccount acct = new BankAccount();
acct.balance = -999999; // no validation possible
With encapsulation (robust)
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
balance += amount;
}
public boolean withdraw(double amount) {
if (amount > balance) return false;
balance -= amount;
return true;
}
public double getBalance() { return balance; }
}
Now the balance can only change through controlled operations. The class guarantees it can never go negative — callers cannot bypass this.
Getters and setters
Getters (accessors) and setters (mutators) are the conventional way to provide controlled access to private fields:
public class Person {
private String name;
public String getName() { return name; }
public void setName(String name) {
if (name == null || name.isBlank()) throw new IllegalArgumentException();
this.name = name;
}
}
Note: not all fields need setters. Immutable objects have getters only and are often the better design.
Immutable encapsulation
The strongest form of encapsulation: make everything final and set it only in the constructor. No setter needed, no mutation possible — thread-safe by design:
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
public Point translate(int dx, int dy) {
return new Point(x + dx, y + dy); // returns a new object
}
}
Why encapsulation matters at scale
- Refactoring freedom: if
balanceis private, you can change it fromdoubletoBigDecimalwithout touching any caller. - Invariant enforcement: validation lives in one place, not scattered across every caller.
- Reduced coupling: callers depend on the public method signature, not the internal representation.
Records (Java 16+)
Records provide encapsulation with minimal boilerplate — fields are automatically private and final, and accessors are generated:
public record Point(int x, int y) {}
// Generated: private final int x, y; + accessors x(), y(); + equals, hashCode, toString