Learning goals
Initialize objects with constructors, enforce invariants through encapsulation, use getters/setters judiciously, and apply final fields for required immutable state.
Encapsulation
Hide internal state; expose controlled operations:
public class BankAccount {
private final String id;
private double balance;
public BankAccount(String id, double initialBalance) {
if (initialBalance < 0) {
throw new IllegalArgumentException("negative balance");
}
this.id = id;
this.balance = initialBalance;
}
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("amount");
balance += amount;
}
public double getBalance() {
return balance;
}
public String getId() {
return id;
}
}
private fields cannot be accessed from other classes—only through your API.
Constructors
Special methods that run on new:
public class User {
private final String email;
public User(String email) {
this.email = Objects.requireNonNull(email, "email");
}
}
Rules:
- Same name as class, no return type.
- Overloading allowed (different parameters).
- If no constructor written, compiler adds default no-arg (unless any constructor exists).
Constructor chaining
public class Rectangle {
private final int width;
private final int height;
public Rectangle(int side) {
this(side, side); // must be first statement
}
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
}
this(...) delegates to another constructor.
Getters and setters
public class Temperature {
private double celsius;
public double getCelsius() {
return celsius;
}
public void setCelsius(double celsius) {
if (celsius < -273.15) {
throw new IllegalArgumentException("below absolute zero");
}
this.celsius = celsius;
}
}
Not every field needs a setter—immutable objects expose getters only (final fields set in constructor).
final fields
public class Order {
private final String orderId;
private final Instant createdAt;
public Order(String orderId) {
this.orderId = orderId;
this.createdAt = Instant.now();
}
}
final must be assigned exactly once (constructor or field initializer). Object contents can still mutate if mutable:
private final List<String> lines = new ArrayList<>();
// lines reference fixed; list contents can change
Expose unmodifiable view:
public List<String> getLines() {
return List.copyOf(lines);
}
Static factory methods (pattern)
Alternative to public constructors:
public class Money {
private final long cents;
private Money(long cents) {
this.cents = cents;
}
public static Money ofDollars(double dollars) {
return new Money(Math.round(dollars * 100));
}
}
Name conveys intent (ofDollars, fromJson) and can cache instances.
Validation and invariants
An invariant is always true for valid objects (balance never negative, email non-null). Validate in constructors and mutators—reject bad state early.
Common mistakes
- Leaking
thisin constructor — registering with listeners before fully constructed. - Publishing mutable internals —
return items;allows caller to mutate; return copy or unmodifiable list. - Setter without validation — defeats encapsulation.
- Boolean getter named
getActive()— preferisActive(). - Overusing
publicfields on data classes—use records or private fields.
Practice checkpoint
-
Write constructor for
Person(name, age)rejecting blank name and age < 0.- Answer:
Objects.requireNonNull,if (age < 0) throw.
- Answer:
-
Why
private final String idwith no setter?- Answer: Identity should not change after construction.
-
What does
this(...)do?- Answer: Calls another constructor in same class; must be first line.
-
Fix: class exposes
public List<Order> ordersmutated by anyone.- Answer: Make private; add
addOrdermethod; returnList.copyOfor unmodifiable view.
- Answer: Make private; add
Interview angles
- “Encapsulation benefits?” — Hide implementation, enforce invariants, reduce coupling.
- “Immutable class checklist?” — final class, final fields, no setters, defensive copies of mutable components.
- “Default constructor?” — Provided only if no user-defined constructors exist.