Encapsulation in Java β€” Private Fields and Getters/Setters

Encapsulation is the principle of hiding an object's state behind methods. Fields are declared private; state changes go through public (or package-private) methods that can validate, log, synchronise, or fire events. The class controls its invariants β€” callers can't break them by reaching in directly.

The classical pattern

public class Circle {
    private double radius;                      // hidden

    public double getRadius() {                  // controlled read
        return radius;
    }
    public void setRadius(double radius) {        // controlled write
        if (radius < 0) throw new IllegalArgumentException("radius < 0");
        this.radius = radius;
    }
}

Immutability β€” often the best encapsulation

public final class Circle {                     // final β€” can't be subclassed
    private final double radius;                  // final β€” can't be reassigned

    public Circle(double radius) {
        if (radius < 0) throw new IllegalArgumentException("radius < 0");
        this.radius = radius;
    }
    public double radius() { return radius; }     // no setter
}

Records β€” encapsulation by default (Java 14+)

public record Circle(double radius) {
    public Circle {                               // compact constructor β€” validation
        if (radius < 0) throw new IllegalArgumentException("radius < 0");
    }
}
// Fields are private and final, accessor is radius(), equals/hashCode/toString free.

When getters/setters are not needed

A record replaces most "data carrier" classes. For internal-only types in a package, package-private fields are sometimes cleaner than getters β€” the boundary is the package, not the class.

Defensive copies for mutable fields

public class Order {
    private final List<Item> items;

    public Order(List<Item> items) {
        this.items = List.copyOf(items);          // immutable snapshot
    }
    public List<Item> items() {
        return items;                             // already immutable
    }
}

Common mistakes

  • Public fields β€” bypasses every benefit of encapsulation.
  • Setters without validation β€” just a verbose public field.
  • Returning internal mutable state β€” return this.items; lets callers corrupt it. Wrap in List.copyOf or Collections.unmodifiableList.
  • Auto-generating setters for fields that shouldn't be set β€” most fields should be final. Only add a setter if the invariant allows mutation.

Related

Pillar: OOP in Java. Tool: JSON to POJO generates encapsulated classes (POJO / record / Lombok).