JavaEncapsulation

Encapsulation

Encapsulation means bundling an object's data (fields) and the methods that operate on that data together into a single unit — a class — while restricting direct access to the internal state from outside code. Instead of letting other code reach in and change a field freely, the class controls access through its own methods.

The classic pattern: private fields, public accessors

The most common way to encapsulate a class in Java is to make its fields private and expose controlled access through public getter and setter methods.

A minimally-encapsulated class

Java
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) {
        this.age = age;
    }
}

On its own, this looks like it just adds boilerplate around plain field access — and it would, if the setters did nothing more than assign the value. The real payoff of encapsulation shows up once a setter actually enforces a rule.

Why it matters: protecting invariants

An invariant is a rule about an object's state that should always hold true — for example, “a person's age can never be negative.” If fields were public, any code anywhere could set person.age = -5 and nothing would stop it. By keeping the field private and forcing every change through a setter, the class gets one chokepoint where it can validate input and refuse to let the object enter an invalid state.

Validating input in a setter

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

    public void setAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative: " + age);
        }
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p.setAge(30);   // fine
        p.setAge(-5);   // throws IllegalArgumentException — invariant protected
    }
}

Because age is private, this validation logic is the only way to change it — there is no back door. That guarantee is what encapsulation buys you: every Person object that exists is guaranteed to have never had a negative age, at any point in its lifetime.

Warning
Encapsulation is broken the moment you expose a mutable field directly as `public`, or return a mutable internal object (like an array or `ArrayList`) from a getter without copying it — callers can then mutate your internal state from outside, bypassing every rule your setters enforce.
Not just getters and setters

Encapsulation is a broader idea than “always write a getter and setter for every field.” Sometimes the right amount of encapsulation is no setter at all — for example, an immutable class might expose only a getter and set the field once, in the constructor, exactly like the final fields you'll see on Records. The core principle is the same either way: the class, not outside code, decides how its own state can change.

Note
Encapsulation relies directly on Java's access modifiers (`private`, `protected`, package-private, `public`) to actually restrict access. See the dedicated **Access Modifiers** page for the full breakdown of what each one allows.
  • Encapsulation bundles data and behavior into one class and restricts direct access to the data.

  • The classic pattern: private fields, public getter/setter methods.

  • Setters are the natural place to validate input and protect an object's invariants.

  • Exposing a mutable field or returning a mutable internal reference silently defeats encapsulation.