Encapsulation in Java
A medicine capsule keeps the powder inside a shell: you can swallow it, but you can't spill it or mix something else into it. Encapsulation does the same for an object's data. The fields are kept private, inside the object, and the only way in is through the object's public methods, which check every request. A bank account that lets anyone write balance = -5000 has no rules at all; an encapsulated one simply refuses.
The Problem with Public Fields
class BankAccount {
public String holder;
public long balance;
}
account.balance = -5000; // nothing stops this
account.holder = null; // or thisEvery line of code anywhere in the program can put the object into a nonsense state, and when a bug appears, any of those lines could be the cause.
Fields Private, Methods Public
public class Wallet {
static class BankAccount {
private final String holder;
private long balance;
BankAccount(String holder, long opening) {
if (opening < 0) {
throw new IllegalArgumentException("Negative opening");
}
this.holder = holder;
this.balance = opening;
}
public String getHolder() { return holder; }
public long getBalance() { return balance; }
public void deposit(long amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit must be positive");
}
balance += amount;
}
public boolean withdraw(long amount) {
if (amount <= 0 || amount > balance) {
return false;
}
balance -= amount;
return true;
}
}
public static void main(String[] args) {
BankAccount acc = new BankAccount("Navika", 5000);
acc.deposit(1500);
System.out.println("Withdraw 10000: " + acc.withdraw(10000));
System.out.println("Withdraw 2000: " + acc.withdraw(2000));
System.out.println(acc.getHolder() + ": " + acc.getBalance());
try {
acc.deposit(-300);
} catch (IllegalArgumentException e) {
System.out.println("Rejected: " + e.getMessage());
}
}
}Withdraw 10000: false
Withdraw 2000: true
Navika: 4500
Rejected: Deposit must be positiveHow it works
holderandbalanceareprivate: only code insideBankAccountcan touch them.- The constructor refuses a negative opening balance, so an account can never start invalid.
depositandwithdraware the only ways to change the balance, and each checks its rule. Withdrawing Rs. 10,000 from Rs. 6,500 is refused.getBalance()lets outside code read the balance without being able to write it.
Try it yourself: add a rule that one withdrawal can't exceed Rs. 25,000. Only one method needs to change.
A note on this example: in a single file like this, main could still reach acc.balance, because private members of a nested class are visible to the whole top-level class Wallet. In real projects each class has its own file, and the compiler blocks it: balance has private access in Account (see "Access Modifiers in Java").
What Encapsulation Buys You
| Benefit | In the example |
|---|---|
| Valid state, always | The balance can't go negative |
| One place for each rule | The withdrawal limit lives in withdraw, nowhere else |
| Freedom to change the inside | balance could become paise in a long, or a BigDecimal, without changing any caller |
| Easier debugging | If the balance is wrong, only a few methods could have changed it |
| Read-only data | holder has a getter and no setter |
Common Mistakes
A getter and setter for every field, with no checks. That's hiding the field's name, not protecting it. Add methods that express real actions, like deposit.
Returning a mutable internal object. A getter that returns the internal List lets callers change it; return a copy (see "Getters and Setters in Java").
Making fields public "for now". Every caller then depends on them, and it's hard to take back.
Interview Corner
What is encapsulation in Java?
Encapsulation is bundling an object's data with the methods that operate on it, and restricting direct access to the data. Fields are made private, and public methods provide controlled access, enforcing rules such as validation. This keeps objects in a valid state, localizes rules, and lets the internal representation change without affecting callers.
How do you achieve encapsulation in Java?
Declare fields private, initialize them through constructors that validate input, expose behavior through public methods that enforce the class's rules, provide getters only where reading is needed and setters only where changing is allowed, and avoid leaking references to mutable internal objects.
Practice
- Easy: Write a
Thermostatclass whosetemperaturemust stay between 16 and 30. ProvidegetTemperature(),warmer()andcooler()that move it by one degree and stop at the limits. - Harder: Change
BankAccountto store the balance in paise (long) internally, whiledeposit,withdrawandgetBalancestill work in rupees. Doesmainneed any change?
Key Takeaways
Was this tutorial helpful?