Object. Practical Tasks
The Object class is the root of the Java class hierarchy, so every object has its equals(), hashCode(), and toString() methods. By default, equals() compares references, hashCode() ignores the fields, and toString() returns something like Book@1b6d3586. That is why value classes (a point, a book, a money amount) almost always override all three methods together.
This page has 3 hands-on exercises.
What to review before you start
The exercises build on three lessons: the Object class and its methods, the equals() method and its contract, and the toString() method. A quick cheat sheet:
| Method | What Object's implementation does | When to override |
|---|---|---|
equals(Object o) | Compares references, just like == | When objects with the same data must count as equal |
hashCode() | Returns a number unrelated to the fields | Always together with equals(): equal objects must have equal hash codes |
toString() | getClass().getName() + "@" + Integer.toHexString(hashCode()) | Almost always: for logs, debugging, and console output |
Exercise 1. Default Object methods
- Create a
Bookclass with the fieldstitle(String),author(String), andyear(int), plus a constructor for all three. Do not override anyObjectmethods. - Create two objects with identical data:
new Book("War and Peace", "Tolstoy", 1869). - For each object, print the object itself with
System.out.println(book), thenbook.hashCode()andbook.getClass().getName(). - Print the results of
book1 == book2andbook1.equals(book2). Then create a referenceBook book3 = book1;and comparebook1withbook3both ways. - In a code comment, explain every line of output: what the
Book@...string is made of, whyequals()returnedfalsefor identical books buttrueforbook3.
Hint: the hexadecimal number after @ in the println output matches Integer.toHexString(book.hashCode()). Verify it in code.
Exercise 2. Overriding toString() and inheritance
- Override
toString()inBookso it returns a string likeBook{title='War and Peace', author='Tolstoy', year=1869}. Add the@Overrideannotation. - Create a class
EBook extends Bookwith an extra fieldfileSizeMb(double). OverridetoString()usingsuper.toString()so you don't repeat the parent's fields. - Check three places where
toString()is called implicitly:System.out.println(book), the concatenation"Book: " + book, and printing aList<Book>withprintln. - Create a
Book[]array of three books. Print it withSystem.out.println(array)and withArrays.toString(array). Explain why the results differ. - Set a
Bookvariable tonulland compare the behavior ofSystem.out.println(book)andbook.toString(). Then printString.valueOf(book)andObjects.toString(book, "book not found").
Exercise 3. equals() vs ==: the Point class
- Create a
Pointclass withxandyfields (int) and overrideequals(): two points are equal when both coordinates match. Check in this order: same reference,null, object class, cast, field comparison. - Write a
mainmethod that checks every rule of theequals()contract and prints the result of each check:
a) reflexivity:p.equals(p);
b) symmetry:p1.equals(p2)andp2.equals(p1);
c) transitivity for three equal points;
d) comparing withnullreturnsfalsewithout an exception;
e) comparing with an object of another class, such as the string"1,2", returnsfalse. - Add a
String labelfield toPointthat may benull, and include it in the comparison withObjects.equals(label, other.label). Test the cases wherelabelisnullin one point and in both. - Compare two strings,
String a = "java";andString b = new String("java");, with==and withequals(). Then compare twoint[]arrays with the same contents usingequals()andArrays.equals(). Explain the results.
Hint: a method with the signature equals(Point other) is an overload, not an override: HashSet and List.contains() will never call it. The @Override annotation turns that mistake into a compilation error right away.
Common mistakes
- Overriding
equals()but forgettinghashCode(). Objects can no longer be found in aHashSetorHashMap, even though everything works in anArrayList. Always override the two methods as a pair. - Declaring
equals(Point other)instead ofequals(Object o). That's an overload: collections call the version fromObjectand compare references. The@Overrideannotation protects you. - Using different fields in
equals()andhashCode(). IfhashCode()uses a field thatequals()ignores, equal objects end up with different hash codes. Hash the same fields you compare (or a subset of them). - Comparing strings and other object fields with
==insideequals(). Reference-type fields needequals(), andObjects.equals()if the field can benull. - Calling
obj.toString()explicitly whenobjmay benull. You get aNullPointerException;println(obj), string concatenation, andString.valueOf(obj)safely printnull.
Before you submit a solution, check it against common Java code style conventions: meaningful names, one class per file, and no unused code.
Frequently Asked Questions
Do I need to override equals() and hashCode() in every class?
No. Override them in value classes that are compared by their data and stored in a HashSet or used as HashMap keys: a point, a money amount, a book's ISBN. Services, controllers, threads, and other objects with an “identity” are fine with the reference comparison inherited from Object. toString(), on the other hand, is worth overriding almost everywhere, for the sake of your logs.
Can hashCode() return a constant, like 42?
Technically yes: the contract isn't broken, because equal objects still get equal hash codes. But every object lands in the same bucket, so a HashMap degrades into a slow linear search: the more elements it holds, the slower each lookup gets. Use Objects.hash() over the same fields you compare in equals().
Should toString() include every field?
No. Leave out passwords, tokens, and personal data: toString() output often ends up in logs. Bidirectional links are dangerous too: if Order prints its Customer and the customer prints its list of Order objects, the calls loop until a StackOverflowError. For such fields, print only the ID.
Comments