Checkpoint: Objects & Data

You've learned switch , wrapper classes , access modifiers , packages , dates , Comparable/Comparator and records — let's combine them. This checkpoint has no new theory; it's where the last seven lessons click together into something you build yourself.

Learn Checkpoint: Objects & Data in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

A quick recap of the toolkit you're about to wield together:

Map one value to a result with the arrow form: case "A" -> 3; — no break, returns a value.

Integer , Double … box primitives for collections; compare with .equals() , parse with parseInt .

private by default; expose only what callers need. Helpers stay hidden.

Namespaces for classes; import to borrow types; java.lang is automatic.

Immutable LocalDate ; measure gaps with ChronoUnit.DAYS.between .

One natural order via compareTo ; many ad-hoc orders via Comparator.comparing(...) .

One line gives immutable fields, accessors, and equals/hashCode/toString — the perfect data model to sort and print.

First a short warm-up that stitches a few features together so you can see them cooperate. Then the main event: a multi-step build challenge with a starter scaffold. Try it yourself before revealing the solution, and finish with a quick quiz to confirm it all stuck.

Read this before running it. Notice how a record models each sale, a Comparator orders them, and a switch expression picks a weight — three lessons in twenty lines.

Build a small library catalogue that brings together a record , access modifiers , a switch expression , a Comparator , a LocalDate calculation, and clean formatted output. Work through the four numbered steps in the starter. Don't reveal the solution until you have a running version (or you're truly stuck).

1. Declare record Loan(String title, String category, LocalDate due) .

2. A private daysLeft(loan, today) using ChronoUnit.DAYS.between .

3. A private fee(category) using a switch expression.

4. In main : build the list, sort by due date with a Comparator, print each line.

Here's one clean way to wire it all together. Notice the helpers are private static (access modifiers), the data is a record , fee is a switch expression, the list is ordered by a Comparator keyed on Loan::due , and daysLeft uses ChronoUnit .

The list is built with Atlas/Algorithms/Dune, but Comparator.comparing(Loan::due) sorts by due date ascending: 20 June (Algorithms), 25 June (Dune), 7 July (Atlas). The day counts come from ChronoUnit.DAYS.between(today, due) with today = 17 June, giving 3, 8, and 20 days.

Answer each in your head, then reveal. Six questions across all seven topics.

Answer: It returns a value you can assign to a variable, running only the matching branch with no fall-through. A single-expression branch ( case A -> 5; ) returns automatically; a multi-line branch uses a {' '} block ending in yield value; to hand the value back.

Answer: 1000 is outside Java's Integer cache (-128..127), so a and b are separate objects and == compares references. Use a.equals(b) to compare wrapper values — it's always correct.

Answer: Only classes in the same package — that's package-private (the default), not public. It's narrower than protected , which also reaches subclasses in other packages.

Answer: Still 2026-01-01 . LocalDate is immutable, so plusDays returns a new date that's thrown away here. You must capture it: d = d.plusDays(10); .

Answer: Reverse the primary (score) comparator for descending, then chain thenComparing(Player::name) for the alphabetical tie-breaker. The tie-breaker is consulted only when scores are equal.

Answer: Any three of: private final fields x / y ; a canonical constructor; accessors x() and y() (no "get" prefix); and value-based equals() , hashCode() , and toString() . All immutable, all from one line.

You just combined seven separate topics — switch, wrapper classes, access modifiers, packages, dates, Comparable/Comparator, and records — into one working program. That's a genuine milestone: you're no longer learning isolated features, you're assembling them into software.

Next up: Methods — the next building block, where you'll package logic into reusable, parameterised units.

Practice quiz

A switch EXPRESSION (the arrow form, case A -> 5;) primarily lets you:

  • Fall through to the next case
  • Loop over values
  • Return a value you can assign, running only the matching branch
  • Replace a for loop

Answer: Return a value you can assign, running only the matching branch. A switch expression returns a value with no fall-through; a multi-line branch uses a block ending in yield.

Why is Integer a = 1000, b = 1000; a == b false?

  • 1000 is outside the Integer cache (-128..127), so a and b are different objects
  • == always returns false for Integer
  • 1000 is too large for an Integer
  • Autoboxing is disabled

Answer: 1000 is outside the Integer cache (-128..127), so a and b are different objects. Values outside -128..127 are not cached, so == compares two distinct references. Use a.equals(b).

For comparing wrapper values like Integer reliably, you should use:

  • ==
  • the - operator
  • compareTo only
  • .equals()

Answer: .equals(). .equals() compares the wrapped values and is always correct; == may compare references for uncached values.

A field written with NO access keyword is visible to:

  • Everyone (public)
  • Only classes in the same package (package-private)
  • Subclasses anywhere
  • Only the same class

Answer: Only classes in the same package (package-private). No keyword means package-private — same package only. That is narrower than protected.

After LocalDate d = LocalDate.of(2026,1,1); d.plusDays(10); what is d?

  • Still 2026-01-01
  • 2026-01-11
  • A NullPointerException
  • 2026-01-10

Answer: Still 2026-01-01. LocalDate is immutable. plusDays returns a NEW date that's discarded here; you must write d = d.plusDays(10).

Which measures whole days between two LocalDates?

  • today.minus(due)
  • due - today
  • ChronoUnit.DAYS.between(today, due)
  • Period.now()

Answer: ChronoUnit.DAYS.between(today, due). ChronoUnit.DAYS.between(start, end) returns the whole-day gap between two temporal values.

To sort by score DESCENDING, breaking ties by name ascending, you write:

  • Comparator.comparing(Player::name).reversed()
  • Comparator.comparingInt(Player::score).reversed().thenComparing(Player::name)
  • Comparator.comparingInt(Player::score).thenComparing(Player::name).reversed()
  • Comparator.naturalOrder()

Answer: Comparator.comparingInt(Player::score).reversed().thenComparing(Player::name). Reverse the score comparator for descending, then chain thenComparing(name) for the ascending tie-breaker.

Comparable vs Comparator: which is correct?

  • A type can have many Comparable orders
  • Comparator must be implemented by the class itself
  • They are interchangeable in every case
  • Comparable defines one natural order via compareTo; Comparator gives many external orders

Answer: Comparable defines one natural order via compareTo; Comparator gives many external orders. Comparable is the single natural order baked into the class; Comparators are external and you can have many.

For a record Point(int x, int y), the accessor for x is called:

  • getX()
  • x()
  • getx()
  • valueX()

Answer: x(). Records generate accessors named after the component with no 'get' prefix, so it is x(), not getX().

Which does a record Point(int x, int y) {} NOT automatically generate?

  • A canonical constructor
  • equals() and hashCode() by value
  • Mutable setters like setX()
  • toString()

Answer: Mutable setters like setX(). Records are immutable — no setters. They give you final fields, a constructor, accessors, equals/hashCode/toString.