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 runtimeWithout 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