Lesson 15 of 20
Encapsulation
Protect object state with access modifiers and expose controlled operations through methods, getters, and setters.
Learning objectives
- Explain why public fields can be dangerous.
- Use
privateto protect fields. - Create getters and validated setters.
- Prefer behavior methods when they better express business rules.
- Build a class that prevents invalid state.
1. Why encapsulation matters
If every field is public, any part of the program can put an object into an invalid state. Encapsulation hides internal data and controls how it may change.
Problem example: public balance
public class BadAccount {
public double balance;
}
// Elsewhere:
BadAccount account = new BadAccount();
account.balance = -1000000; // allowed, even if this makes no sense
2. Protect fields with private
public class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double openingBalance) {
this.accountNumber = accountNumber;
this.balance = openingBalance;
}
public double getBalance() {
return balance;
}
}
Outside code cannot directly change balance. It must use methods that the class intentionally exposes.
3. Validate changes
Example: controlled deposit and withdrawal
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit must be positive.");
}
balance += amount;
}
public boolean withdraw(double amount) {
if (amount <= 0 || amount > balance) {
return false;
}
balance -= amount;
return true;
}
These methods preserve the object's rules. A withdrawal cannot silently create a negative balance.
4. Getters and setters
A setter is useful when direct assignment needs validation, but not every field needs a setter. If a value should never change after construction, expose only a getter.
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email.");
}
this.email = email;
}
Mini project: encapsulated student record
public class Student {
private final String studentId;
private String name;
private int mark;
public Student(String studentId, String name, int mark) {
this.studentId = studentId;
this.name = name;
setMark(mark);
}
public String getStudentId() {
return studentId;
}
public String getName() {
return name;
}
public int getMark() {
return mark;
}
public void setMark(int mark) {
if (mark < 0 || mark > 100) {
throw new IllegalArgumentException("Mark must be 0 to 100.");
}
this.mark = mark;
}
public boolean hasPassed() {
return mark >= 50;
}
}
Common mistakes
| Problem | Why it happens | How to fix it |
|---|---|---|
| Making every field public | The object cannot protect its own rules. | Use private fields and deliberate public methods. |
| Creating setters for everything | Some values should be immutable after construction. | Use final and getters where appropriate. |
| Validating only in the UI | Other callers can bypass UI validation. | Enforce important rules inside the domain class too. |
Summary
Encapsulation protects object integrity by controlling access to state. Lesson 16 builds on this by creating related class hierarchies with inheritance and polymorphism.