iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Classes & Objects

A class is a blueprint — fields hold state, methods describe behaviour, constructors initialise instances. Java’s class system underpins everything from POJOs and DTOs to records and sealed hierarchies.

Fields, ctors, encapsulation, records

EXAMPLE
// 1) Minimal class
public class Point {
    int x;
    int y;
}

Point p = new Point();
p.x = 3;
p.y = 4;

// 2) Encapsulation — private fields, public methods
public class BankAccount {
    private final String owner;          // immutable after construction
    private long balanceCents;            // mutable state

    public BankAccount(String owner, long openingCents) {
        if (owner == null || owner.isBlank()) throw new IllegalArgumentException("owner");
        if (openingCents < 0) throw new IllegalArgumentException("negative opening");
        this.owner = owner;
        this.balanceCents = openingCents;
    }

    public String owner() { return owner; }
    public long  balanceCents() { return balanceCents; }

    public void deposit(long cents) {
        if (cents <= 0) throw new IllegalArgumentException("positive deposit only");
        balanceCents += cents;
    }

    public void withdraw(long cents) {
        if (cents <= 0)                  throw new IllegalArgumentException();
        if (cents > balanceCents)        throw new IllegalStateException("insufficient funds");
        balanceCents -= cents;
    }
}

// 3) Multiple constructors + this()-chaining
public class Money {
    private final long cents;
    private final String currency;

    public Money(long cents, String currency) {
        this.cents = cents;
        this.currency = currency;
    }
    public Money(long cents) { this(cents, "AUD"); }      // delegate to canonical ctor
}

// 4) Static members — belong to the class, not instances
public class IdGen {
    private static long counter = 0;
    public static synchronized long next() { return ++counter; }   // thread-safe enough
    private IdGen() {}                                              // utility — no instances
}
long id = IdGen.next();

// 5) Nested types
public class Outer {
    private int v = 10;

    public class Inner {                                 // non-static — captures Outer
        int show() { return v; }                          // can read Outer's fields
    }

    public static class Helper {                          // static — no Outer reference
        public int doubled(int x) { return x * 2; }
    }
}
// Prefer 'static class' unless you specifically want the captured outer instance.

// 6) toString / equals / hashCode — the boilerplate trio
import java.util.Objects;

public final class Coord {
    private final int x, y;
    public Coord(int x, int y) { this.x = x; this.y = y; }
    public int x() { return x; } public int y() { return y; }

    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Coord c)) return false;        // pattern matching for instanceof (Java 16+)
        return x == c.x && y == c.y;
    }
    @Override public int hashCode() { return Objects.hash(x, y); }
    @Override public String toString() { return "Coord[" + x + "," + y + "]"; }
}

// 7) Records (Java 16+) — concise immutable data classes
public record Coord2(int x, int y) {}
// Auto-generated: canonical constructor, accessors x() and y(), equals, hashCode, toString.

public record Order(String id, long totalCents, List<LineItem> items) {
    public Order {
        Objects.requireNonNull(id);
        if (totalCents < 0) throw new IllegalArgumentException();
        items = List.copyOf(items);    // defensive copy in the compact constructor
    }
    public boolean isFree() { return totalCents == 0; }
}

// 8) Inheritance — careful, often LSP-violating
public abstract class Animal {
    private final String name;
    protected Animal(String name) { this.name = name; }
    public String name() { return name; }
    public abstract String sound();
}
public class Dog extends Animal {
    public Dog(String name) { super(name); }
    @Override public String sound() { return "woof"; }
}

// Prefer composition over inheritance unless the IS-A relationship is rock solid.

// 9) Sealed classes — restrict who can extend (Java 17+)
public sealed interface Shape permits Circle, Rect, Triangle {}
public record Circle(double r) implements Shape {}
public record Rect(double w, double h) implements Shape {}
public record Triangle(double a, double b, double c) implements Shape {}

// Pattern matching for switch (Java 21+)
static double area(Shape s) {
    return switch (s) {
        case Circle c   -> Math.PI * c.r() * c.r();
        case Rect r     -> r.w() * r.h();
        case Triangle t -> heron(t.a(), t.b(), t.c());
    };
}

// 10) Immutability checklist (a final class to a code reviewer)
//   ✓ class is final OR sealed
//   ✓ all fields are private final
//   ✓ no setters
//   ✓ defensive copies for mutable collections / arrays in ctor AND accessors
//   ✓ accessor methods return immutable views (List.copyOf, Map.copyOf)
//   ✓ equals + hashCode based on the value, not identity

// 11) Common bugs
//   • Missing equals OR hashCode — using object as map key behaves randomly
//   • Default toString() — class@1a2b3c — useless in logs and tests
//   • Mutable Date / Calendar fields — use java.time.* and immutable types instead
//   • Setters returning 'this' for chaining — looks like a builder, isn't safe — use records or true builders
//   • Allowing 'null' through constructors — Optional or Objects.requireNonNull at the boundary
//   • Sharing a non-thread-safe collection field — return List.copyOf or unmodifiableList in getters

Why it matters

Modern Java leans on records and sealed interfaces, and you should too: a record gives you correct equals/hashCode/toString for free, and a sealed hierarchy lets pattern matching cover every case at compile time. Reach for inheritance only when the IS-A relationship is truly stable.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
public class User {
    String name;
    int age;
    public User(String name, int age) { this.name = name; this.age = age; }
    public String greet() { return "hi, " + name; }
}
Try it Yourself »

Exercise

Refer to the current instance.

.name = name;

Test yourself

Q1. A class is instantiated with…
Q2. "this" inside an instance method refers to…
Q3. A constructor has…

Discussion

Loading…