Java Object Class Source Code Analysis
The Object class in Java is the root of all classes; every other class inherits from it, either directly or indirectly. The Object class contains several fundamental methods and attributes that can be inherited and used by all subclasses. Below are some important methods and attributes within the Object class:
- equals(Object obj) method: Used to determine if two objects are equal. By default, the
equals()method compares object references (addresses), determining whether two objects refer to the same instance. Subclasses can override this method to implement custom equality logic. - hashCode() method: Returns a hash code value for the current object. A hash code is an integer value that allows for efficient comparison of whether two objects are equal. If two objects'
equals()methods return true, their hash codes must be identical. Therefore, if you override theequals()method, you must also override thehashCode()method. - toString() method: Returns a string representation of the current object. If this method is not overridden, it returns a string containing the class name and the hash code of the object.
- getClass() method: Returns the Class object representing the type of the executing thread or the current object's runtime type (in modern Java contexts). Specifically for
Object, it returns the actual runtime class of the instance. - wait(), notify(), notifyAll(): Methods used for inter-thread communication, enabling threads to wait and be awakened.
- clone() method: Used to create a new object that is an exact copy (shallow clone) of this object. It creates a new object, copies all attributes from the current object into it, and returns the reference to the new object.
In addition to these methods, the Object class also includes methods such as finalize() and getClassLoader(). Since all classes inherit from the Object class, its methods can be invoked on any object instance. Access permissions for an object's attributes and methods are determined by the class of which it is a member; therefore, subclasses can access protected and public members defined in their parent class but cannot access private members.
Object Class Source Code
public class Object {
private static native void registerNatives();
// Static initializer block to ensure natives are registered before the object is used
static {
registerNatives();
}
public final native Class<?> getClass();
/**
* Returns a hash code value for this object.
*/
public native int hashCode();
/**
* Indicates whether some other object is "equal to" this one.
* By default, checks if the reference argument refers to the same object as this object.
*/
public boolean equals(Object obj) {
return (this == obj);
}
protected native Object clone() throws CloneNotSupportedException;
/**
* Returns a string representation of the object.
* Format: "ClassName@hashcode"
*/
@Override // Implicitly available in modern Java, but good to note behavior here
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
/**
* Wakes up a single waiting thread.
*/
public final native void notify();
/**
* Wakes up all threads that are waiting on this object's monitor.
*/
public final native void notifyAll();
/**
* Causes the current thread to wait in a timed manner for another thread to acquire its lock, or until some other condition is signaled by calling one of the {@link #notify()} and {@link #notifyAll()} methods on this object.
*/
public final native void wait(long timeout) throws InterruptedException;
/**
* Causes the current thread to wait in a timed manner for another thread to acquire its lock, or until some other condition is signaled by calling one of the {@link #notify()} and {@link #notifyAll()} methods on this object.
*/
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// Validate nanosecond range: must be between 0 and 999,999 inclusive.
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
// Convert nanoseconds to milliseconds for the native wait method call.
// If nanos is non-zero, increment timeout by one millisecond (approximate conversion logic).
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
/**
* Causes the current thread to wait in an untimed manner for another thread to acquire its lock.
*/
public final void wait() throws InterruptedException {
// Calls the timed version with a zero timeout, effectively blocking indefinitely until notified or interrupted.
wait(0);
}
/**
* Invoked by default when an object is garbage collected and there are no longer any references to it.
* This method can be overridden in subclasses to perform cleanup tasks before the object's memory is reclaimed.
*/
protected void finalize() throws Throwable { }
}
Overriding the equals Method of Object Class
In Java, overriding the equals() method ensures that when comparing two objects, their content is compared rather than just their reference addresses (memory locations). The general steps for overriding the equals() method are as follows:
- Check if the passed-in object is null; if so, return false.
- Check if the passed-in object refers to the same instance as the current object; if true, return true.
- Verify that the passed-in object's type matches the expected class (or a superclass/subclass relationship exists); otherwise, return false.
- Cast the passed-in object to the appropriate type and compare their attribute values for equality. If all attributes match, return true; otherwise, return false.
The following example code demonstrates how to override the equals() method:
public class Person {
private String name;
private int age;
// Constructor, getters, and setters would be defined here
/**
* Determines if this person is equal to another object.
*/
@Override
public boolean equals(Object obj) {
// Step 1: Check for null reference
if (obj == null) {
return false;
}
// Step 2: Check for self-reference optimization
if (this == obj) {
return true;
}
// Step 3: Type check - ensure the object is an instance of Person or a subclass thereof
if (!(obj instanceof Person)) {
return false;
}
// Cast to Person type for further comparison
Person other = (Person) obj;
// Step 4: Compare attribute values
boolean nameMatch = this.name.equals(other.name);
boolean ageMatch = this.age == other.age;
return nameMatch && ageMatch;
}
}
In this example code, we first check if the passed-in object is null. Next, we verify if it refers to the same instance as the current object (optimization). Finally, we cast the passed-in object to the Person type and compare their attributes (name and age). If all attributes match, true is returned; otherwise, false is returned.