Java:Object 上帝类

Object 类是 Java 语言中所有类的根类。即使你没有显式写 extends,自定义类也会隐式继承 Object,因此它也常被称为“上帝类”。想要深入理解 Java 的类模型、集合实现以及对象行为,先弄清楚 Object 的常用方法是非常有帮助的。

Object 类中常用方法解析

getClass() 方法

getClass()final 修饰,不能被子类重写。它是一个本地方法,作用是返回当前运行时对象的 Class 类型对象。拿到这个 Class 对象之后,就能获取类的各种元信息,比如类名、包名、字段、方法等。常见调用方式如下:

1
2
Class<?> clazz = obj.getClass();
String className = clazz.getName();

getclass() 方法

hashCode() 方法

hashCode() 也是一个本地方法,返回一个 int 值。默认情况下,它通常是基于对象的内存地址进行哈希计算得到的结果。它的作用是为了让对象在哈希表、集合、映射中更容易定位。需要注意的是:

  • 如果两个对象的 hashCode 相等,不代表它们一定是同一个对象。
  • 如果两个对象的 hashCode 不相等,那么它们通常不是同一个对象。
  • 如果两个对象是同一个对象,那么它们的 hashCode 必定相等。

这四条原则都很好理解。拿 String 为例,String 已经重写了 hashCode()。由于 int 只有 32 位,而字符串数量几乎是无限的,因此不同字符串对象出现相同 hashCode 的情况是可能的,这也就是哈希冲突。比如:ABCDEa123abcABCDFB123abc 可能有相同的哈希值,而真正区分这两个字符串是否相等,就依赖 equals()

equals() 方法

equals()Object 中默认是用 == 来实现的。对基本类型而言,比较的是值是否相等;对引用类型而言,比较的是对象的内存地址是否相同。也就是说,默认情况下,只有同一个对象实例才会返回 true

在重写 hashCode() 后,通常也需要重写 equals(),并且这两个方法之间有如下约定:

  • 如果两个对象 equals() 返回 true,那么它们的 hashCode() 返回值必须相同。
  • 如果两个对象的 hashCode() 相同,不代表 equals() 一定为 true,因为可能存在哈希冲突。

为什么要同时重写 hashCode() 和 equals()?

这是因为在 Java 编程中,我们经常会把对象放进集合中,然后再根据某个 key 取出对应的 value。Map 是最典型的容器之一,HashMapHashTable 都依赖哈希结构来提高查找效率。

HashMap 的底层本质上是一个数组 + 链表(或红黑树)组成的结构,数组的长度通常是 2 的幂,默认值为 16。存入元素时,会先计算对象的 hashCode,再通过取模运算定位桶位置。这样做的好处是:

  • 如果桶位为空,直接放入。
  • 如果桶位已经有元素,则发生哈希冲突。
  • 此时会使用 equals() 判断当前对象和桶中对象是否“相同内容”。
  • 如果相同,则覆盖旧值;如果不同,则在链表或红黑树中追加元素。

因此:

  • hashCode() 负责快速定位元素所在桶位。
  • equals() 负责保证内容唯一性和解决冲突。

两者相辅相成,才能保证集合中 key 的唯一性和高效查询,元素put进HashMap流程如下:

注:当元素数量越来越大时,哈希冲突不可避免。若数组长度较小,冲突会更多,链表长度也会增大,查询效率会下降;因此 HashMap 会在一定条件下自动扩容,扩容因子默认是 0.75

clone() 方法

clone() 是 Java 中原型模式的一种实现。要调用 clone(),目标类必须实现 Cloneable 接口,否则会抛出 CloneNotSupportedException。这里涉及浅拷贝和深拷贝的内容,通常需要结合具体场景来理解,这里不展开深讲,读者可以结合实际代码和资料进一步学习。

toString() 方法

Object 的默认 toString() 实现如下:

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

默认实现并没有太大意义,所以很多类都会重写这个方法。System.out.println(obj) 时,默认打印的也就是对象的 toString() 返回值。

tostring() 方法

tostring() 方法

Object 线程相关方法

waitnotify 这类方法必须在同步代码块或同步方法中调用,否则会抛出 IllegalMonitorStateException 异常。

wait()

调用 wait() 时,当前线程会进入等待状态,并且释放所持有的锁,直到被其他线程唤醒。

wait(long timeout)

带超时时间的等待方法,线程会在指定时间内等待,超时后自动醒来继续执行。

wait(long timeout, int nanos)

更细粒度的等待重载版本,支持纳秒级小的等待控制。

notify()

随机唤醒一个等待中的线程,具体唤醒哪个线程由 JVM 和操作系统实现决定。

notifyAll()

唤醒所有等待中的线程。

其他方法

finalize() 方法

finalize()Object 提供的一个方法,用于在对象被垃圾回收前执行清理逻辑。由于它的执行时机不可预测,而且在 Java 9 之后逐步被更多机制替代,因此实际开发中通常不推荐依赖它做资源回收。

registerNatives() 方法

registerNatives() 是一个本地方法,用于注册本地 JVM 的实现代码。它通常在类初始化阶段被调用,用于桥接 Java 层和底层 C/C++ 实现。

总结

Object 看似简单,但它真正构成了 Java 对象模型的基础。hashCode()equals()toString()clone() 以及线程通信相关方法都是 Java 编程中必须理解的核心概念。真正掌握它们,才能更容易理解集合、并发、对象设计以及 JDK 源码中的实现细节。