Encapsulation
BeginnerProtect object state with private fields and controlled access through validated getters and setters — the foundation of maintainable OOP design.
Overview
Encapsulation means bundling data (fields) and behaviour (methods) together and restricting direct access to the data. By making fields private and exposing them only through public methods, you control how state is read and modified. Callers don't need to know how data is stored — only what operations are available. This lets you change the internal representation at any time without breaking any code that uses your class.
Private Fields & Controlled Access
The simplest encapsulation pattern: declare all instance fields private, provide a public getter for read access, and a public setter with validation for write access. If a field should never change after construction, expose only the getter — making the object effectively immutable for that field.
Record classes (Java 16+) auto-generate a canonical constructor and getters without explicit boilerplate — see the Records topic.
public class Temperature {
private double celsius; // private — callers cannot access directly
public Temperature(double celsius) {
setCelsius(celsius); // reuse setter validation in constructor
}
// Getter
public double getCelsius() { return celsius; }
// Setter with validation
public void setCelsius(double celsius) {
if (celsius < -273.15)
throw new IllegalArgumentException("Below absolute zero: " + celsius);
this.celsius = celsius;
}
// Derived values — no extra field needed, calculated on demand
public double getFahrenheit() { return celsius * 9.0 / 5.0 + 32; }
public double getKelvin() { return celsius + 273.15; }
@Override
public String toString() {
return String.format("%.1f°C / %.1f°F / %.1fK", celsius, getFahrenheit(), getKelvin());
}
public static void main(String[] args) {
Temperature t = new Temperature(100);
System.out.println(t); // 100.0°C / 212.0°F / 373.2K
t.setCelsius(0);
System.out.println(t.getKelvin()); // 273.15
// t.celsius = -300; // compile error — field is private
}
}Immutable Objects
An immutable object's state cannot change after construction. Benefits: thread-safe without synchronisation, safe to share and cache, easy to reason about.
To make a class immutable: 1. Declare the class final (prevents subclassing that adds mutability) 2. Make all fields private and final 3. Initialise all fields in the constructor 4. Provide no setters 5. For mutable fields (arrays, collections), return defensive copies in getters
Java's String, Integer, LocalDate, and BigDecimal are immutable.
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
public final class ImmutablePerson { // final — no subclassing
private final String name; // final — assigned once
private final int age;
private final List<String> hobbies; // mutable reference — needs care
public ImmutablePerson(String name, int age, List<String> hobbies) {
this.name = name;
this.age = age;
// Defensive copy — we own the list, caller's changes don't affect us
this.hobbies = List.copyOf(hobbies); // Java 10+ unmodifiable copy
}
public String getName() { return name; }
public int getAge() { return age; }
// Return unmodifiable view — caller cannot mutate our list
public List<String> getHobbies() { return hobbies; }
// "Wither" pattern — returns a new object with one field changed
public ImmutablePerson withAge(int newAge) {
return new ImmutablePerson(name, newAge, hobbies);
}
public static void main(String[] args) {
List<String> h = new ArrayList<>(List.of("reading", "hiking"));
ImmutablePerson p = new ImmutablePerson("Alice", 30, h);
h.add("painting"); // modifying original list
System.out.println(p.getHobbies().size()); // 2 — defensive copy protects us
ImmutablePerson older = p.withAge(31);
System.out.println(p.getAge()); // 30 — original unchanged
System.out.println(older.getAge()); // 31
}
}Encapsulation in Practice — Design Rules
Beyond getters/setters, encapsulation is a design philosophy:
Tell, Don't Ask — instead of getting state and computing externally, push the logic into the class. Instead of if (account.getBalance() >= amount) account.setBalance(...), write account.withdraw(amount).
Minimal surface area — expose the smallest public API that solves the problem. Every public method is a commitment you must maintain forever.
Validate at the boundary — all validation belongs in setters and constructors, not scattered throughout the codebase.
// BAD — Tell, Don't Ask violation
class BadOrder {
public List<Item> items = new ArrayList<>(); // public mutable field!
public double total = 0;
}
// Caller must manage invariants manually:
// order.items.add(item);
// order.total += item.getPrice(); // easy to forget or get wrong
// GOOD — encapsulated, self-consistent
public class Order {
private final List<Item> items = new ArrayList<>();
private double total = 0;
// Behaviour method keeps the object in a valid state
public void addItem(Item item) {
if (item == null) throw new NullPointerException("item");
items.add(item);
total += item.getPrice();
}
public boolean removeItem(Item item) {
if (items.remove(item)) {
total -= item.getPrice();
return true;
}
return false;
}
// Read-only views — callers cannot mutate
public List<Item> getItems() { return Collections.unmodifiableList(items); }
public double getTotal() { return total; }
public int getItemCount() { return items.size(); }
}Interactive Visualization
Key Points to Remember
- Make fields private; expose state through public methods with validation
- Immutable classes: final class, final fields, no setters, defensive copies of mutable fields
- "Tell, Don't Ask" — push logic into the class instead of extracting state and computing outside
- Derived values (getFahrenheit from celsius) eliminate redundant fields and synchronisation bugs
- Every public method is a public API commitment — minimise the surface area
- Validate in the constructor and setters so the object is always in a valid state
Practice Encapsulation in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat is encapsulation and why is it important?
How do you make a class immutable in Java?
Why should mutable fields in an immutable class return defensive copies?
What is the "Tell, Don't Ask" principle?
What are the advantages of immutable objects in multithreaded applications?
Ask Aria about Encapsulation
Your personal AI tutor — ask anything about this concept