skip to content

Java lets you make a field public, so why does encapsulation favor private fields with accessor methods?

level: juniorimportance: must knowfreq 70%

answer

  1. public field = name+type become your contract, can't change
  2. private + method = class controls every read/write
  3. Enables validation, read-only, derived, logging — invisibly
  4. Callers depend on behavior, not data layout
  5. Exception: public static final constants, immutable records

basics

~20 s

A private field hides the data and lets the class control how it's read or changed through methods. That means you can add validation, compute values, or change how the data is stored later without breaking code that uses the class.

solid answer

~50 s

Making a field public lets any code read and overwrite it directly, which means the field's exact name, type, and presence become part of your public contract — you can't change them later without breaking callers, and you can't stop anyone from putting the object into an illegal state. Making the field private and exposing methods (getters/setters or richer behavior) flips that: the class controls every read and write, so you can validate inputs (reject a negative balance), compute or lazily derive a value, log or fire events on change, make a field read-only by omitting the setter, or switch the internal representation entirely — all without touching callers, because they only ever talk to the method. That's encapsulation: depend on behavior, not on the data layout. The cost is a little boilerplate, but it buys the freedom to evolve internals safely, which is why public mutable fields are an anti-pattern in non-trivial classes.

go deeper

for a junior

State that private fields let the class control access and that public fields can't be validated or changed later safely.

for a middle

List concrete benefits (validation, read-only, derived values, representation change) and note that blind getters/setters aren't true encapsulation.

for a senior

Frame it as depending on behavior over layout, discuss when public fields are acceptable (constants, immutable records) and prefer behavior-rich APIs over naked accessors.

for a principal

Tie field exposure to long-term API/contract stability and domain-model integrity, and weigh encapsulation against value-object/record patterns and serialization/framework constraints.

## The choice Java lets you expose object state two ways: ```java class A { public int balance; } // direct field access class B { private int balance; public int getBalance() { return balance; } public void deposit(int amt) { /* ... */ } } // access through methods ``` Both compile. So why is `B` the recommended shape? The answer is **encapsulation** — hiding the internal representation so callers depend only on a behavioral contract. ## What goes wrong with a public field When a field is `public`, callers write `a.balance = -50;` directly. This has three consequences: 1. **You can't validate or guard it.** Nothing stops illegal values; the object can be driven into invalid states (a negative balance) and the class can't intervene. 2. **The field's identity becomes your contract.** Its **name** and **type** are now baked into every caller. Rename `balance`, change it from `int` to `BigDecimal`, or compute it from other state instead of storing it — and every caller breaks. You've lost the freedom to change the representation. 3. **You can't add cross-cutting behavior.** Want to log writes, fire a change event, or make the field lazily computed? Impossible without a method in the path. ## What private + accessors buys you With a `private` field reached only through methods, the class controls **every** read and write, so you can later — *without changing the method's signature, hence without breaking callers* — do any of: - **Validate:** `setBalance` can reject negatives or throw. - **Make it read-only:** provide a getter, omit the setter. - **Compute / derive:** `getFullName()` can concatenate `first` + `last`; there need not even be a stored field. - **Change representation:** store cents as a `long` internally while the getter still returns a `BigDecimal`. - **Add behavior:** log, cache, lazily initialize, or publish an event on change. All of these are invisible to callers because callers only ever see the **method**, not the field. That is the entire point of encapsulation: callers **depend on behavior, not on data layout**, so the layout stays free to evolve. ## Important nuance: getters/setters aren't automatically 'good' Blindly generating a getter and setter for every field gives back nearly the same exposure as a public field — it just adds boilerplate. Real encapsulation often means exposing **richer behavior** (`account.deposit(amt)` that enforces rules) rather than a naked `setBalance`. And some types are deliberately the exception: a small **immutable value carrier** (e.g. a Java `record`) may expose its components directly because there's no mutable state to protect and no representation you intend to hide. ## When public fields are acceptable - `public static final` **constants** (e.g. `Math.PI`) — immutable, no invariant to protect. - Components of an immutable `record`, where exposure is the design. ## Bottom line Prefer `private` fields plus the minimal behavioral surface. The small cost in boilerplate buys validation, read-only-ness, derived values, and — most importantly — the freedom to change internals later without breaking anyone.

  • Give a concrete thing you can do with a private field + getter that you cannot do with a public field.
    Validate or transform on access — e.g. reject negative inputs in a setter, or return a value computed from other fields — and change the internal storage type later without breaking callers.
  • Are auto-generated getters and setters always proper encapsulation?
    No. A public setter for every field exposes nearly as much as a public field. Real encapsulation often means richer behavior (e.g. deposit/withdraw) that enforces invariants, not naked accessors.

saying these in an interview costs you the question

  • Claiming getters/setters on every field is automatically good encapsulation — it can just re-expose state
  • Exposing mutable public fields in a domain class
  • Thinking encapsulation is only about 'hiding for security' rather than change-freedom
  • Adding a setter reflexively when the field should be read-only

context