VisualStudioTutor.com Java Tutorial All Lessons

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 private to 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

ProblemWhy it happensHow to fix it
Making every field publicThe object cannot protect its own rules.Use private fields and deliberate public methods.
Creating setters for everythingSome values should be immutable after construction.Use final and getters where appropriate.
Validating only in the UIOther 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.