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, andhashCodecorrectly - Respect the equals/hashCode contract
- Use wrapper classes and autoboxing
- Handle exact and huge numbers with
BigDecimalandBigInteger - Model "maybe a value" safely with
Optional - Lean on utility classes like
ObjectsandUUID
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 byprintlnand string concatenation)equals(Object)— logical equality (same content, not same object)hashCode()— an int used by hash-based collections to bucket objects
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
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 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
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:
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 constantsWatch out
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:
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)); // exactTip
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:
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
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 identifierRandom/ThreadLocalRandom— random numbers; orMath.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.