Encapsulation
Encapsulation is the first of the four OOP pillars and the one interviewers probe with "why make fields private?" A crisp answer โ bundling plus hiding, enforced through a controlled interface โ signals you understand not just syntax but design intent.
The core idea
Encapsulation means bundling data (fields) together with the methods that operate on that data into a single unit (a class), and restricting direct access to the internal state. Outsiders interact only through a public interface (methods), while the internals stay private. This gives you data hiding and control over how state changes.
Beginner โ access modifiers
Encapsulation is realized with access modifiers:
- private โ visible only inside the class.
- protected โ visible in the class and its subclasses.
- public โ visible everywhere.
The rule of thumb: make fields private, expose behavior through public methods. Reads/writes go through getters and setters (accessors/mutators), which can validate input.
Intermediate โ why hide state
Hiding internals lets a class enforce invariants. A BankAccount with a private balance can reject negative deposits in its setter; if balance were public, any code could corrupt it. Encapsulation also decouples callers from implementation: you can change how balance is stored (int, BigDecimal, a computed value) without touching any caller, because they only see the method signatures.
Advanced โ encapsulation vs abstraction, and good practice
Encapsulation ("how" โ hiding the internal state and wiring) is often confused with abstraction ("what" โ exposing only essential behavior); they're related but distinct. Good encapsulation means a minimal public surface, immutability where possible, and not leaking mutable internals (e.g., returning a copy of a collection, not the live reference โ otherwise callers can mutate your private state behind your back). Overusing trivial getters/setters that expose every field is a code smell that defeats the purpose โ prefer methods that express behavior ("deposit"), not raw field access.
Worked example
A BankAccount keeps balance private. Deposits and withdrawals go through methods that validate: deposit rejects negative amounts; withdraw rejects amounts over the balance. Because no external code can set balance directly, the class guarantees the balance is always valid โ the invariant "balance >= 0" is enforced in one place. Later you can add logging or currency conversion inside those methods without changing any caller.
When to use
Always encapsulate state that has rules or invariants, or that you might want to change later. Expose behavior, not raw data. This is foundational to virtually every well-designed class.
Tricks and mnemonic
Remember "Encapsulation = capsule: data + methods sealed together, accessed only through the label." Keep fields private; expose behavior, not fields.
Don't equate "add a getter and setter for every field" with encapsulation. Blindly exposing every field through accessors gives callers full read/write access โ barely better than public fields. True encapsulation exposes behavior and protects invariants.
- โ- Encapsulation bundles data + methods and hides internal state.
- โ- Access modifiers: private (class), protected (subclasses), public (all).
- โ- Make fields private; expose behavior via methods; validate in setters.
- โ- Benefits: enforce invariants, decouple callers from implementation.
- โ- Don't leak mutable internals; avoid mindless getter/setter pairs.
- โEncapsulation seals a class's data together with the methods that use it and hides the internals behind a controlled public interface. Keep fields private, expose behavior, validate through setters, and don't leak mutable state โ this protects invariants and lets you change internals freely.
Encapsulation โ Revision Notes
Fast revision of encapsulation.
- Encapsulation = bundle data + methods in a class and hide the internal state behind a public interface.
- Access modifiers: private (class only), protected (class + subclasses), public (everywhere).
- Rule: make fields private, expose behavior via methods; validate in setters.
- Benefits: enforce invariants (e.g., balance >= 0) and decouple callers from the implementation.
- Don't leak mutable internals (return copies of collections, not live references).
- Mindless getter/setter-for-every-field is a smell โ expose behavior, not raw fields.
- Encapsulation = "how it's hidden"; abstraction = "what is exposed".
Encapsulation โ Flashcards
Cover the answer, recall, then check. 8 cards.
Q1. What is encapsulation?
A1. Bundling data with the methods that operate on it and hiding the internal state behind a controlled interface.
Q2. Name the three common access modifiers.
A2. private (class only), protected (class + subclasses), public (everywhere).
Q3. What is the golden rule of encapsulation?
A3. Make fields private and expose behavior through public methods.
Q4. What can setters do that public fields cannot?
A4. Validate input and enforce invariants before changing state.
Q5. Give a benefit of encapsulation for maintenance.
A5. You can change the internal implementation without affecting callers who use only the public methods.
Q6. What does "leaking mutable internals" mean?
A6. Returning a live reference (e.g., a collection) that callers can mutate, bypassing your class's control.
Q7. Why is a getter/setter for every field not real encapsulation?
A7. It exposes full read/write access to all state, barely different from public fields.
Q8. Encapsulation vs abstraction in one line?
A8. Encapsulation hides how state is stored; abstraction exposes only what behavior matters.
Encapsulation โ Worked Example
Worked Example
Problem: Design a BankAccount so its balance can never go negative or be tampered with directly. Show what breaks without encapsulation.
Solution: Encapsulation = keep data private and expose only controlled methods that enforce the class's invariants.
class BankAccount {
private double balance = 0; // hidden state
public void deposit(double amt) {
if (amt > 0) balance += amt; // validated
}
public boolean withdraw(double amt) {
if (amt > 0 && amt <= balance) { // guard the invariant
balance -= amt; return true;
}
return false; // reject overdraft
}
public double getBalance() { return balance; }
}
Trace: deposit(1000) โ balance 1000. withdraw(1500) โ 1500 > 1000, rejected, balance stays 1000.
If balance were public, any code could write account.balance = -5000, silently corrupting the invariant. Making it private forces all changes through validated methods.
Answer: Private field + validated methods keep balance โฅ 0; a public field would allow corruption.
- โ- Encapsulation hides internal state behind a controlled public interface.
- โ- Getters/setters (or methods) enforce invariants that raw fields cannot.
- โ- It localises change: internals can be refactored without breaking callers.