Skip to content

Lesson 01 · Classes & Objects

Objectives

After this lesson you will be able to:

  • Declare fields, methods, and constructors, and use this.
  • Apply access modifiers and encapsulation, and reason about variable scope.
  • Tell static from instance members, and predict initialization order.
  • Overload methods (including varargs) and resolve which overload is chosen.
  • Explain an object's lifecycle and when it becomes eligible for garbage collection.
  • Build immutable objects.

Fields, methods, constructors

A class bundles state (fields) and behavior (methods). A constructor has the class's name and no return type; if you write none, the compiler supplies a no-arg default constructor — but only if you declare no constructor at all.

java
class Point {
    int x, y;                       // instance fields (default to 0)

    Point(int x, int y) {           // constructor
        this.x = x;                 // `this` disambiguates field from parameter
        this.y = y;
    }
    Point() { this(0, 0); }         // constructor chaining via this(...)
}

Exam trap

Once you declare any constructor, the default no-arg constructor is gone. new Point() then fails to compile unless you also declare a no-arg constructor. A this(...) call must be the first statement in a constructor.

Access modifiers & encapsulation

Each member has an access level. From most to least restrictive:

ModifierSame classSame packageSubclass (other pkg)Anywhere
private
(package-private — no keyword)
protected
public

Encapsulation is the idiom of keeping fields private and exposing behavior through methods, so a class controls its own state and can change internals without breaking callers.

java
class Account {
    private long balance;                       // hidden state
    public long balance() { return balance; }   // controlled read
    public void deposit(long amount) {          // controlled, validated write
        if (amount <= 0) throw new IllegalArgumentException();
        balance += amount;
    }
}

Gotcha

Top-level classes may only be public or package-private — not private/protected. protected adds access to subclasses in other packages (beyond package-private), but only through the subclass's own type. Choosing a wider level than needed quietly leaks internals.

Variable scope

A local variable exists only within the block ({ }) where it's declared; a field exists for the life of the object (or class, if static). A local variable or parameter shadows a field of the same name — use this.field to reach the field.

java
class Box {
    int size = 10;
    void set(int size) {        // parameter shadows the field
        size = size;            // BUG: assigns the parameter to itself
        this.size = size;       // correct: field = parameter
    }
}

Static vs instance members

A static member belongs to the class, shared by all instances; an instance member belongs to each object. Static methods cannot use this or access instance members directly.

java
class Counter {
    static int total;               // one shared slot
    int id;                         // one per object

    Counter() { id = ++total; }
    static int count() { return total; }   // no access to id here
}

Initialization order

When a class loads and an object is built, members initialize in a fixed order:

  1. Static fields and static initializer blocks, in source order — once, when the class first loads.
  2. Instance fields and instance initializer blocks, in source order — every time an object is created.
  3. The constructor body.
java
class Demo {
    static int s = log("static field");
    static { log("static block"); }
    int i = log("instance field");
    { log("instance block"); }
    Demo() { log("constructor"); }
}
// First new Demo(): static field, static block, instance field, instance block, constructor
// Second new Demo(): instance field, instance block, constructor  (statics already done)

(Across a class hierarchy this interleaves super- and subclass steps — see Lesson 02.)

Overloading

Overloading = same method name, different parameter lists. The compiler picks the most specific applicable overload at compile time, preferring an exact match, then widening, then boxing, then varargs.

java
void f(int x)     { }
void f(long x)    { }
void f(Integer x) { }
void f(Object x)  { }

f(5);   // calls f(int) — exact match wins over widening (long) and boxing (Integer)

Gotcha

Overload resolution prefers widening over boxing, and boxing over varargs. So a short argument with overloads f(int) and f(Short) picks f(int) (widening) over f(Short) (boxing). Return type alone does not distinguish overloads.

Varargs

A varargs parameter (Type... name) lets a method accept zero or more arguments. Inside the method it is exactly an array; callers may pass loose arguments, an array, or nothing.

java
static int sum(int... xs) {          // xs is an int[]
    int total = 0;
    for (int x : xs) total += x;
    return total;
}

sum();              // 0   — empty array
sum(1, 2, 3);       // 6   — packed into new int[]{1,2,3}
sum(new int[]{4});  // 4   — pass an array directly

Exam trap

A method may have only one varargs parameter, and it must be last (f(String label, int... xs)). When a fixed-arity overload also matches, it wins over the varargs form. Passing null to a varargs parameter passes a null array (then iterating it throws NPE), not an empty one.

Object lifecycle & garbage collection

An object is created with new (allocated on the heap, fields default-initialized, then the constructor runs). It lives as long as it is reachable from a live reference. When the last reference is dropped — reassigned or goes out of scope — it becomes eligible for garbage collection; the JVM reclaims its memory at some unspecified later time.

java
StringBuilder sb = new StringBuilder("hi");
sb = null;          // the "hi" StringBuilder is now unreachable → eligible for GC

Gotcha

Java has no destructors and no manual delete. System.gc() is only a hint — you cannot force collection. finalize() is deprecated; for cleanup use try-with-resources / AutoCloseable (Module 04). An object is eligible only when no live reference reaches it (cycles among otherwise unreachable objects are still collected).

Immutable objects

An immutable object can't change after construction — making it inherently thread-safe and a safe map key. The recipe:

  1. mark the class final (no overriding subclass can add mutability),
  2. make all fields private final,
  3. provide no setters,
  4. defensively copy any mutable input in, and any mutable field out.
java
final class Names {
    private final List<String> names;
    Names(List<String> names) { this.names = List.copyOf(names); }   // copy in
    List<String> names() { return names; }                            // List.copyOf is unmodifiable
}

SDET note

Constructors, overloads, and immutability are where "looks right, compiles wrong" AI suggestions hide — a removed no-arg constructor, an ambiguous overload, or a leaked mutable field. A record (Lesson 05) gives you most of the immutable-class recipe for free. Let the compiler and a unit test, not a glance, confirm behavior.

Key Takeaways

  • A constructor names the class and has no return type; declaring any constructor removes the default no-arg one. this(...)/super(...) must be the first statement.
  • Access runs private → package-private → protectedpublic; encapsulation keeps fields private behind methods. A local/parameter shadows a field — qualify with this.
  • static members belong to the class (shared, no this); instance members belong to objects.
  • Init order: static fields/blocks once at class load, then per object the instance fields/blocks, then the constructor body.
  • Overloading resolves at compile time: exact → widening → boxing → varargs. A varargs parameter is an array, must be last, and there can be only one.
  • An object lives while reachable; dropping the last reference makes it GC-eligible. No destructors; finalize is deprecated; System.gc() is a hint.
  • Immutable = final class, private final fields, no setters, defensive copies in and out.

Lesson Quiz

Lesson Quiz · Classes & Objects0 / 10
  1. Which call fails to compile?

    class P {
        P(int x) { }
    }
    // ...
    new P(1);
    new P();
    • Anew P(1)
    • Bnew P()
    • CBoth
    • DNeither
  2. What is printed on the SECOND new Demo() call?

    static int s = log("S");
    static { log("SB"); }
    int i = log("I");
    { log("IB"); }
    Demo() { log("C"); }
    • AS, SB, I, IB, C
    • BI, IB, C
    • CS, SB, C
    • DI, C
  3. Which overload does f(5) call?

    void f(long x) {}
    void f(Integer x) {}
    void f(Object x) {}
    • Af(long)
    • Bf(Integer)
    • Cf(Object)
    • DAmbiguous
  4. Which member access level allows a subclass in ANOTHER package, but not unrelated code?

    • Aprivate
    • Bpackage-private (default)
    • Cprotected
    • Dpublic
  5. What does sum() followed by sum(1, 2, 3) return?

    static int sum(int... xs) {
        int t = 0; for (int x : xs) t += x; return t;
    }
    • A0 then 6
    • Berror then 6
    • C0 then 123
    • D0 then 3
  6. Which is TRUE about a varargs parameter?

    • AA method may have several varargs parameters
    • BIt must be the last parameter and there can be only one
    • CIt must come first
    • DIt cannot be combined with normal parameters
  7. After sb = null; what is true of the StringBuilder it referenced?

    StringBuilder sb = new StringBuilder("hi");
    sb = null;
    • AIt is immediately freed
    • BIt is now eligible for garbage collection
    • Cfinalize() runs synchronously
    • DIt causes a memory leak
  8. Which step is NOT part of making a class immutable?

    • AMark the class final
    • BMake fields private final
    • CProvide setters that validate input
    • DDefensively copy mutable inputs and outputs
  9. Where must a this(...) constructor call appear?

    • AAnywhere
    • BFirst statement of the constructor
    • CLast statement
    • DOnly in static methods
  10. Can a static method access an instance field directly?

    • AYes
    • BNo — it has no this
    • COnly if final
    • DOnly in the same package

Next: Inheritance & Polymorphism. Run the matching code in labs/src/main/java/com/jse21/m03_oop/.