What is encapsulation, and how do access modifiers help achieve it?
Encapsulation means keeping an object's data and the methods that change it together, and hiding the data so outside code can only change it through controlled methods. In Java, access modifiers (private, default, protected, public) decide who can see each member, and making fields private with validated methods is the usual way to encapsulate.
Explain it simply
Think of an ATM. The cash is locked inside the machine. You cannot open the box and take out notes; you have to insert a card, enter a PIN and request an amount. The machine checks your balance before giving anything. The money (data) and the rules for touching it (methods) live together, and the only way in is through the screen.
That is encapsulation. A class hides its fields and offers methods that check every change, so the object can never end up in a nonsense state, like a bank balance of minus fifty thousand rupees.
Example
public class BankAccount {
private final String accountNumber; // hidden from outside
private double balance; // hidden from outside
public BankAccount(String accountNumber) {
this.accountNumber = accountNumber;
}
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
balance += amount;
}
public void withdraw(double amount) {
if (amount > balance) throw new IllegalStateException("Insufficient balance");
balance -= amount;
}
public double getBalance() { return balance; } // read-only access, no setter
public static void main(String[] args) {
BankAccount acc = new BankAccount("SB-1029");
acc.deposit(5000);
acc.withdraw(1200);
System.out.println(acc.getBalance()); // prints: 3800.0
// acc.balance = -50000; // compile error: balance has private access
}
}There is no setBalance method. The only ways to change the balance are deposit and withdraw, and both enforce the rules.
How it works
Java has four access levels:
| Modifier | Same class | Same package | Subclass in another package | Everywhere |
|---|---|---|---|---|
private | Yes | No | No | No |
| (default, no keyword) | Yes | Yes | No | No |
protected | Yes | Yes | Yes | No |
public | Yes | Yes | Yes | Yes |
A common pattern:
- Fields are
private. - Methods that other code needs are
public. - Helper methods used only inside the class are
private. - Getters are added only when needed, and setters only when a field should really change, with validation inside.
Real-world use
- A
ShoppingCartthat keeps its item list private and exposesaddItem,removeItemandtotal, so nobody can insert an item with a negative quantity. - An
Orderwhose status can move from PLACED to SHIPPED but never back, enforced inside the class. - Libraries keep internals private so they can change them in a new version without breaking your code.
Common mistakes
- Making fields private and then generating a public getter and setter for every one without any checks. That is just a public field with extra steps.
- Returning a private mutable list directly from a getter. The caller can then modify it. Return
List.copyOf(items)or an unmodifiable view. - Confusing encapsulation with abstraction. Encapsulation hides data and protects it; abstraction hides complexity behind a simple interface.
- Thinking
protectedmeans "only subclasses". In Java it also gives access to every class in the same package.
What to say in the interview
"Encapsulation is bundling data with the methods that operate on it and restricting direct access to the data. In Java I make fields private and expose public methods that validate every change, so the object can't be put into an invalid state. For example, a BankAccount has a private balance and only deposit and withdraw methods, no setter. Access modifiers control visibility: private is class only, default is package, protected is package plus subclasses, and public is everywhere."
Follow-up questions
- What is the difference between encapsulation and abstraction?
- Can a top-level class be
privateorprotected? - How do you make a class fully immutable?
- What is the default access modifier for members of an interface?
Go deeper
Read these once the basics above feel easy.
Getters can leak internals if they return mutable objects. If Student has private List<String> subjects and getSubjects() returns that list, any caller can clear it. Return a copy or an unmodifiable view, and copy incoming lists in the constructor too (a defensive copy). Java records, added in Java 16, give a quick way to write small encapsulated data classes: record Point(int x, int y) {} creates private final fields, a constructor and accessor methods, though you still need defensive copies for mutable components.