Encapsulation
Getters and Setters
Master accessor and mutator design, read-only/write-only properties, and defensive copying of mutable references.
Interview: Critically important: defensive copying inside getters and constructors to prevent reference leaks of mutable objects.
Getters and Setters (accessors and mutators) are public methods used to inspect or update private fields. While they provide data protection, developers must implement Defensive Copying when handling mutable reference fields to prevent state leaks.
Access Separation
Omitting setters creates read-only variables. Omitting getters creates write-only variables (e.g., password hashing buffers).
Reference Leaks
Returning a direct reference to a private mutable object (like a Date or List) allows external callers to modify internal state directly.
Defensive Copies
To secure state, copy mutable inputs on constructor/setter entry and return new copies on getter access.
The Danger of Mutable Reference Leaks
Declaring a field private only protects the reference pointer, not the underlying object's contents. If a class has a field private java.util.Date joiningDate, and the getter is written as:
public Date getJoiningDate() {
return this.joiningDate; // Reference Leak!
}
An external caller can write:
Date date = employee.getJoiningDate();
date.setTime(0); // Mutates employee's internal state directly!
How Defensive Copying Resolves Reference Leaks
To guarantee true encapsulation, copy all mutable object types during parameter inputs and getter returns:
- In Constructors & Setters: Do not assign parameters directly (e.g.
this.joiningDate = joiningDate;). Instead, clone or recreate:this.joiningDate = new Date(joiningDate.getTime());. - In Getters: Do not return the internal reference. Return a copy:
return new Date(this.joiningDate.getTime());. - For Collections: Return unmodifiable views using
Collections.unmodifiableList(list)or return new lists:return new ArrayList<>(this.items);.
Common Pitfalls
- Returning Uncopied Arrays: Returning internal array fields directly in getters, letting callers modify elements by index (e.g.,
arr[0] = newVal). - Auto-Generating Getters and Setters: Blindly using IDE auto-generation or Lombok annotations on mutable fields without adding defensive copy overrides.
Best Practices
- Use Immutable Types: Prefer immutable types (e.g.
java.time.LocalDateinstead ofjava.util.Date) to eliminate the need for defensive copying. - Use Unmodifiable Collection Wrappers: Expose lists as
Collections.unmodifiableList(this.list)or use Java 9+List.copyOf(this.list).
Interview-Relevant Information
Q1: What is a reference leak in Java, and how do you prevent it?
Answer: A reference leak occurs when a class exposes a direct reference to its private mutable fields (like dates, arrays, or lists) via getters or constructors. External code can modify the internal state of the object directly. It is prevented by implementing defensive copying (returning and storing copies of mutable objects).
Q2: How do you write a defensive copy for a List field?
Answer: Inside the getter, return a new list: return new ArrayList<>(this.myList); or wrap it: return Collections.unmodifiableList(this.myList);. Inside the constructor/setter, clone it: this.myList = new ArrayList<>(inputList);.
Quick Checklist
Can you explain reference leakage, implement defensive copying for Date and List fields, and write a read-only object wrapper? If yes, you understand getters, setters, and defensive copying.
Use Cases
Designing secure system entities that are passed across boundary layers (e.g. database entities).
Creating robust configuration containers that cannot be altered dynamically by consumer code.
Common Mistakes
Returning direct private array references in getter methods, exposing index assignments.
Storing mutable constructor parameters directly, exposing the class to post-construction external mutation.