Java:Object 上帝类

Java:Object 上帝类
chifyObject 类是 Java 语言中所有类的根类。即使你没有显式写 extends,自定义类也会隐式继承 Object,因此它也常被称为“上帝类”。想要深入理解 Java 的类模型、集合实现以及对象行为,先弄清楚 Object 的常用方法是非常有帮助的。
Object 类中常用方法解析
getClass() 方法
getClass() 由 final 修饰,不能被子类重写。它是一个本地方法,作用是返回当前运行时对象的 Class 类型对象。拿到这个 Class 对象之后,就能获取类的各种元信息,比如类名、包名、字段、方法等。常见调用方式如下:
1 | Class<?> clazz = obj.getClass(); |
hashCode() 方法
hashCode() 也是一个本地方法,返回一个 int 值。默认情况下,它通常是基于对象的内存地址进行哈希计算得到的结果。它的作用是为了让对象在哈希表、集合、映射中更容易定位。需要注意的是:
- 如果两个对象的
hashCode相等,不代表它们一定是同一个对象。 - 如果两个对象的
hashCode不相等,那么它们通常不是同一个对象。 - 如果两个对象是同一个对象,那么它们的
hashCode必定相等。
这四条原则都很好理解。拿 String 为例,String 已经重写了 hashCode()。由于 int 只有 32 位,而字符串数量几乎是无限的,因此不同字符串对象出现相同 hashCode 的情况是可能的,这也就是哈希冲突。比如:ABCDEa123abc 与 ABCDFB123abc 可能有相同的哈希值,而真正区分这两个字符串是否相等,就依赖 equals()。
equals() 方法
equals() 在 Object 中默认是用 == 来实现的。对基本类型而言,比较的是值是否相等;对引用类型而言,比较的是对象的内存地址是否相同。也就是说,默认情况下,只有同一个对象实例才会返回 true。
在重写 hashCode() 后,通常也需要重写 equals(),并且这两个方法之间有如下约定:
- 如果两个对象
equals()返回true,那么它们的hashCode()返回值必须相同。 - 如果两个对象的
hashCode()相同,不代表equals()一定为true,因为可能存在哈希冲突。
为什么要同时重写 hashCode() 和 equals()?
这是因为在 Java 编程中,我们经常会把对象放进集合中,然后再根据某个 key 取出对应的 value。Map 是最典型的容器之一,HashMap 和 HashTable 都依赖哈希结构来提高查找效率。
HashMap 的底层本质上是一个数组 + 链表(或红黑树)组成的结构,数组的长度通常是 2 的幂,默认值为 16。存入元素时,会先计算对象的 hashCode,再通过取模运算定位桶位置。这样做的好处是:
- 如果桶位为空,直接放入。
- 如果桶位已经有元素,则发生哈希冲突。
- 此时会使用
equals()判断当前对象和桶中对象是否“相同内容”。 - 如果相同,则覆盖旧值;如果不同,则在链表或红黑树中追加元素。
因此:
hashCode()负责快速定位元素所在桶位。equals()负责保证内容唯一性和解决冲突。
两者相辅相成,才能保证集合中 key 的唯一性和高效查询,元素put进HashMap流程如下:
flowchart TD
A[对象被放入 HashMap] --> B["调用 hashCode()"]
B --> C{哈希桶是否有位置}
C -->|空| D[直接存入]
C -->|冲突| E["调用 equals() 比较"]
E -->|相同| F[覆盖旧值]
E -->|不同| G["链表/红黑树追加节点"]
注:当元素数量越来越大时,哈希冲突不可避免。若数组长度较小,冲突会更多,链表长度也会增大,查询效率会下降;因此
HashMap会在一定条件下自动扩容,扩容因子默认是0.75。
clone() 方法
clone() 是 Java 中原型模式的一种实现。要调用 clone(),目标类必须实现 Cloneable 接口,否则会抛出 CloneNotSupportedException。这里涉及浅拷贝和深拷贝的内容,通常需要结合具体场景来理解,这里不展开深讲,读者可以结合实际代码和资料进一步学习。
toString() 方法
Object 的默认 toString() 实现如下:
1 | public String toString() { |
默认实现并没有太大意义,所以很多类都会重写这个方法。System.out.println(obj) 时,默认打印的也就是对象的 toString() 返回值。
Object 线程相关方法
wait 和 notify 这类方法必须在同步代码块或同步方法中调用,否则会抛出 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 源码中的实现细节。












