Java Object Class
## Introduction to the Java Object Class
In Java, the `Object` class is the root of the class hierarchy. Every class in Java directly or indirectly inherits from the `Object` class. If a class does not explicitly extend another class, it implicitly extends `Object` by default.
Because every class is a subclass of `Object`, all Java classes inherit its non-private methods. The `Object` class is located in the `java.lang` package, which is automatically imported by the compiler into every Java source file.
---
## Inheritance Syntax
You can inherit from the `Object` class either explicitly or implicitly. Both of the following declarations are functionally identical:
### Explicit Inheritance
```java
public class YouTipDemo extends Object {
// Class body
}
```
### Implicit Inheritance (Recommended)
```java
public class YouTipDemo {
// Class body
}
```
---
## Constructor of the Object Class
The `Object` class provides a single, default constructor:
| Constructor | Description |
| :--- | :--- |
| `Object()` | Constructs a new instance of the `Object` class. |
---
## Methods of the Object Class
The `Object` class defines several fundamental methods that are crucial for object lifecycle management, comparison, cloning, and multithreading.
| # | Method Signature | Description |
| :--- | :--- | :--- |
| 1 | `protected Object clone()` | Creates and returns a shallow copy of this object. |
| 2 | `boolean equals(Object obj)` | Indicates whether some other object is "equal to" this one. |
| 3 | `protected void finalize()` | **Deprecated.** Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. |
| 4 | `Class> getClass()` | Returns the runtime class of this `Object`. |
| 5 | `int hashCode()` | Returns a hash code value for the object. |
| 6 | `void notify()` | Wakes up a single thread that is waiting on this object's monitor. |
| 7 | `void notifyAll()` | Wakes up all threads that are waiting on this object's monitor. |
| 8 | `String toString()` | Returns a string representation of the object. |
| 9 | `void wait()` | Causes the current thread to wait until another thread invokes `notify()` or `notifyAll()` for this object. |
| 10 | `void wait(long timeout)` | Causes the current thread to wait until either another thread invokes `notify()` / `notifyAll()`, or a specified amount of real time has elapsed. |
| 11 | `void wait(long timeout, int nanos)` | Similar to `wait(long timeout)`, but offers finer-grained control over the timeout period in nanoseconds (range: 0-999999). |
---
## Code Examples
Here are practical examples demonstrating how to override and use the most common `Object` methods: `toString()`, `equals()`, and `hashCode()`.
### 1. Overriding `toString()`, `equals()`, and `hashCode()`
```java
import java.util.Objects;
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Overriding toString() to provide a meaningful string representation
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
// Overriding equals() to compare object states instead of memory addresses
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return age == user.age && Objects.equals(name, user.name);
}
// Overriding hashCode() to maintain the contract: equal objects must have equal hash codes
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public static void main(String[] args) {
User user1 = new User("Alice", 25);
User user2 = new User("Alice", 25);
User user3 = new User("Bob", 30);
// Test toString()
System.out.println("User 1: " + user1); // Output: User{name='Alice', age=25}
// Test equals()
System.out.println("user1 equals user2: " + user1.equals(user2)); // Output: true
System.out.println("user1 equals user3: " + user1.equals(user3)); // Output: false
// Test hashCode()
System.out.println("user1 hashCode: " + user1.hashCode());
System.out.println("user2 hashCode: " + user2.hashCode()); // Will match user1's hashCode
}
}
```
### 2. Using `getClass()`
The `getClass()` method is used to retrieve the runtime class of an object, which is highly useful in reflection and debugging.
```java
public class GetClassDemo {
public static void main(String[] args) {
Object obj = "Hello, YouTip!";
// Get the runtime class of the object
Class> clazz = obj.getClass();
System.out.println("Class Name: " + clazz.getName());
// Output: Class Name: java.lang.String
}
}
```
---
## Key Considerations
1. **The `equals` and `hashCode` Contract**:
If you override the `equals()` method, you **must** override the `hashCode()` method as well. If two objects are equal according to the `equals(Object)` method, calling `hashCode()` on each of them must produce the same integer result. Failing to do so will cause unexpected behavior in hash-based collections like `HashMap`, `HashSet`, and `Hashtable`.
2. **Default `toString()` Behavior**:
The default implementation of `toString()` in the `Object` class returns a string consisting of the class name, an "@" symbol, and the unsigned hexadecimal representation of the hash code of the object (e.g., `java.lang.Object@15db9742`). It is highly recommended to override this method in your custom classes to provide readable, diagnostic output.
3. **Deprecation of `finalize()`**:
The `finalize()` method has been deprecated starting from Java 9 and removed in later versions because it can lead to performance issues, deadlocks, and resource leaks. Use `try-with-resources` statements or implement `AutoCloseable` for resource cleanup instead.
4. **Thread Synchronization**:
The methods `wait()`, `notify()`, and `notifyAll()` are used for inter-thread communication. They can only be called from within a synchronized context (i.e., a synchronized block or method), otherwise, a `IllegalMonitorStateException` will be thrown at runtime.
YouTip