Constructors and Encapsulation

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

  1. Leaking this in constructor — registering with listeners before fully constructed.
  2. Publishing mutable internalsreturn items; allows caller to mutate; return copy or unmodifiable list.
  3. Setter without validation — defeats encapsulation.
  4. Boolean getter named getActive() — prefer isActive().
  5. Overusing public fields on data classes—use records or private fields.

Practice checkpoint

  1. Write constructor for Person(name, age) rejecting blank name and age < 0.

    • Answer: Objects.requireNonNull, if (age < 0) throw.
  2. Why private final String id with no setter?

    • Answer: Identity should not change after construction.
  3. What does this(...) do?

    • Answer: Calls another constructor in same class; must be first line.
  4. Fix: class exposes public List<Order> orders mutated by anyone.

    • Answer: Make private; add addOrder method; return List.copyOf or unmodifiable view.

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.

Next: /java/learn/oop/inheritance-and-polymorphism/