Phase 3 · Core JavaModule 15~38 min read

Object Class & Essential APIs

The methods every object inherits, wrapper types, big numbers, and Optional.

What you'll learn

Every class in Java secretly inherits from Object, and a handful of its methods — plus a few essential utility types — appear in virtually every program. This module covers the "glue" APIs that make objects work correctly in collections and beyond.

By the end you'll be able to:

  • Override toString, equals, and hashCode correctly
  • Respect the equals/hashCode contract
  • Use wrapper classes and autoboxing
  • Handle exact and huge numbers with BigDecimal and BigInteger
  • Model "maybe a value" safely with Optional
  • Lean on utility classes like Objects and UUID

The Object class

Object is the root of every class hierarchy — if you don't extend anything, you implicitly extend Object. It provides methods every object has, and the three you'll override most are:

  • toString() — a readable text description (used by println and string concatenation)
  • equals(Object) — logical equality (same content, not same object)
  • hashCode() — an int used by hash-based collections to bucket objects
Point.java
import java.util.Objects;

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }

    @Override
    public String toString() { return "Point(" + x + ", " + y + ")"; }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Point p)) return false;
        return x == p.x && y == p.y;
    }

    @Override
    public int hashCode() { return Objects.hash(x, y); }
}

public class Main {
    public static void main(String[] args) {
        Point a = new Point(1, 2);
        Point b = new Point(1, 2);
        System.out.println(a);                 // uses toString
        System.out.println(a.equals(b));       // true (same content)
        System.out.println(a.hashCode() == b.hashCode());
    }
}

Note

The default toString prints something like Point@1b6d3586, and the default equals checks identity (same object). Override them to give your objects meaningful behaviour — or use a record (Module 10), which generates all three for you.

The equals & hashCode contract

These two methods have a binding relationship. Break it and your objects will mysteriously fail inside HashMap and HashSet:

The contract

The rule

If a.equals(b) is true, then a.hashCode() must equal b.hashCode().

Break it, and…

Your objects misbehave in HashMap and HashSet — lookups fail, duplicates slip in. Always override both together.

Always override them together

If you override equals, you must override hashCode too — and base both on the same fields. Objects.hash(...) makes writing a correct hashCode easy.

Wrapper classes & autoboxing

Primitives (int, double…) aren't objects, so they can't go directly into collections, which hold objects. Each primitive has a wrapper class (Integer, Double, Boolean…). Java converts between them automatically — autoboxing (primitive → wrapper) and unboxing (the reverse). Wrappers also carry handy utilities like parseInt and MAX_VALUE:

Wrappers.java
int primitive = 42;
Integer boxed = primitive;      // autoboxing: int -> Integer
int back = boxed;               // unboxing: Integer -> int

Integer parsed = Integer.parseInt("100");
System.out.println(boxed + parsed);        // 142
System.out.println(Integer.MAX_VALUE);     // useful constants

Watch out

Autoboxing is convenient but not free — boxing millions of values in a tight loop costs performance, and a null Integer will throw NullPointerException when unboxed. Use primitives for heavy numeric work.

BigDecimal & BigInteger

double can't represent decimals like 0.1 exactly, which is disastrous for money. BigDecimal gives exact decimal arithmetic; BigInteger handles integers beyond long's range. Both are immutable, so operations return a new value:

BigNumbers.java
System.out.println(0.1 + 0.2);      // floating-point surprise!

import java.math.BigDecimal;
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));       // exact

Tip

For anything involving money, use BigDecimal — and always construct it from a String (new BigDecimal("0.1")), never a double, to avoid inheriting the double's imprecision.

Optional

Optional is a container that may or may not hold a value — a clear, null-safe way to say "this might be absent." Returning an Optional instead of null forces callers to consider the empty case, cutting down on NullPointerExceptions:

Optionals.java
import java.util.Optional;

Optional<String> found = Optional.of("Sara");
Optional<String> empty = Optional.empty();

System.out.println(found.isPresent());       // true
System.out.println(found.orElse("Unknown")); // Sara
System.out.println(empty.orElse("Unknown")); // Unknown
found.ifPresent(name -> System.out.println("Hi " + name));

Note

Prefer orElse, orElseGet, map, and ifPresent over calling get() directly (which throws if empty). Use Optional as a return type, not for fields or parameters.

Handy utility classes

  • Objects — null-safe helpers: Objects.equals(a, b), Objects.hash(...), Objects.requireNonNull(x)
  • UUID — UUID.randomUUID() generates a unique identifier
  • Random / ThreadLocalRandom — random numbers; or Math.random() for a quick double

Recap & quick check

Key takeaways

  • Every class extends Object; override toString, equals, and hashCode for meaningful behaviour.
  • If equals returns true for two objects, their hashCodes must match — override both together.
  • Wrapper classes let primitives act as objects; autoboxing converts automatically.
  • Use BigDecimal (from a String) for exact decimal/money math; double is imprecise.
  • Optional models a maybe-absent value and reduces NullPointerExceptions.

Quick check

1. If you override equals(), what else must you override?

2. What is autoboxing?

3. Which type should you use for exact money calculations?

4. What problem does Optional help avoid?

5. What does the default toString() return?

Great — you now understand the methods and types that make objects behave well everywhere. Next up: Module 16 — the Date & Time API.