The Objects class in Java
equals, hash, requireNonNull, toString with default, isNull.
equals method works perfectly — until one object has a null name and the whole HashMap lookup crashes. One helper class makes this whole category of crash disappear.A null-safe toolbox
**java.util.Objects** is full of static helpers that handle null for you. **Objects.equals(a, b)** returns true if both are null, false if only one is, and otherwise calls a.equals(b) — you never call a method on null.
Comparing with nulls
What does this print?
String a = null;
String b = null;
System.out.println(Objects.equals(a, b));
System.out.println(Objects.equals(a, "x"));true falsefalse falseThrows NullPointerException
Show the answer
true — two nulls count as equal. false — only one is null. No exception, because Objects.equals never calls a method on null.
Writing equals by hand
return age == p.age
&& name.equals(p.name);If this.name is null, name.equals(...) throws NullPointerException.
return age == p.age
&& Objects.equals(name, p.name);Handles null on either side.
Hashing fields
**Objects.hash(f1, f2, …)** combines fields into one hash code — the perfect partner for equals. Records generate both for you.
@Override
public int hashCode() {
return Objects.hash(name, age);
}Fail fast with requireNonNull
**Objects.requireNonNull(x, "msg")** throws a NullPointerException with your message right where the bad value enters — instead of failing later at some confusing spot.
Pet(String name) {
this.name = Objects.requireNonNull(
name, "name is required");
}Friendly fallbacks
What does this print?
String n = null;
System.out.println(
Objects.toString(n, "n/a"));
System.out.println(
Objects.requireNonNullElse(n, "guest"));null guestn/a guestThrows NullPointerException
Show the answer
**toString(o, default) returns the default text for null, and requireNonNullElse(x, d)** returns d when x is null.
Predicates for streams
**Objects::isNull and Objects::nonNull** are ready-made predicates — filter(Objects::nonNull) is a common idiom to drop nulls from a stream.
List<String> clean = Stream.of("a", null, "b")
.filter(Objects::nonNull)
.toList(); // [a, b]Defensive code that reads well
Constructors that start with requireNonNull checks turn mysterious NPEs deep in business logic into clear errors at the boundary, naming the culprit. Most hand-written equals/hashCode in production uses Objects.equals and Objects.hash.
Key takeaways
- Objects.equals(a, b) is safe even when a is null
- Objects.hash(f1, f2…) combines fields for hashCode()
- requireNonNull(x, "msg") throws NPE early with a message
- Objects::isNull and Objects::nonNull work as Predicates
Since Java 14, NullPointerException messages are "helpful": they name exactly what was null, like Cannot invoke "String.length()" because "name" is null.
Practice questions
What does this print?
String a = null;
System.out.println(Objects.equals(a, "x"));
System.out.println(Objects.toString(a, "n/a"));- false null
- false n/a
- Throws NullPointerException
- true n/a
Check your answer
false n/a. Objects.equals handles the null safely and returns false. Objects.toString returns the default text when the object is null.
What does this print?
String name = null;
String n = Objects.requireNonNull(
name, "name is required");
System.out.println(n.length());- 0
- null
- Throws NullPointerException
- Compile error
Check your answer
Throws NullPointerException. requireNonNull throws a NullPointerException with your message right where the bad value enters, instead of failing later at some confusing spot.