← Back to Java series
🧱
OOP
OOP Β· Prerequisite: previous lecture

07. Encapsulation

Encapsulation is the OOP principle of hiding an object's **state (fields)** behind methods (getters/setters or domain methods). It prevents the object from being broken by invalid values and gives you room to change the implementation later.

JavaOOPobject-orientedencapsulation
Duration
⏱ ~1-1.5 hours
Level
πŸ“Š Beginner-Intermediate
Prerequisite
🎯 Previous lecture or equivalent knowledge
OUTCOME
Encapsulation is the OOP principle of hiding an object's **state (fields)** behind methods (getters/setters or domain methods). It prevents the object from being broken by invalid values and gives you room to change the implementation later.

What you'll learn

  • 1Know `public` / `private` / package-private / `protected`
  • 2Write the `private` field + getter/setter pattern
  • 3Understand immutable objects and their benefits
  • 4See how JDK 16+ `record` reduces boilerplate
  • 5Touch on the package concept

Overview

Encapsulation is the OOP principle of hiding an object's **state (fields)** behind methods (getters/setters or domain methods). It prevents the object from being broken by invalid values and gives you room to change the implementation later.

Core Concepts

1) Access modifiers

ModifierSame classSame packageSubclassOther package
`public`βœ…βœ…βœ…βœ…
`protected`βœ…βœ…βœ…βŒ
(default)βœ…βœ…βŒβŒ
`private`βœ…βŒβŒβŒ

Most fields should be **`private`** by default.

2) getter / setter

java
public class Account {
    private long balance;

    public long getBalance() { return balance; }

    public void deposit(long amount) {
        if (amount <= 0) throw new IllegalArgumentException("amount > 0");
        this.balance += amount;
    }
}

**Don't always add a setter.** If there's a meaningful **domain action** (like deposit), use it instead.

3) Immutable objects

An **immutable object** never changes after construction. They're thread-safe and easy to debug.

java
public final class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) { this.x = x; this.y = y; }
    public int getX() { return x; }
    public int getY() { return y; }
}

`final` class + `final` fields + no setters + values set only in the constructor.

4) `record` (JDK 16+)

Define an immutable data carrier in one line.

java
public record Point(int x, int y) {}

The compiler auto-generates the constructor, accessor `x()` (not `getX()`), and `equals` / `hashCode` / `toString`.

5) Packages

Packages are **namespaces** for related classes. Declare `package com.codingnow.lecture.oop07;` at the top of the file. This lecture skips packages for simplicity; from lecture 21 onward we use them with the standard Maven layout.

Examples

Example 1 β€” `PrivateField.java`: first step of encapsulation

java
public class PrivateField {
    private int age;

    public int getAge() { return age; }

    public void setAge(int age) {
        if (age < 0) throw new IllegalArgumentException("age >= 0");
        this.age = age;
    }

    public static void main(String[] args) {
        PrivateField p = new PrivateField();
        p.setAge(21);
        System.out.println("age=" + p.getAge());
        try {
            p.setAge(-1);
        } catch (IllegalArgumentException e) {
            System.out.println("rejected: " + e.getMessage());
        }
    }
}

**Output**

text
age=21
rejected: age >= 0

**Note:** putting **validation logic** in the setter blocks invalid state up front.

Example 2 β€” `Account.java`: domain actions over setters

java
public class Account {
    private long balance;

    public Account(long initial) { this.balance = initial; }

    public long getBalance() { return balance; }

    public void deposit(long amount) {
        if (amount <= 0) throw new IllegalArgumentException("amount > 0");
        balance += amount;
    }

    public void withdraw(long amount) {
        if (amount <= 0) throw new IllegalArgumentException("amount > 0");
        if (amount > balance) throw new IllegalStateException("insufficient balance");
        balance -= amount;
    }

    public static void main(String[] args) {
        Account a = new Account(10_000);
        a.deposit(5_000);
        a.withdraw(3_000);
        System.out.println("balance=" + a.getBalance());
    }
}

**Output**

text
balance=12000

**Note:** domain methods like `deposit` / `withdraw` express intent much better than raw setters.

Example 3 β€” `ImmutablePoint.java`: immutable object

java
public final class ImmutablePoint {
    private final int x;
    private final int y;

    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { return x; }
    public int getY() { return y; }

    public ImmutablePoint translate(int dx, int dy) {
        return new ImmutablePoint(x + dx, y + dy);
    }

    public static void main(String[] args) {
        ImmutablePoint p = new ImmutablePoint(1, 2);
        ImmutablePoint q = p.translate(10, 20);
        System.out.println("p=(" + p.getX() + "," + p.getY() + ")");
        System.out.println("q=(" + q.getX() + "," + q.getY() + ")");
    }
}

**Output**

text
p=(1,2)
q=(11,22)

**Note:** instead of mutating state, **return a new object** β€” the canonical immutable pattern.

Example 4 β€” `PointRecord.java`: a record in one line

java
public class PointRecord {
    public record Point(int x, int y) {}

    public static void main(String[] args) {
        Point a = new Point(1, 2);
        Point b = new Point(1, 2);
        System.out.println(a);
        System.out.println("a.x = " + a.x());
        System.out.println("a.equals(b) = " + a.equals(b));
    }
}

**Output**

text
Point[x=1, y=2]
a.x = 1
a.equals(b) = true

**Note:** `record` auto-generates `toString` / `equals` / `hashCode`.

Common Mistakes

  1. Reaching for a setter first β€” ask whether there's a meaningful domain method
  2. Wanting immutability but exposing the internal collection directly (shallow immutability)
  3. Accepting a mutable field directly in a `record` (needs a defensive copy)
  4. Sprinkling `public` fields everywhere β†’ encapsulation breaks down
  5. Skipping validation in the setter

Summary

  • Fields default to `private`; expose methods for the outside world
  • Immutable objects are safe and simple
  • `record` removes the boilerplate of data classes

Practice

# Practice - 07. Encapsulation

## Exercise 1 β€” `Temperature` encapsulation

  • File: `Homework01.java`
  • Key concepts: `private` field, setter validation

Requirements

  • Class `Temperature` with `private double celsius`.
  • `setCelsius(double)` throws `IllegalArgumentException` if below `-273.15`.
  • `toFahrenheit()` converts and returns Fahrenheit.

Expected output

text
25.0Β°C = 77.0Β°F
rejected: -300.0Β°C is below absolute zero

## Exercise 2 β€” `Money` record

  • File: `Homework02.java`
  • Key concepts: `record`, auto-generated equals/hashCode

Requirements

  • Define `record Money(long amount, String currency)`.
  • Two `Money` with the same amount/currency must be equal via `.equals()`.

Expected output

text
m1=Money[amount=1000, currency=KRW]
m1.equals(m2)=true
m1.equals(m3)=false

## Solutions After trying it yourself, compare with [`answer/`](./answer/).

Solution code (homework/answer/)

answer/Homework01.java

java
/** Encapsulation of Temperature class. */
public class Homework01 {
    public static void main(String[] args) {
        Temperature t = new Temperature();
        t.setCelsius(25.0);
        System.out.println(t.getCelsius() + "Β°C = " + t.toFahrenheit() + "Β°F");
        try {
            t.setCelsius(-300.0);
        } catch (IllegalArgumentException e) {
            System.out.println("rejected: " + e.getMessage());
        }
    }
}

class Temperature {
    private double celsius;

    public double getCelsius() { return celsius; }

    public void setCelsius(double c) {
        if (c < -273.15) throw new IllegalArgumentException(c + "Β°C is below absolute zero");
        this.celsius = c;
    }

    public double toFahrenheit() {
        return celsius * 9.0 / 5.0 + 32.0;
    }
}

answer/Homework02.java

java
/** Money record. */
public class Homework02 {
    record Money(long amount, String currency) {}

    public static void main(String[] args) {
        Money m1 = new Money(1000, "KRW");
        Money m2 = new Money(1000, "KRW");
        Money m3 = new Money(1000, "USD");
        System.out.println("m1=" + m1);
        System.out.println("m1.equals(m2)=" + m1.equals(m2));
        System.out.println("m1.equals(m3)=" + m1.equals(m3));
    }
}

Try It Yourself

bash
cd 02_oop/07_encapsulation/src
javac Account.java
java Account

Next Lecture

[08_Inheritance](../08_상속/) β€” `extends`, `super`, `@Override` and method overriding.

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—