解析Java根基:Object类核心方法

解析 Java 根基:Object 类核心方法深度详解

java.lang.Object 是 Java 中所有类的直接或间接父类,是整个类型体系的根。
理解 Object 的每个方法及其实现细节、默认行为、重写场景、注意事项,是 Java 开发者的基本功,也是面试中高频且容易挖深度的题目

Object 的 11 个核心方法一览表

方法签名作用简述是否默认有意义是否建议重写常见重写场景是否线程安全(默认)
public final Class<?> getClass()获取运行时类型信息有意义禁止重写
public int hashCode()获取对象的哈希码有意义强烈建议放入 HashMap/HashSet 时否(需自己保证)
public boolean equals(Object obj)判断对象是否“相等”有意义强烈建议与 hashCode 一起重写
protected Object clone()浅拷贝对象有意义视情况需要拷贝对象时
public String toString()对象的字符串表示有意义(但很丑)强烈建议日志、调试、打印对象
public final void notify()唤醒在此对象监视器上等待的一个线程有意义禁止重写线程通信(wait/notify 机制)
public final void notifyAll()唤醒在此对象监视器上等待的所有线程有意义禁止重写线程通信
public final void wait()当前线程等待,直到被 notify/notifyAll有意义禁止重写线程通信
public final void wait(long timeout)带超时等待有意义禁止重写
public final void wait(long timeout, int nanos)更精确的超时等待有意义禁止重写
protected void finalize()对象被 GC 前可能被调用(已废弃)无意义不建议

一、最常被重写的三个方法(面试重点)

1. equals() 与 hashCode() —— 必须一起重写

默认行为

  • equals() 默认比较的是对象地址==
  • hashCode() 默认返回的是对象在内存中的某种哈希值(通常与地址相关)

契约(Contract)——必须遵守的规则

  1. 相等性一致:如果 a.equals(b) 返回 true,则 a.hashCode() 必须等于 b.hashCode()
  2. hashCode 一致性:同一个对象在程序运行期间多次调用 hashCode(),只要 equals 使用的信息没变,返回值必须相同
  3. 非空性x.equals(null) 永远返回 false
  4. 对称性a.equals(b)b.equals(a)
  5. 传递性a.equals(b) && b.equals(c)a.equals(c)

正确重写姿势(推荐模板)

@Override
public boolean equals(Object o) {
    if (this == o) return true;                    // 同一对象
    if (o == null || getClass() != o.getClass())   // 类型不同
        return false;

    Person person = (Person) o;                    // 安全转型

    return age == person.age &&                    // 基本类型直接 ==
           Objects.equals(name, person.name) &&    // 使用工具类避免空指针
           Objects.equals(idCard, person.idCard);
}

@Override
public int hashCode() {
    return Objects.hash(name, age, idCard);        // 推荐使用 Objects.hash
}

常见错误

  • 只重写了 equals 没重写 hashCode → HashMap/HashSet 失效
  • == 比较对象字段 → 永远不等
  • 没有处理 null → NullPointerException
  • 用了可变字段参与 equals/hashCode → 放入集合后修改导致找不到

2. toString() —— 日志、调试、打印的门面

默认实现
类名@十六进制hashCode(非常难读)

推荐重写方式

@Override
public String toString() {
    return "Person{" +
           "name='" + name + '\'' +
           ", age=" + age +
           ", idCard='" + idCard + '\'' +
           '}';
}

现代最佳实践(强烈推荐):

  • 使用 Lombok@ToString
  • 或使用 commons-lang3ToStringBuilder
  • 或使用 JSON 序列化(调试时很友好)

3. clone() —— 浅拷贝与深拷贝

默认行为

  • protected 方法
  • 实现 Cloneable 接口后才能调用,否则抛 CloneNotSupportedException
  • 浅拷贝(只复制引用,对象内部引用字段仍指向同一对象)

两种常见实现方式

// 方式1:实现 Cloneable(最常见)
public class Person implements Cloneable {
    private String name;
    private int age;
    private Address address;  // 引用类型

    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person p = (Person) super.clone();           // 浅拷贝
        p.address = address.clone();                 // 手动深拷贝引用字段
        return p;
    }
}
// 方式2:推荐 —— 使用拷贝构造器或拷贝工厂(更安全、更清晰)
public Person(Person other) {
    this.name = other.name;
    this.age = other.age;
    this.address = new Address(other.address);   // 深拷贝
}

结论:现代 Java 项目中,clone() 方法使用率很低,更推荐使用:

  • 拷贝构造器
  • 拷贝工厂方法
  • 第三方库(如 Apache Commons BeanUtils、Dozer、MapStruct)
  • 序列化方式(JSON、Protobuf)

二、被 final 修饰禁止重写的 5 个方法

  • getClass()
  • wait() / wait(long) / wait(long,int)
  • notify() / notifyAll()

这些方法涉及 JVM 底层实现和线程调度逻辑,禁止重写非常合理。

三、finalize() —— 已被废弃的“遗物”

  • Java 9 开始标记为 @Deprecated
  • 原因:不可预测、性能差、容易造成内存泄漏、替代方案更好(Cleaner、try-with-resources)
  • 永远不要依赖 finalize() 做资源释放

四、面试高频追问总结

  1. 为什么 equals 和 hashCode 要一起重写?
  2. 如果只重写 equals 不重写 hashCode 会发生什么?
  3. hashCode 相等,equals 一定相等吗?反过来呢?
  4. clone 是深拷贝还是浅拷贝?怎么实现深拷贝?
  5. Object 的 wait/notify 机制原理是什么?为什么必须在 synchronized 里用?
  6. toString() 不重写会有什么影响?
  7. getClass() 为什么是 final 的?

希望这份总结能让你对 Object 类的理解从“知道有这些方法”升级到“知道每个方法的设计意图、默认行为、重写规范、常见陷阱”。

如果你想继续深入某个方法(比如手写一个安全的深拷贝、wait/notify 经典实现、生产级 equals/hashCode 写法),或者想看某个具体面试题的完整回答范例,随时告诉我!

文章已创建 4580

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部