Skip to content

Encapsulation — Access Modifiers, Getters/Setters, JavaBeans, and Data Hiding

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Encapsulation. We cover key concepts, practical examples, and best practices to help you master this topic.

Encapsulation in Java hides internal object state and exposes controlled access through getters and setters, enforced by access modifiers. Encapsulation is the first principle of object-oriented programming — it prevents external code from putting an object into an invalid state by restricting direct access to its fields.

What You'll Learn

  • The four Java access modifiers: private, default, protected, public
  • Getter and setter conventions (JavaBeans style)
  • Why encapsulation is critical for maintainable code
  • Validation logic in setters

Why It Matters

Without encapsulation, any code can set any field to any value — including invalid ones like a negative age or an empty name. Encapsulation centralizes validation in one place (the setter) rather than scattering checks everywhere the field is used.

Real-World Use

Every production Java framework relies on encapsulation. JPA entities use private fields with getters/setters, Spring beans encapsulate configuration, and DTOs (Data Transfer Objects) shield internal data structures from external consumers.


Access Modifiers

Java provides four access levels:

Modifier Same Class Same Package Subclass (different package) Any Class
private Yes No No No
default (no modifier) Yes Yes No No
protected Yes Yes Yes No
public Yes Yes Yes Yes

Private

public class Person {
    private String name;
    private int age;
}

private fields are accessible only within the same class. This is the most restrictive modifier and the default for fields in well-designed classes.

Package-Private (Default)

class Helper {
    int value; // accessible within the same package only
}

If no modifier is specified, the member is accessible from any class in the same package. This is useful for internal helper classes that should not be part of the public API.

Protected

public class Parent {
    protected int id;
}

public class Child extends Parent {
    void show() {
        System.out.println(id); // accessible in subclass
    }
}

protected allows access from subclasses (even in different packages) and from any class in the same package.

Public

public class MathUtils {
    public static int max(int a, int b) {
        return a > b ? a : b;
    }
}

public members are accessible from anywhere. Use public for the API of your class — constructors, methods intended for callers, and constants.

Getters and Setters

The standard pattern is to make fields private and expose access through public methods:

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Age must be between 0 and 150");
        }
        this.age = age;
    }
}

JavaBeans Naming Convention

  • For a boolean field active:
    • Getter: isActive() (not getActive())
  • For other types:
    • Getter: getFieldName()
    • Setter: setFieldName()

This convention is required by many frameworks (JSP, Spring, Jackson for JSON Serialization).

Validation and Data Integrity

Setters are the natural place to enforce invariants:

public void setEmail(String email) {
    if (email == null || !email.contains("@")) {
        throw new IllegalArgumentException("Invalid email address");
    }
    this.email = email;
}

This guarantees that every Person object always has a valid email, regardless of which code creates or modifies it.

Immutable Objects with No Setters

Sometimes you want an object that cannot be modified after creation:

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

Immutable objects are inherently thread-safe, can be cached freely, and are easier to reason about. Use final fields and omit setters.

Common Mistakes

  1. Making all fields public. This breaks encapsulation — any code can modify fields without validation. Always start with private fields.
  2. Exposing mutable objects in getters. If a field is a List or Date, returning the reference lets callers modify it. Return a copy or an unmodifiable view:
    public List<String> getTags() {
        return Collections.unmodifiableList(tags);
    }
    
  3. Leaking this in a constructor. Do not pass this to another method during construction — the object is not fully initialized yet.
  4. Using protected fields for inheritance. Protected fields couple the parent and child classes tightly. Prefer private fields with protected getters/setters.
  5. Writing getters/setters for every field by default. Some fields should not have setters (e.g., id, createdAt). Use your judgment.

Practice Questions

1. What is the difference between private and protected?
private restricts access to the same class only. protected allows access from subclasses and same-package classes.

2. Why should fields typically be private?
To enforce encapsulation — only the owning class can modify its state, ensuring validation and consistency.

3. What is the JavaBeans naming convention for boolean getters?
Use isXxx() instead of getXxx(). For example, isActive() rather than getActive().

4. How do you protect a mutable list from external modification?
Return Collections.unmodifiableList(list) from the getter, or return a defensive copy with new ArrayList<>(list).

5. Can a class be both immutable and have getters?
Yes. Immutable classes have only getters, no setters, and all fields are final. The object is fully initialized in the constructor.

Challenge Question:
Design an ImmutableStudent class with fields id (final int), name (final String), and grades (final List). Ensure the list cannot be modified from outside. Provide a method addGrade(int grade) that returns a new ImmutableStudent with the grade appended.

FAQ

What is the difference between data hiding and encapsulation?

Data hiding is restricting direct access to fields (via private). Encapsulation is the broader principle of bundling data and methods together and controlling access through a well-defined interface. Encapsulation includes data hiding but also includes validation and business logic.

Should I write getters for every field?

Yes, generally. Getters provide controlled read access. However, consider omitting setters for fields that should not change after creation (like id or createdTimestamp).

What is a Data Transfer Object (DTO)?

A DTO is a simple object that carries data between layers (e.g., from a REST API to a service). DTOs typically have only fields, getters, setters, and no business logic. Records (Java 14+) are perfect for DTOs.

Why does `List.of()` return an immutable list?

List.of() (Java 9+) returns an unmodifiable list as a safety measure. Callers cannot modify the returned collection, which prevents accidental side effects. This aligns with the principle of immutability.

What is defensive copying?

Returning a copy of an internal mutable object instead of the original reference. This prevents callers from modifying the internal state. For example, return new Date(this.createdAt.getTime()) instead of return createdAt.

Mini Project

Write a program BankAccount.java that:

  1. Has private fields: accountNumber (String), balance (double, initialized to 0)
  2. Provides getters for both fields
  3. Setter for accountNumber that rejects null or empty strings
  4. Methods deposit(double amount) and withdraw(double amount) with validation (no negative deposits, no overdraft)
  5. Uses a protected method calculateInterest() that subclasses can override
  6. Create a SavingsAccount subclass that overrides calculateInterest() with a different rate
  7. Demonstrates that the balance field cannot be directly modified from outside

What's Next

Encapsulation organizes data within a single class. But real systems need relationships between classes. Lesson 13 introduces inheritance — the extends keyword, super for parent access, method overriding, and the methods inherited from Object.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro