In the realm of Java programming, the java.lang.Object
class holds a position of utmost importance. It serves as the root of the class hierarchy, making it a foundational element of the Java language. Understanding the Object
class is crucial for every Java developer, as it forms the basis for many core concepts and functionalities within the language.
Understanding the java.lang Package
For any Java program, the most commonly required classes and interfaces are grouped together into a separate single package known as the java.lang
package. We are not required to import the java.lang
package because all classes and interfaces in this package are by default available to all Java classes.
Java Object Class (java.lang.Object
)
The most commonly required methods for any Java class are grouped into a single class called the Object class. It is also the root class for any Java class. If any Java class does not extend any other Java class, then it is a direct child class of the Object class; otherwise, it is an indirect child class of the Object class. When it is an indirect child of the Object class, it is multi-level inheritance, not multiple inheritance.
For example, class A extends B
i.e., A --> B --> Object
. This is multilevel inheritance and not multiple inheritance.
Whether directly or indirectly, Java does not support multiple inheritance with respect to Java classes.
The Object class defines the following 11 methods:
public String toString()
public native int hashCode()
public boolean equals(Object o)
protected native Object clone() throws CloneNotSupportedException
protected void finalize() throws Throwable
public final Class getClass()
public final void wait() throws InterruptedException
public final native void wait(long ms) throws InterruptedException
public final native void wait(long ms, int ns) throws InterruptedException
public native final void notify()
public native final void notifyAll()
Note: The Object class contains a 12th method, but it is for internal purposes for the JVM: private static native void registerNatives
.
toString()
: This method is used to get the string representation of an object. If our class does not contain toString()
, then the toString()
method of the Object class will be executed. The Object class toString()
method is: public String toString(){return getClass().getName() + "@" + Integer.toHexString(hashCode());}
. For example, classname@hashcode-in-hexadecimal-form
, i.e., Student@1888759
.
Based on our requirement, we can override toString()
in any Java class, and it is highly recommended. For example: public String toString(){return name + "..." + rollno;}
.
hashCode()
: Based on the object’s address, the JVM will generate a unique number called the hash code of that object. It doesn’t mean the object’s address is the hash code of that object. The JVM will generate a hash code for hashing-related data structures like HashTable, HashMap, HashSet to store objects into buckets based on their hash codes so that searching for that object becomes very efficient. As we know, the number one searching algorithm is hashing, and its time complexity is O(1).
public native int hashCode();
We can override the hashCode()
method of the Object class according to our requirement. Suppose we want to override hashCode()
in the Student class; then, it will return the unique roll number of the student. This is the proper way to implement the hashCode
method.
equals()
: For example, obj1.equals(obj2)
.
If our class doesn’t contain equals()
, then the equals()
method of the Object class will be executed. The equals()
method of the Object class is built for reference comparison, not for content comparison.
For example:
Student s1 = new Student("amol", 101);
Student s2 = new Student("softAai", 102);
Student s3 = new Student("amol", 101);
Student s4 = s1;
System.out.println(s1.equals(s2)); // false, as the Object class equals() is built for reference comparison
System.out.println(s1.equals(s3)); // false, even though the content is equal, the references are different
System.out.println(s1.equals(s4)); // true, as both references are pointing to the same object, meaning the references are equal
Now, based on our requirement, we can override the equals()
method of the Object class for content comparison into our own class. But while doing this, we need to take care of the following things:
- Determine whether we are comparing the whole content (student name, roll number) or only specific content (only roll number), and implement our
equals()
accordingly. - Ensure that if we pass a different object, it will not raise a ClassCastException, and if we pass null, it won’t raise a NullPointerException. In both cases, we need to handle them by returning false.
In the case of Strings
String s1 = new String("softAai");
String s2 = new String("softAai");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
// In the case of StringBuffer:
StringBuffer sb1 = new StringBuffer("softAai");
StringBuffer sb2 = new StringBuffer("softAai");
System.out.println(sb1 == sb2); // false
System.out.println(sb1.equals(sb2)); // false
“==” is built for reference comparison. In the case of the String class, the equals()
method is overridden for content comparison. Therefore, even though the objects are different, if the content is the same, equals()
returns true. However, for StringBuffer, the equals()
method is not overridden for content comparison, so if the objects are different, equals()
returns false even if the content is the same. Its behavior is like the equals()
method of the Object class, built for reference comparison.
getClass() is used to get the runtime class definition of an object so that we can access class-level properties like class name, declared methods, and constructors. The signature is: public final Class getClass()
. We can utilize the Reflection API for this purpose from java.lang.reflection.*
.
The finalize()
method is called just before an Object is destroyed by the garbage collector to perform cleanup activities. Once the finalize method completes, the garbage collector automatically destroys that object.
Other methods like wait()
, notify()
, and notifyAll()
are discussed in more detail in multithreading.
We can use these methods for inter-thread communication. The thread that is expecting an update is responsible for calling wait()
, which immediately puts the thread into a waiting state. The thread responsible for performing the update can then call notify()
. The waiting thread will receive that notification and continue its execution with those updates.
ay to create string objects from different sources such as literals, StringBuffer objects, char arrays, or byte arrays, offering flexibility in string manipulation and handling in Java.
Conclusion
Empower your Java programming journey by mastering the Object class and its myriad functionalities. With our comprehensive guide, you’ll gain a deeper understanding of Java’s foundational concepts and learn how to harness the full potential of the Object class for building robust and scalable applications. Start exploring today and elevate your Java programming skills to new heights!