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.