EMZETT.
Login

Encapsulation

In short: Keeping a class’s fields private and only allowing access via public methods (getters/setters) — protects internal state from uncontrolled modification from outside.

In more detail: A setter can additionally validate before it accepts a value (e.g. reject negative values) — which wouldn’t be possible with a directly public field. Encapsulation is one of the four core pillars of OOP and makes a class more robust against misuse by other code.

class Account {
    private double balance;
    public double getBalance() { return balance; }
    public void deposit(double amount) {
        if (amount > 0) balance += amount;
    }
}

In Depth

Basic pattern: private field, public access methods

class Person {
    private int age; // NOT directly accessible from outside
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Invalid age: " + age);
        }
        this.age = age;
    }
}
 
Person p = new Person();
p.setAge(30);       // ok
// p.age = -5;      // would NOT compile - field is private
p.setAge(-5);       // compiles, but throws IllegalArgumentException at runtime

Without encapsulation (a public field int age;), any code anywhere in the program could set the field to an invalid value without the class being able to prevent it — the error then often only shows up much later and in a completely different place, once further work is done with the now-broken state, which makes debugging harder. With encapsulation, EVERY change necessarily runs through the setter, which enforces validation — if the validation logic changes later (e.g. lowering the maximum age to 130), it only has to be adjusted in ONE place, not everywhere in calling code.

Practical example: protected account balance

class Account {
    private double balance;
 
    public Account(double startingBalance) {
        if (startingBalance < 0) {
            throw new IllegalArgumentException("Starting balance must not be negative");
        }
        this.balance = startingBalance;
    }
 
    public double getBalance() {
        return balance;
    }
 
    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
        balance += amount;
    }
 
    public void withdraw(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
        if (amount > balance) throw new IllegalStateException("Not enough balance");
        balance -= amount;
    }
}

This shows the actual value of encapsulation: withdraw() guarantees the balance never becomes negative, no matter where in the program the method is called from. A public field double balance, by contrast, could be set to any value (even negative) from outside at any time, with no way for the class to prevent it — the invariant “balance is never negative” couldn’t be guaranteed at all.

Getters/setters are not mandatory — and no guarantee

A common misconception is that encapsulation automatically means “write a getter AND setter for every field”. In fact, a field with no setter (only a getter) is often the better choice when it shouldn’t change at all after construction — this enforces immutability and automatically makes the class thread-safe with respect to concurrent reads. Conversely, a getter that simply does return field; with no additional logic at all is sometimes also a sign that the field actually wouldn’t need to be encapsulated at all (e.g. for pure data holders). Many IDEs automatically generate getters/setters via right-click (“Generate…”), and modern Java versions (from Java 16) additionally offer record as a compact variant for immutable data classes, where all fields are automatically private final and only getters (with no setter, no get prefix) are generated — record Person(int age) {} implicitly creates a method age() instead of getAge().

Distinction from the other OOP core pillars

Encapsulation is often confused with abstraction, because both terms somehow have to do with “hiding” — the difference: abstraction hides complexity (HOW something works internally), encapsulation hides and protects state (WHICH data is directly accessible). An interface is primarily an abstraction tool, while private fields with getters/setters are primarily encapsulation — but in practice both principles are usually used together.

See also: Modifiers, OOP, Classes, Abstraction, Constructors