Java + Spring Boot track

Lesson 07 · Part 2: Object-Oriented Programming

Encapsulation

Encapsulation is an OOP principle that hides the state (field) of an object from the outside and makes it accessible only through designated methods (getter/setter…

Part 2: Object-Oriented Programming4 min readFree lesson

Encapsulation is an OOP principle that hides the state (field) of an object from the outside and makes it accessible only through designated methods (getter/setter, or domain method). This prevents objects from being broken by incorrect values ​​and allows room to freely change the internal implementation.

Learning Objectives

  • Know the difference between public / private / package-private access limiters
  • Write private field + getter/setter pattern
  • Understand the meaning and advantages of immutable objects
  • See how JDK 16+ record reduces boilerplate
  • Let’s take a look at the package concept.

Key concepts

1) Access restrictor

qualifier same class Same package child class Other packages
public ✅ ✅ ✅ ✅
protected ✅ ✅ ✅ ❌
(default) ✅ ✅ ❌ ❌
private ✅ ❌ ❌ ❌

It is recommended that most fields be left as private.

2) getter/setter

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 necessarily create setters. If there is a separate domain action such as depositing, it is better than a setter.

3) Immutable objects

An object whose value never changes once set is called an immutable object. It is safer and easier to debug even in multithreading.

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 field + no setter + value determination only in constructor.

4) record (JDK 16+)

An immutable data carrier can be defined in one line:

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

x()·equals/hashCode/toString is automatically created instead of constructor·getX().

5) Package

It is a namespace that groups classes with the same meaning. Declare it once at the top of the file, like package com.codingnow.lecture.oop07;. In this section, packages are not used to simplify the directory structure, but after Part 21, packages are automatically used along with the standard directory structure in Maven projects.

See as example

Example 1 — PrivateField.java: The first step in encapsulation

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("Reject:" + e.getMessage());
        }
    }
}

Execution result

age=21
Reject: age >= 0

Memo: Placing validation logic in the setter can prevent incorrect states in advance.

Example 2 — Account.java: Domain Behavior Driven

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());
    }
}

Execution result

Balance=12000

Note: Domain methods such as deposit / withdraw are more meaningful than setters.

Example 3 — ImmutablePoint.java: immutable object

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() + ")");
    }
}

Execution result

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

Note: The usage pattern for immutable objects is to return a new object instead of changing state.

Example 4 — PointRecord.java : One line as record

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));
    }
}

Execution result

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

Memo: record automatically creates toString / equals / hashCode.

Common Mistakes

  1. Create setters for all fields → Think about meaningful domain methods first
  2. Intended to be immutable, but internal collection is exposed as is (shallow immutability)
  3. Receiving variable object fields as is within record (defensive copy required)
  4. Overuse of public field → Encapsulation collapse
  5. Missing validation in setter

Cleanup

  • The field is first private, and the path to the outside is method
  • Immutable objects are safe and simple
  • record reduces boilerplate of data classes

Try it yourself

cd 02_Object-Oriented/07_Encapsulation/src
javac Account.java
java Account

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/Account.javaJava
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());
    }
}
src/ImmutablePoint.javaJava
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() + ")");
    }
}
src/PointRecord.javaJava
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));
    }
}
src/PrivateField.javaJava
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("Reject:" + e.getMessage());
        }
    }
}

Assignment

Problem 1 — Temperature encapsulation

  • File name: Homework01.java
  • Core concepts: private field, setter verification

Requirements

  • Place field private double celsius in class Temperature.
  • If setCelsius(double) is less than -273.15, it throws IllegalArgumentException.
  • toFahrenheit() is converted to Fahrenheit and returned.

Expected output

25.0°C = 77.0°F
Rejection: -300.0°C below absolute zero

Problem 2 — Money record

  • File name: Homework02.java
  • Core concept: record, equals/hashCode automatic generation

Requirements

  • Define record Money(long amount, String currency).
  • If both Money have the same amount/currency, equals is true.

Expected output

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

Check the correct answer

After solving it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

Homework01.javaJava
/** Encapsulation of the 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("Reject:" + 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;
    }
}
Homework02.javaJava
/** 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));
    }
}

Learn this lesson with a mentor

Work through the Java + Spring Boot track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now