LearnFrenzy
Sign in Take a free test

Classes and Objects · Lesson 22 of 42

Encapsulation, Access Modifiers and Packages

5 min read Updated Nov 7, 2026 Saurabh Samir

In the first lesson of this chapter anyone could write account.balance = -99999;. Real code can't allow that. Encapsulation means hiding an object's data and exposing only safe operations, so the object can always protect its own rules. It is the first of the four pillars of object-oriented programming.

In this lesson

  • Private fields with getters and validated setters
  • The four access levels: public, protected, default, private
  • Packages and imports
  • Immutable classes

Encapsulation in practice

public class Main {
    public static void main(String[] args) {
        BankAccount acc = new BankAccount("Meena", 1000);
        acc.withdraw(300);
        System.out.println(acc.getOwner() + " has ₹" + acc.getBalance());
        try {
            acc.withdraw(5000);
        } catch (IllegalStateException e) {
            System.out.println("Blocked: " + e.getMessage());
        }
        // acc.balance = 1_000_000;   → error: balance has private access
    }
}

class BankAccount {
    private final String owner;
    private double balance;              // hidden: only this class can touch it

    BankAccount(String owner, double opening) {
        this.owner = owner;
        deposit(opening);
    }

    public double getBalance() {          // read access
        return balance;
    }

    public String getOwner() {
        return owner;
    }

    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Deposit must be positive");
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > balance) throw new IllegalStateException("Insufficient funds");
        balance -= amount;
    }
}

Output

Meena has ₹700.0
Blocked: Insufficient funds

Encapsulated object

  1. Public methodsdeposit(), withdraw(), getBalance() – the safe interface
  2. Rules insideamount must be positive, no overdraft
  3. Private databalance – unreachable from outside

Notice there is no setBalance(). Good encapsulation doesn't mean a getter and setter for every field – it means exposing only the operations that make sense for the object.

Access modifiers

ModifierSame classSame packageSubclass (other package)Everywhere
private✓✗✗✗
(default – no keyword)✓✓✗✗
protected✓✓✓✗
public✓✓✓✓

A practical default: make fields private, make methods public only if other classes need them, and use protected sparingly for things subclasses must customise. A top-level class can only be public or default.

Common mistake: public fields

A public field can be set to any value from anywhere, and you can never add validation later without breaking every caller. Keep fields private from day one.

Getters and setters with validation

public class Main {
    public static void main(String[] args) {
        Employee e = new Employee();
        e.setName("  Farah Khan ");
        e.setAge(29);
        System.out.println("[" + e.getName() + "] " + e.getAge());
        try {
            e.setAge(12);
        } catch (IllegalArgumentException ex) {
            System.out.println(ex.getMessage());
        }
    }
}

class Employee {
    private String name;
    private int age;

    public String getName() { return name; }

    public void setName(String name) {
        if (name == null || name.isBlank()) throw new IllegalArgumentException("Name required");
        this.name = name.strip();
    }

    public int getAge() { return age; }

    public void setAge(int age) {
        if (age < 18 || age > 65) throw new IllegalArgumentException("Age must be 18–65");
        this.age = age;
    }
}

Output

[Farah Khan] 29
Age must be 18–65

The naming convention getX / setX (and isX for booleans) matters: frameworks such as Spring, Hibernate and Jackson use it to read and write your objects automatically.

Packages and imports

A package groups related classes, like folders, and gives them unique full names. Package names are lowercase and usually start with a reversed domain:

src/
└── com/learnfrenzy/bank/
    ├── BankAccount.java     // package com.learnfrenzy.bank;
    └── service/
        └── TransferService.java   // package com.learnfrenzy.bank.service;
package com.learnfrenzy.bank.service;

import com.learnfrenzy.bank.BankAccount;   // one class
import java.util.*;                        // every class in java.util (not sub-packages)

public class TransferService {
    public void transfer(BankAccount from, BankAccount to, double amount) {
        from.withdraw(amount);
        to.deposit(amount);
    }
}
  • java.lang (String, Math, Integer, System…) is imported automatically.
  • If two packages have a class with the same name (java.util.Date and java.sql.Date), use the full name for one of them.
  • Default (package-private) access lets classes in the same package cooperate while hiding details from the rest of the application.

Immutable classes

An immutable object can't change after creation – like String. Immutable objects are simple to reason about and safe to share between threads.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        Money a = new Money(19_999, "INR");
        Money b = a.plus(new Money(5_001, "INR"));
        System.out.println(a + " | " + b);
        List<String> fixed = List.of("read-only");
        System.out.println(fixed);
    }
}

final class Money {                       // final: no subclass can add mutability
    private final long paise;             // final fields, set once
    private final String currency;

    Money(long paise, String currency) {
        this.paise = paise;
        this.currency = currency;
    }

    Money plus(Money other) {             // "changes" return a new object
        return new Money(paise + other.paise, currency);
    }

    @Override
    public String toString() {
        return String.format("%s %.2f", currency, paise / 100.0);
    }
}

Output

INR 199.99 | INR 250.00
[read-only]

Java 16+ records (chapter 10) create classes like this in a single line.

Practice

Try it yourself

  1. Write a Temperature class with a private Celsius field, a setter that rejects values below −273.15, and a getFahrenheit() method.

    Show solution
    public class Main {
        public static void main(String[] args) {
            Temperature t = new Temperature();
            t.setCelsius(30);
            System.out.println(t.getFahrenheit());
        }
    }
    
    class Temperature {
        private double celsius;
    
        public void setCelsius(double c) {
            if (c < -273.15) throw new IllegalArgumentException("Below absolute zero");
            celsius = c;
        }
    
        public double getFahrenheit() { return celsius * 9 / 5 + 32; }
    }

    Output

    86.0

Quick check

Which access level has no keyword?

Default (package-private): visible within the same package only.

Does encapsulation mean a getter and setter for every field?

No – expose only the operations the object should support, and validate them.

Which package is imported automatically?

java.lang

Summary

  • Encapsulation: private data, public operations that enforce the rules.
  • Access: private < default < protected < public.
  • Packages organise classes; immutable classes use final fields and return new objects for changes.

Interview practice: Java OOP in depth.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links