← 返回题目列表

Object 类有哪些方法?分别有什么作用?

简单 第 20 / 32 题 更新于 2026/07/27
ObjecthashCodewaitclone

简化版

Object 是所有类的根父类(所有类都直接或间接继承它),它定义了每个 Java 对象都拥有的通用方法,主要有:equals(Object)(判断两个对象是否「相等」,默认比较地址)、hashCode()(返回对象的哈希码,配合 equals 用于哈希表)、toString()(对象的字符串表示,默认「类名@哈希码」)、getClass()(返回运行时的类对象 Class)、clone()(浅拷贝对象,需实现 Cloneable)、wait()/notify()/notifyAll()(线程间等待-通知,必须在同步块内用)、finalize()(对象回收前调用,已废弃)。理解 Object 的方法,就理解了「每个 Java 对象都天生具备哪些能力」——equals/hashCode/toString 常被重写,wait/notify 是线程协作基础。

详细版

Object 的主要方法

方法作用是否常重写
boolean equals(Object o)判断相等(默认比较引用地址 ==)✅ 常重写
int hashCode()返回哈希码(配合 equals 用于 HashMap 等)✅ 常重写(和 equals 一起)
String toString()对象的字符串表示(默认 类名@十六进制hashCode)✅ 常重写
Class<?> getClass()返回运行时的 Class 对象(final,不能重写)❌ final
Object clone()浅拷贝对象(protected,需实现 Cloneable)✅ 常重写
void wait() / wait(long)释放锁并等待(需在 synchronized 内)❌ final
void notify() / notifyAll()唤醒等待的线程(需在 synchronized 内)❌ final
void finalize()对象被 GC 前调用(已废弃,别用)曾可重写
// equals 和 hashCode 常一起重写(契约:equals 相等则 hashCode 必须相等)
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    User user = (User) o;
    return Objects.equals(name, user.name);
}
@Override
public int hashCode() { return Objects.hash(name); }

// toString 重写方便打印
@Override
public String toString() { return "User{name='" + name + "'}"; }

⚠️ wait()/notify()/notifyAll() 定义在 Object 而不是 Thread 上,是因为它们操作的是「对象的监视器锁」——每个对象都有一把锁和一个等待队列,wait/notify 让线程在「某个对象的锁」上等待/唤醒。既然锁绑在对象上,这三个方法自然就定义在所有对象的根类 Object 上(任何对象都能作为锁)。这是高频追问:「为什么 wait/notify 在 Object 不在 Thread」——答案就是「锁绑在对象上」。

完整版教学

一、Object 是所有类的根

Object 是 Java 类层次的——所有类都直接或间接继承 Object(不写 extends 就默认继承 Object):

class User {}   等价于   class User extends Object {}

所以:
  任何对象都是 Object 的实例(Object o = anyObject 总成立)
  任何对象都拥有 Object 定义的那些通用方法(equals/hashCode/toString...)
  数组也是 Object(Object o = new int[3] 成立)

Object 的意义是「定义每个 Java 对象都必须具备的基础能力」——因为所有对象都继承它,所以每个对象天生就能 equalshashCodetoStringgetClass、参与线程等待通知等。这也是「一切皆对象」的体现——Java 里除了基本类型,一切引用类型都是 Object 的子类。理解「Object 是所有类的根、定义了每个对象的通用方法」,就理解了为什么任何对象都能调 toString()、能作为 HashMap 的 key(有 hashCode/equals)、能作为锁(有 wait/notify)。

二、equals 和 hashCode:相等判断与哈希

equalshashCode 是最常被重写的一对,它们配合用于「判断相等」和「哈希存储」:

equals(Object o):判断两个对象是否"相等"
  Object 的默认实现:return this == o(比较引用地址,即是否同一个对象)
  重写后:可以按"业务上的相等"判断(如两个 User 的 name 相同就算相等)

hashCode():返回对象的哈希码(一个 int)
  Object 的默认实现:通常和对象的内存地址相关(每个对象不同)
  重写后:让"业务上相等的对象"有相同的哈希码

两者的契约(必须一起重写)如果 equals 相等,hashCode 必须相等(反之不要求)。原因:HashMap 等哈希结构先用 hashCode 定位桶、再用 equals 精确判断——如果两个「equals 相等」的对象 hashCode 不同,会被放进不同的桶,导致「放进去却取不出来」。所以重写 equals 必须同时重写 hashCode(保持契约)。这是「为什么 equals 和 hashCode 要一起重写」的答案(前面「equals 和 hashCode 关系」题详讲)。理解「equals 判相等、hashCode 给哈希码、必须一起重写保持契约」,就掌握了这对方法的核心。

三、toString 和 getClass:表示与类型

toStringgetClass 是两个「获取对象信息」的方法:

// toString:对象的字符串表示
Object o = new Object();
o.toString();   // 默认:"java.lang.Object@1b6d3586"(类名@十六进制 hashCode)
// 重写后可以返回有意义的信息,便于日志、调试
// 打印对象、字符串拼接对象时自动调 toString

// getClass:返回运行时的 Class 对象
User user = new User();
Class<?> clazz = user.getClass();   // 返回 User.class
clazz.getName();                    // "com.xx.User"

toString 返回对象的字符串表示,默认是「类名@十六进制 hashCode」(信息量少)——所以业务类常重写它返回有意义的内容(字段值),方便日志和调试。打印对象(System.out.println(obj))、字符串拼接对象时会自动调 toStringgetClass 返回对象运行时的实际类型(Class 对象),是反射的入口,且它是 final 的(不能重写,保证类型信息可靠)。一个区别点:getClass() 返回实际类型(多态下是子类),而 instanceof 判断「是否是某类型或其子类」——所以 equals 里用 getClass() != o.getClass() 是「严格同类型」判断。理解「toString 给字符串表示(默认类名@hashCode,常重写)、getClass 给运行时类型(final、反射入口)」,就掌握了这两个信息方法。

四、clone:浅拷贝

clone() 用于复制一个对象,但它是浅拷贝且用起来有讲究:

class User implements Cloneable {   // 必须实现 Cloneable 标记接口
    String name;
    int[] scores;
    @Override
    public User clone() throws CloneNotSupportedException {
        return (User) super.clone();   // 调 Object 的 clone(浅拷贝)
    }
}
User u2 = u1.clone();   // 复制 u1

clone() 的几个要点:① 是 protected(要重写成 public 才能外部调);② 必须实现 Cloneable 标记接口(否则 super.clone()CloneNotSupportedException);③ 默认是浅拷贝——只复制对象本身,对象内部的引用字段(如 int[] scores)仍指向同一个数组(u1u2 共享 scores)。要深拷贝得手动复制引用字段(或用序列化)。clone() 因为设计有缺陷(浅拷贝、需实现 Cloneable、异常处理繁琐),实际开发常用拷贝构造器、静态工厂或工具类替代(如 new User(u1))。理解「clone 是浅拷贝、需实现 Cloneable、有缺陷常被替代」,就掌握了它的用法和局限(前面「深浅拷贝」题详讲)。

五、wait / notify / notifyAll:线程协作

waitnotifynotifyAll线程间等待-通知的方法,用于线程协作(前面「wait/notify」题详讲):

synchronized (lock) {           // 必须在 synchronized 块内
    while (条件不满足) {
        lock.wait();            // 释放 lock 的锁,进入等待
    }
    // 条件满足,继续
}
// 另一个线程:
synchronized (lock) {
    // 改变条件
    lock.notifyAll();           // 唤醒在 lock 上等待的线程
}

三个要点:① 定义在 Object 上(因为操作对象的监视器锁,每个对象都有锁);② 必须在 synchronized 块内调用(否则抛 IllegalMonitorStateException,因为要持有对象锁才能操作它的等待队列);wait() 会释放锁并等待(区别于 sleep 不释放锁)。一个高频追问「为什么 wait/notify 在 Object 不在 Thread」——因为它们操作的是「对象的监视器锁和等待队列」,锁绑在对象上,所以方法定义在所有对象的根类 Object 上(任何对象都能作为锁来协调线程)。理解「wait/notify 是线程协作、在 Object 上(锁绑对象)、必须在 synchronized 内」,就把 Object 和线程协作串起来了。

六、finalize:已废弃的终结方法

finalize() 是「对象被 GC 回收前调用的方法」,本意是给对象一个「清理资源」的机会,但它已被废弃(Java 9 起 @Deprecated),不该用:

@Override
protected void finalize() throws Throwable {   // 已废弃,别用
    // 本意:对象回收前清理资源(如关闭文件)
}

finalize 为什么被废弃(这是一个不错的 low 频加分点):① 执行时机不确定——finalize 由 GC 触发,你无法控制它什么时候执行(甚至可能永远不执行),不能依赖它及时清理资源;② 性能差——有 finalize 的对象回收要经过额外的处理(放进 finalize 队列、由专门线程调用),拖慢 GC;③ 可能导致对象「复活」——finalize 里如果把对象重新赋给一个引用,对象就不会被回收(诡异行为);④ 异常被吞——finalize 里抛的异常会被忽略。所以 finalize 已被废弃,替代方案是:资源清理用 try-with-resources(AutoCloseable)(确定性、及时)、或 Java 9 的 Cleaner(基于虚引用,比 finalize 可控)。理解「finalize 因时机不确定/性能差/对象复活/吞异常而废弃、用 try-with-resources 或 Cleaner 替代」,就掌握了这个「反面教材」——它是「不该用的方法」的典型。

记忆钩子:「Object 是所有类的根,定义每个对象的通用方法:equals(判相等,默认比地址)+ hashCode(哈希码)必须一起重写、toString(字符串表示,默认类名@hashCode)、getClass(运行时类型,final,反射入口)、clone(浅拷贝,需 Cloneable,有缺陷常被替代)、wait/notify/notifyAll(线程协作,在 Object 因锁绑对象、必须 synchronized 内、wait 释放锁)、finalize(已废弃,用 try-with-resources/Cleaner 替代)」

七、常见误区与追问

  • 误区:equals 默认比较对象内容。 Object 的默认 equals 是 this == o(比较引用地址、是否同一对象);要按内容判相等需重写 equals。
  • 误区:wait/notify 是 Thread 的方法。 它们定义在 Object 上——因为操作的是对象的监视器锁和等待队列(锁绑在对象上),任何对象都能作为锁;所以在所有对象的根类 Object 上。
  • 误区:clone 是深拷贝。 Object 的 clone 是浅拷贝——只复制对象本身,内部引用字段仍指向同一对象;要深拷贝得手动复制引用字段或用序列化。
  • 误区:finalize 能可靠地清理资源。 不能——执行时机不确定(GC 触发、可能不执行)、性能差、可能对象复活、异常被吞,已废弃;资源清理用 try-with-resources 或 Cleaner。
  • 追问:Object 有哪些方法? equals、hashCode、toString、getClass、clone、wait/wait(long)、notify、notifyAll、finalize(已废弃);其中 getClass/wait/notify/notifyAll 是 final 不能重写。
  • 追问:为什么 wait/notify 定义在 Object 上? 它们操作对象的监视器锁和等待队列,锁绑在对象上(任何对象都能作为锁),所以定义在所有对象的根类 Object 上,让任何对象都能协调线程等待/唤醒。
  • 追问:finalize 为什么被废弃,用什么替代? 执行时机不确定、性能差、可能对象复活、异常被吞;替代方案是 try-with-resources(AutoCloseable,确定性及时清理)或 Java 9 的 Cleaner(基于虚引用,比 finalize 可控)。

八、加强记忆

Object 是所有类的根父类(所有类默认继承它,数组也是 Object),定义了每个 Java 对象都具备的通用方法equals(Object)(判相等,默认比引用地址)和 hashCode()(哈希码)必须一起重写(契约:equals 相等则 hashCode 必相等,否则 HashMap 放进去取不出);toString()(字符串表示,默认「类名@十六进制 hashCode」,常重写返回有意义内容);getClass()(运行时实际类型、反射入口、final 不能重写);clone()浅拷贝、protected、需实现 Cloneable、有缺陷常用拷贝构造器替代);wait()/notify()/notifyAll()(线程等待-通知、定义在 Object 因锁绑对象必须在 synchronized 内、wait 释放锁);finalize()(对象回收前调用、已废弃——时机不确定/性能差/可能对象复活/吞异常,用 try-with-resources 或 Cleaner 替代)。其中 getClass/wait/notify/notifyAll 是 final。一句话「Object 是根类定义通用方法:equals+hashCode 一起重写、toString 默认类名@hashCode、getClass 运行时类型 final、clone 浅拷贝需 Cloneable、wait/notify 在 Object 因锁绑对象要 synchronized、finalize 已废弃用 try-with-resources」。