Java Notes
Complete guide to encapsulation in Java — access modifiers, getters and setters, data hiding, immutable classes, and building robust protected object state.
Encapsulation is the practice of bundling data (fields) and the methods that operate on that data into a single unit (class), while restricting direct access to the internal state. External code interacts with the object only through a controlled public interface.
Think of a capsule — medicine is wrapped inside a protective shell. You take the capsule (public interface), but you never directly touch the medicine inside (private fields).
Why Encapsulation Matters
Without encapsulation, any code can modify any data:
// ❌ NO encapsulation — fields exposed
public class BankAccount {
public double balance;
public String owner;
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.balance = 1000;
// ANYONE can do this — no validation!
account.balance = -999999; // Negative balance? No protection!
account.owner = ""; // Empty owner? No rules!
}
}With encapsulation, you control all access:
Error: Insufficient funds Error: Deposit must be positive Balance: $1300.0 History: [Opened with $1000.0, Deposited: $500.0, Withdrew: $200.0]
Access Modifiers — The Tools of Encapsulation
| Modifier | Same Class | Same Package | Subclass (other pkg) | Everywhere |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
| (default) | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
public class AccessDemo {
private int privateField = 1; // Only this class
int defaultField = 2; // This package
protected int protectedField = 3; // This package + subclasses
public int publicField = 4; // Everyone
private void privateMethod() { }
void defaultMethod() { }
protected void protectedMethod() { }
public void publicMethod() { }
}Choosing the Right Modifier
private— Default choice for fields. Always start here.protected— For fields/methods that subclasses need(default)— For package-internal helperspublic— Only for the intended API surface
Getters and Setters
The standard pattern for controlled field access:
Not All Fields Need Setters
Some fields should be read-only after construction:
public class OrderItem {
private final String productId; // Immutable after creation
private final String productName;
private int quantity; // Can be updated
public OrderItem(String productId, String productName, int quantity) {
this.productId = productId;
this.productName = productName;
setQuantity(quantity);
}
// Only getters for immutable fields
public String getProductId() { return productId; }
public String getProductName() { return productName; }
// Getter AND setter for mutable field
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) {
if (quantity < 1) {
throw new IllegalArgumentException("Quantity must be at least 1");
}
this.quantity = quantity;
}
}Defensive Copies — Protecting Mutable Fields
When your fields contain mutable objects (like lists, arrays, dates), you must return copies:
Grades: [85, 90, 78, 92] Average: 86.25
Immutable Classes — Maximum Encapsulation
An immutable class cannot be modified after creation:
Price: USD 29.99 Tax: USD 2.40 Total: USD 32.39 After 10% off: USD 29.15
Rules for Immutable Classes:
- Class is
final(cannot be subclassed) - All fields are
private final - No setters
- Methods that "modify" return new objects
- Defensive copies for mutable parameters and returns
Real-World Encapsulation: Configuration Manager
public class AppConfig {
private static AppConfig instance;
private final Map<String, String> settings;
private final String environment;
private boolean locked;
private AppConfig(String environment) {
this.environment = environment;
this.settings = new HashMap<>();
this.locked = false;
}
public static AppConfig create(String environment) {
if (instance != null) {
throw new IllegalStateException("Config already initialized");
}
instance = new AppConfig(environment);
return instance;
}
public static AppConfig getInstance() {
if (instance == null) {
throw new IllegalStateException("Config not initialized");
}
return instance;
}
public void set(String key, String value) {
if (locked) {
throw new IllegalStateException("Config is locked — cannot modify");
}
settings.put(key, value);
}
public String get(String key) {
return settings.getOrDefault(key, null);
}
public String get(String key, String defaultValue) {
return settings.getOrDefault(key, defaultValue);
}
// Once locked, no more modifications
public void lock() {
this.locked = true;
System.out.println("Config locked for " + environment);
}
public String getEnvironment() { return environment; }
public boolean isLocked() { return locked; }
}Common Mistakes
- Exposing internal mutable objects:
``java private List<String> items; public List<String> getItems() { return items; } // ❌ External code can modify! public List<String> getItems() { return Collections.unmodifiableList(items); } // ✅ ``
- Public fields:
``java public int age; // ❌ No validation possible ``
- Setters that don't validate:
``java public void setAge(int age) { this.age = age; } // ❌ age = -5 allowed! ``
- Getter returns reference to mutable field:
``java private Date birthDate; public Date getBirthDate() { return birthDate; } // ❌ Caller can mutate! ``
- Breaking encapsulation with reflection (intentional violation):
``java Field field = obj.getClass().getDeclaredField("privateField"); field.setAccessible(true); // Breaks encapsulation! Don't do this in production. ``
Best Practices
- Default to
private— only expose what's necessary - Use
finalfor fields that shouldn't change after construction - Return defensive copies of mutable objects
- Validate all input in setters and constructors
- Prefer immutable classes when possible
- Don't generate getters/setters blindly — think about what should be exposed
- Use
Collections.unmodifiableList()for read-only collection access
Interview Questions
Q1: What is encapsulation? Bundling data and methods into a class while hiding internal state behind access modifiers. External code uses only the public interface.
Q2: How does encapsulation differ from abstraction? Abstraction is about design (WHAT to show). Encapsulation is about implementation (HOW to hide). Encapsulation implements the hiding that abstraction designs.
Q3: Can we achieve encapsulation without getters/setters? Yes. Not all fields need external access. Some fields are purely internal. Encapsulation means controlling access — sometimes that means NO access.
Q4: What is a POJO? Plain Old Java Object — a class with private fields, a constructor, getters, and setters. No business logic.
Q5: Why return defensive copies from getters? To prevent external code from modifying your internal state through the returned reference. Especially important for mutable objects like List, Date, and arrays.
Q6: What is the benefit of immutable classes? Thread-safe without synchronization, safe to share, no unexpected state changes, simpler to reason about, and good HashMap keys.
Q7: What happens if you access a private field from another instance of the same class? It works! Access modifiers are per-CLASS, not per-object. One BankAccount object CAN access private fields of another BankAccount object.
Q8: What is the difference between tight encapsulation and loose encapsulation? Tight encapsulation: ALL fields are private, accessed only through methods with validation. No field is accessible without going through the class's public API. Loose encapsulation: some fields are protected or package-private, allowing broader access. Tight encapsulation is preferred for robust code.
Q9: How does encapsulation support the principle of least privilege? Encapsulation ensures each component only exposes what's necessary. Private fields + specific getters/setters mean external code can only do what you explicitly allow. This prevents accidental misuse, reduces bugs, and makes the system more secure and maintainable.
Q10: What is an immutable class and how does encapsulation help? An immutable class has state that cannot change after creation. Encapsulation enables this: make fields private final, provide no setters, don't leak mutable references (return copies), and make the class final. Example: String, Integer, LocalDate.
Q11: How do getters and setters differ from public fields? Getters/setters provide: (1) validation (reject negative age), (2) computed properties (return calculated values), (3) read-only or write-only access, (4) ability to change implementation without affecting users, (5) debugging hooks. Public fields provide none of these.
Q12: Can encapsulation exist without abstraction? Yes, but they usually work together. Encapsulation is the mechanism (private fields, public methods). You could technically have private fields with getters that expose everything — that's encapsulation without meaningful abstraction. Good design uses both: hide data AND simplify the interface.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Encapsulation in Java.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Java Master Course topic.
Search Terms
java-master-course, java master course, java, master, course, oops, encapsulation, encapsulation in java
Related Java Master Course Topics