Java Object Class
Table of Contents + −
In the last lesson you learned about Java abstract classes. Now here is a surprising fact: every class you write already has methods you never wrote, like toString and equals. They come from a hidden parent called Object. Let’s learn it.
🤔 The problem: where do these methods come from?
Write a tiny class with nothing in it, and it still has methods you can call.
class Student {}
public class Main { public static void main(String[] args) { Student s = new Student(); System.out.println(s.toString()); // works! but where is toString defined? }}- No fields, no methods. It looks completely empty.
- Yet
toStringruns. So doequalsandhashCode. - You never wrote them. So who did?
The class is not really empty. Java quietly gave it a parent, and that parent already had these methods.
🌳 Object is the root of every class
Object is a built-in class that sits at the very top of the class family tree.
- Its full name is
java.lang.Object. Every other class sits below it. - If a class extends nothing, Java makes it extend
Objectfor you. - You never type
extends Object. Java adds it. - Even
Dog extends AnimalreachesObject, becauseAnimalextendsObject.
So the two lines below mean the same thing.
class Student { // you write this}
class Student extends Object { // Java treats it as this}One root for everything
Every class you ever use or write, including String, ArrayList, and your own classes, traces back to java.lang.Object. There is exactly one root, and this is it.
🧰 The methods every object inherits
Every object comes with a small set of ready-made methods.
toString()gives a text version of the object.equals(Object other)checks if this object is “equal” to another one.hashCode()gives anintused by hash-based collections likeHashMap.getClass()tells you the real class of the object at runtime.clone()makes a copy of the object.finalize()is deprecated, which means it is old and you should not use it.wait(),notify(),notifyAll()help threads coordinate. You meet them in the multithreading lessons.
The first three matter most. Each gets its own lesson next.
🖨️ The default toString prints a cryptic string
When you print an object, Java calls its toString to turn it into text. The default one looks strange.
class Student { String name;
Student(String name) { this.name = name; }}
public class Main { public static void main(String[] args) { Student s = new Student("Alex"); System.out.println(s); // calls s.toString() automatically }}You might hope this prints “Alex”. It does not.
Output
Student@1b6d3586- It prints the class name, then
@, then the hash code in hexadecimal. - It knows nothing about your fields. So no name shows up.
- The code after
@changes each time you run the program. - It is correct, but useless for a human.
That is why you usually write your own toString. We do that in the next lesson.
⚖️ The default equals checks identity, not contents
The default equals has a narrow idea of equal. It returns true only when both references point to the very same object in memory. This is called reference identity.
class Student { String name;
Student(String name) { this.name = name; }}
public class Main { public static void main(String[] args) { Student a = new Student("Alex"); Student b = new Student("Alex"); Student c = a;
System.out.println(a.equals(b)); // false System.out.println(a.equals(c)); // true }}Output
falsetrueaandblook equal to a human, butequalssays false. They are two separate objects.a.equals(c)is true becausec = apoints at the same object.- Same contents do not count. Only the same object counts.
You usually want same-details to count as equal. That is why classes override equals. It has its own lesson next.
== and default equals do the same thing
For objects, == checks if two references point to the same object. The default equals does the same check. They only start to differ once you override equals to compare contents instead.
🔎 getClass tells you the real type
The getClass() method returns the actual class of your object at runtime. The common use is asking for the class name:
public class Main { public static void main(String[] args) { String text = "hello"; Integer number = 42; int[] numbers = {1, 2, 3};
System.out.println(text.getClass().getName()); System.out.println(number.getClass().getName()); System.out.println(numbers.getClass().getName()); }}Output
java.lang.Stringjava.lang.Integer[IgetName()gives the full name including the package.- The array prints
[I, Java’s short code for “array of int”. - getClass always reports the true type, even through a general reference.
- That makes it handy for logging and debugging.
⬆️ Any object can be stored in an Object reference
Because every class is a kind of Object, you can store any object in an Object variable. Moving an object into a more general reference type is called upcasting.
public class Main { public static void main(String[] args) { Object a = "a piece of text"; // a String is an Object Object b = 100; // an Integer is an Object Object c = new java.util.ArrayList<>(); // a list is an Object
System.out.println(a.getClass().getName()); System.out.println(b.getClass().getName()); System.out.println(c.getClass().getName()); }}Output
java.lang.Stringjava.lang.Integerjava.util.ArrayList- The variable type is
Object, butgetClassstill reports the true type. - This is why the old
ArrayListcould hold anything before generics. It stored everything asObject. - It is also why a method that takes an
Objectparameter accepts any object.
🧠 Why you override toString, equals, and hashCode
The default behavior is very basic, so we often replace these three.
- Override
toStringso printing shows useful details, notStudent@1b6d3586. - Override
equalsso same-contents objects count as equal. - Override
hashCodewithequals, becauseHashMapandHashSetrely on both agreeing.
Each gets a full lesson next. Object gives you a working version for free. You improve it when the default falls short.
⚠️ Common Mistakes
A few Object misunderstandings trip up almost everyone.
- Relying on the default toString. Printing an object and expecting readable details gives you the cryptic code instead.
System.out.println(student); // ❌ prints Student@1b6d3586// ✅ override toString in Student to return the name and marks- Relying on the default equals. Comparing two objects with the same contents returns false unless you override
equals.
new Student("Alex").equals(new Student("Alex")); // ❌ false by default// ✅ override equals to compare the fields you care about- Forgetting that everything is an Object. Some learners think only “special” classes extend
Object. Every class does, including your own and the ones in the library.
class Animal { } // ❌ thinking this has no parentclass Animal extends Object { } // ✅ this is what Java actually does for you- Overriding equals but not hashCode. These two must move together, or hash-based collections behave strangely. The hashCode lesson explains why.
✅ Best Practices
Good habits when working with Object and its methods.
- Override
toStringon classes you print. It makes logs and debugging far easier than reading@1b6d3586codes. - Override
equalswhen “same contents” should mean equal. Do not depend on the default identity check for value-like classes. - Always override
hashCodealongsideequals. Keep the two in agreement soHashMapandHashSetwork correctly. - Use
getClass().getName()for clear logging. It is a quick, reliable way to see what an object really is. - Do not use
finalize. It is deprecated and unreliable. Modern Java has better ways to release resources.
🧩 What You’ve Learned
Nicely done. Let’s recap the Object class.
- ✅ Object (
java.lang.Object) is the root of every class. If a class extends nothing, it extendsObjectautomatically. - ✅ Every object inherits
toString,equals,hashCode,getClass,clone, the deprecatedfinalize, and the thread methodswait/notify/notifyAll. - ✅ The default
toStringprintsClassName@hexhash, and the defaultequalschecks reference identity, not contents. - ✅
getClass().getName()reports an object’s real type at runtime. - ✅ Any object can be upcast to an
Objectreference, which is why one general type can hold them all. - ✅ You usually override
toString,equals, andhashCodeto fit your own class.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What is the parent of a class that does not extend anything?
Why: Any class without an explicit parent automatically extends java.lang.Object, the root of the hierarchy.
- 2
What does the default toString return?
Why: The inherited toString returns ClassName@hexhash, which is why you override it for readable output.
- 3
What does the default equals compare?
Why: By default, equals checks reference identity, the same as ==, so two separate objects are not equal even with identical fields.
- 4
Why can any object be stored in an Object variable?
Why: Since Object is the root of all classes, every object is also an Object, so upcasting to an Object reference always works.
🚀 What’s Next?
You now know that every object has a toString, and that the default version is unreadable. The natural next step is to fix that. Let’s learn how to write your own toString so your objects print clean, meaningful text.