Object类是所有类的父类,因此所有类都继承Object类的方法
1. equals() 方法:逻辑相等性
2. hashCode() 方法:哈希散列值
hashCode()返回对象的哈希码(int值),主要用于哈希表数据结构(如HashMap、HashSet)中快速定位对象。
核心契约:
一致性:在程序同一执行期间,若对象属性未变,多次调用
hashCode()必须返回相同的整数。等价性:如果
a.equals(b)为true,则a.hashCode() == b.hashCode()必须成立。不等价性(非强制):如果
a.equals(b)为false,hashCode()不必不同。但分散的哈希值能提升哈希表性能。
3. 为什么重写 equals() 必须重写 hashCode()?
这是一个经典陷阱。核心原因在于破坏哈希表的契约。
以HashSet为例:
存储时:先计算对象的
hashCode()定位到桶(数组索引),若桶中无元素则直接存入;若有元素,再通过equals()比较是否重复。查找时:同样先通过
hashCode()定位,再通过equals()确认。
如果只重写equals()而不重写hashCode():
obj1和obj2在逻辑上相等(equals返回true)。但它们的
hashCode()不同(因为默认返回内存地址映射)。存入
HashMap时,两个对象会被分配到不同的桶中,导致集合中同时存在两个“逻辑相同”的对象,违反Set的唯一性,且无法通过obj1查找出obj2。
重要结论:
两个不同对象的
hashCode()有极小概率相同(哈希碰撞)。如果
hashCode()不同,则两个对象一定不相等(利用这一点可快速排除不等对象)。
4. HashMap 底层结构简述
理解hashCode与equals的协作,离不开对HashMap结构的认知:
数组(Node<K,V>[] table):作为哈希桶,通过
(n - 1) & hash计算索引定位。链表:当发生哈希碰撞时,新节点以链表形式挂在数组同一索引下。
红黑树(JDK 1.8+):当链表长度超过阈值(默认8)且数组长度大于64时,链表转为红黑树,将查找复杂度从 O(n) 优化至 O(log n)。
查找流程:
计算
key.hashCode()定位桶。若桶内无节点,返回
null。若桶内有节点,通过
equals()在链表/树中查找匹配的键。
5. toString() 方法:对象的字符串表示
toString()方法,本身返回的是地址
输出一个对象,默认调用toString()方法,输出对象所属类型和内存地址
可以重写toString()方法
6.getClass() 方法——运行时类信息
getClass()是Object类中一个非常重要的方法,它返回当前对象的运行时类信息。
获取运行时类型:返回的是对象实际的Class对象,而不是引用声明的类型。
Object obj = new String("Hello"); System.out.println(obj.getClass()); // class java.lang.String System.out.println(obj.getClass().getName()); // java.lang.Stringfinal 方法,不可重写:由 JVM 原生实现,保证所有对象都能获取到准确的类信息。
与反射机制紧密相关:通过Class对象可以获取类的构造器、方法、字段等信息,是实现反射的入口。
Class<?> clazz = obj.getClass(); Method[] methods = clazz.getDeclaredMethods(); // 获取所有方法7. 线程协作:wait() / notify() / notifyAll()
这三个方法属于Object类,因为它们操作的是对象监视器(锁)。
7.1notify()和notifyAll()方法
当一个线程调用wait()进入等待状态,会让出CPU,不参与竞争,调用notify()唤醒等待的线程,notifyAll()唤醒全部线程
7.2wait()方法
wait() 与 sleep() 的核心区别
| 特性 | wait() | sleep() |
|---|---|---|
| 所属类 | Object类 | Thread类 |
| 锁要求 | 必须在同步块/同步方法中持有锁 | 不需要持有锁 |
| 锁释放 | 释放当前对象锁 | 不释放任何锁 |
| 唤醒方式 | 需由notify()/notifyAll()唤醒,或超时自动唤醒 | 时间到期自动唤醒,或interrupt()中断 |
| 使用场景 | 线程间通信(生产者-消费者模式) | 让线程暂停执行一段时间 |