如果你问一个 Java 开发者,什么是 Java 中最重要的类?答案一定是java.lang.Object。作为所有 Java 类的根类,Object 类定义了所有对象都具备的核心行为,是整个 Java 语言的基石。然而,很多开发者虽然每天都在使用 Object 类的方法,却很少深入思考它们的设计意图和正确用法。本文将全面、深入地解析 Object 类的 11 个核心方法,帮助你彻底掌握这个 Java 世界的 "万物之源"。

目录

1. Object 类的核心地位

1.1 什么是 Object 类

1.2 为什么 Object 类如此重要

2. 对象通用操作方法详解(5 个)

2.1 getClass () 方法

2.1.1 方法签名与关键字

2.1.2 核心作用

2.1.3 使用实例

2.1.4 注意事项

2.2 hashCode () 方法

2.2.1 方法签名与关键字

2.2.2 核心作用

2.2.3 设计契约

2.2.4 使用实例

2.2.5 注意事项

2.3 equals () 方法

2.3.1 方法签名与关键字

2.3.2 核心作用

2.3.3 重写规则

2.3.4 使用实例

2.3.5 注意事项

2.4 toString () 方法

2.4.1 方法签名与关键字

2.4.2 核心作用

2.4.3 使用实例

2.4.4 注意事项

2.5 clone () 方法

2.5.1 方法签名与关键字

2.5.2 核心作用

2.5.3 浅拷贝与深拷贝

2.5.4 使用实例

2.5.5 注意事项

3. 线程协作方法详解(5 个)

3.1 线程协作的基础:对象监视器

3.2 wait () 系列方法

3.2.1 wait () 方法

3.2.2 wait (long timeout) 方法

3.2.3 wait (long timeout, int nanos) 方法

3.3 notify () 与 notifyAll () 方法

3.3.1 notify () 方法

3.3.2 notifyAll () 方法

3.4 完整实例:生产者 - 消费者模型

3.5 注意事项

4. 对象生命周期方法:finalize ()

4.1 方法签名与关键字

4.2 设计初衷

4.3 为什么被废弃

4.4 替代方案

5. 关键注意事项与最佳实践

5.1 equals () 与 hashCode () 必须同时重写

5.2 永远不要使用 finalize () 方法

5.3 线程等待必须使用 while 循环

5.4 优先使用 notifyAll () 而非 notify ()

5.5 避免使用 clone () 方法,推荐拷贝构造函数

6. 总结


1. Object 类的核心地位

1.1 什么是 Object 类

java.lang.Object位于 Java 核心包中,无需显式导入即可使用。它是所有 Java 类的直接或间接父类,这意味着任何 Java 对象都可以赋值给 Object 类型的引用。即使你没有显式声明继承自 Object,编译器也会自动为你的类添加extends Object

1.2 为什么 Object 类如此重要

Object 类的设计体现了 Java"一切皆对象" 的核心理念。它为所有对象提供了统一的基础接口,定义了 Java 对象的 "通用契约",确保了语言行为的一致性。无论是集合框架、并发编程还是反射机制,都建立在 Object 类的基础之上。掌握 Object 类的方法,是写出高质量 Java 代码的前提。

2. 对象通用操作方法详解(5 个)

这 5 个方法定义了所有对象都具备的基础行为,是日常开发中最常接触和重写的方法。

2.1 getClass () 方法

2.1.1 方法签名与关键字
  • 完整签名:public final native Class<?> getClass()
  • 关键字:publicfinalnative
  • 参数:无
  • 返回值:对象的运行时Class对象
2.1.2 核心作用

获取对象的精确运行时类型,是 Java 反射机制的入口。与instanceof运算符不同,getClass()返回的是对象的实际类型,不受向上转型的影响。

2.1.3 使用实例
public class GetClassExample {
    public static void main(String[] args) {
        Object obj = "Hello World";
        Class<?> clazz = obj.getClass();
        
        System.out.println(clazz.getName()); // 输出:java.lang.String
        System.out.println(clazz.getSimpleName()); // 输出:String
        System.out.println(obj instanceof Object); // 输出:true
    }
}
2.1.4 注意事项
  • 该方法被final修饰,不可被重写,确保了返回结果的准确性
  • 返回的Class对象是单例的,同一个类的所有对象共享同一个Class实例

2.2 hashCode () 方法

2.2.1 方法签名与关键字
  • 完整签名:public native int hashCode()
  • 关键字:publicnative
  • 参数:无
  • 返回值:对象的 32 位整数哈希码
2.2.2 核心作用

为哈希表(如HashMapHashSetHashtable)提供快速定位能力。哈希码用于计算对象在哈希表中的存储位置,大大提高了查找效率。

2.2.3 设计契约

hashCode()equals()之间存在严格的设计契约,这是 Java 中最重要的规则之一:

  • 若两个对象通过equals()比较相等,则它们的hashCode()必须相等
  • 若两个对象通过equals()比较不相等,它们的hashCode()可以相等(但应尽量避免,否则会降低哈希表性能)
2.2.4 使用实例
import java.util.Objects;

class User {
    private String id;
    private String name;

    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public int hashCode() {
        // JDK7+推荐使用Objects.hash()组合多个字段的哈希码
        return Objects.hash(id, name);
    }
}
2.2.5 注意事项
  • 重写equals()方法时,必须同时重写hashCode()方法,否则会导致哈希表出现逻辑错误
  • 哈希码不是对象的内存地址,不同 JVM 的实现方式不同

2.3 equals () 方法

2.3.1 方法签名与关键字
  • 完整签名:public boolean equals(Object obj)
  • 关键字:public
  • 参数:obj - 要比较的对象
  • 返回值:true表示对象逻辑相等,false表示不相等
2.3.2 核心作用

判断两个对象是否 "逻辑相等"。Object 类的默认实现是引用比较(即this == obj),比较的是两个对象的内存地址。对于业务类来说,通常需要重写该方法以实现基于字段值的比较。

2.3.3 重写规则

重写equals()方法必须满足以下 5 个特性,否则会破坏逻辑:

  1. 自反性x.equals(x)必须返回true
  2. 对称性:若x.equals(y)返回true,则y.equals(x)也必须返回true
  3. 传递性:若x.equals(y)y.equals(z)都返回true,则x.equals(z)也必须返回true
  4. 一致性:只要对象未修改,多次调用equals()应返回相同结果
  5. 非空性x.equals(null)必须返回false
2.3.4 使用实例
import java.util.Objects;

class User {
    private String id;
    private String name;

    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        // 1. 引用相同,直接返回true
        if (this == obj) return true;
        
        // 2. 对象为null或类型不同,返回false
        if (obj == null || getClass()!= obj.getClass()) return false;
        
        // 3. 强转后比较核心字段
        User user = (User) obj;
        return Objects.equals(id, user.id) && Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}
2.3.5 注意事项
  • 使用Objects.equals()比较引用类型字段,可以避免空指针异常
  • 不要使用instanceof进行类型判断,否则会破坏对称性

2.4 toString () 方法

2.4.1 方法签名与关键字
  • 完整签名:public String toString()
  • 关键字:public
  • 参数:无
  • 返回值:对象的字符串表示
2.4.2 核心作用

提供对象的人类可读描述,便于调试和日志记录。Object 类的默认实现是类名@十六进制哈希码(如User@1a2b3c4d),几乎没有实际意义。

2.4.3 使用实例
class User {
    private String id;
    private String name;

    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
               "id='" + id + '\'' +
               ", name='" + name + '\'' +
               '}';
    }
}

// 使用示例
User user = new User("1", "Alice");
System.out.println(user); // 输出:User{id='1', name='Alice'}
2.4.4 注意事项
  • 强烈建议所有业务类重写toString()方法
  • 返回的字符串应包含对象的核心字段信息
  • 可以使用 Lombok 的@ToString注解自动生成该方法

2.5 clone () 方法

2.5.1 方法签名与关键字
  • 完整签名:protected native Object clone() throws CloneNotSupportedException
  • 关键字:protectednative
  • 参数:无
  • 返回值:对象的副本
  • 异常:CloneNotSupportedException - 类未实现Cloneable接口时抛出
2.5.2 核心作用

创建并返回对象的浅拷贝。浅拷贝仅复制对象本身,不复制其引用类型的字段,即副本和原对象共享引用字段指向的对象。

2.5.3 浅拷贝与深拷贝
  • 浅拷贝:复制对象本身,引用类型字段共享
  • 深拷贝:不仅复制对象本身,还递归复制其引用类型的字段,副本和原对象完全独立
2.5.4 使用实例
class User implements Cloneable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写为public访问权限
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone(); // 调用父类的native方法实现浅拷贝
    }

    // getter和setter
    public String getName() { return name; }
    public int getAge() { return age; }
}

// 使用示例
public class CloneExample {
    public static void main(String[] args) throws CloneNotSupportedException {
        User original = new User("Alice", 25);
        User copy = (User) original.clone();
        
        System.out.println(original!= copy); // 输出:true(不同对象)
        System.out.println(original.getName() == copy.getName()); // 输出:true(共享引用)
    }
}
2.5.5 注意事项
  • 类必须实现Cloneable标记接口才能调用clone()方法
  • clone()方法存在设计缺陷,不推荐使用
  • 推荐使用拷贝构造函数或序列化 / 反序列化实现对象复制

3. 线程协作方法详解(5 个)

这 5 个方法是 Java 原生线程通信的基础,基于对象监视器(Monitor)实现线程间的等待 / 唤醒机制。

3.1 线程协作的基础:对象监视器

每个 Java 对象都有一个内置的监视器锁(Monitor Lock),用于实现同步。线程协作方法的本质是操作对象的监视器:

  • 调用wait():线程释放锁,进入等待队列
  • 调用notify()/notifyAll():唤醒等待队列中的线程,进入锁竞争队列

重要规则:所有线程协作方法必须在synchronized同步块 / 方法中调用,否则会抛出IllegalMonitorStateException

3.2 wait () 系列方法

3.2.1 wait () 方法
  • 完整签名:public final void wait() throws InterruptedException
  • 关键字:publicfinal
  • 参数:无
  • 返回值:无
  • 核心作用:让当前线程释放对象锁,进入无限等待状态,直到被其他线程唤醒
  • 异常:InterruptedException - 线程被中断时抛出
3.2.2 wait (long timeout) 方法
  • 完整签名:public final native void wait(long timeout) throws InterruptedException
  • 关键字:publicfinalnative
  • 参数:timeout - 最长等待时间(毫秒)
  • 返回值:无
  • 核心作用:让当前线程释放对象锁,进入超时等待状态,超时后自动唤醒
3.2.3 wait (long timeout, int nanos) 方法
  • 完整签名:public final void wait(long timeout, int nanos) throws InterruptedException
  • 关键字:publicfinal
  • 参数:
    • timeout - 最长等待时间(毫秒)
    • nanos - 额外等待时间(纳秒,0-999999)
  • 返回值:无
  • 核心作用:提供更精确的超时控制(实际 JVM 很少支持精确纳秒级等待)

3.3 notify () 与 notifyAll () 方法

3.3.1 notify () 方法
  • 完整签名:public final native void notify()
  • 关键字:publicfinalnative
  • 参数:无
  • 返回值:无
  • 核心作用:随机唤醒一个正在等待该对象锁的线程
  • 注意:不推荐使用,容易导致信号丢失
3.3.2 notifyAll () 方法
  • 完整签名:public final native void notifyAll()
  • 关键字:publicfinalnative
  • 参数:无
  • 返回值:无
  • 核心作用:唤醒所有正在等待该对象锁的线程(推荐使用)

3.4 完整实例:生产者 - 消费者模型

class SharedBuffer {
    private int data;
    private boolean hasData = false;

    // 生产者方法
    public synchronized void produce(int value) throws InterruptedException {
        // 使用while循环而非if,防止虚假唤醒
        while (hasData) {
            wait(); // 缓冲区有数据,生产者等待
        }
        data = value;
        hasData = true;
        notifyAll(); // 唤醒所有等待的消费者
    }

    // 消费者方法
    public synchronized int consume() throws InterruptedException {
        while (!hasData) {
            wait(); // 缓冲区无数据,消费者等待
        }
        hasData = false;
        notifyAll(); // 唤醒所有等待的生产者
        return data;
    }
}

// 测试代码
public class ProducerConsumerExample {
    public static void main(String[] args) {
        SharedBuffer buffer = new SharedBuffer();

        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    buffer.produce(i);
                    System.out.println("生产:" + i);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    int value = buffer.consume();
                    System.out.println("消费:" + value);
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();
    }
}

3.5 注意事项

  • 所有线程协作方法都是final的,不可被重写
  • 线程等待必须使用while循环,而不是if语句,防止虚假唤醒
  • 优先使用notifyAll()而非notify(),避免信号丢失导致所有线程永久等待

4. 对象生命周期方法:finalize ()

4.1 方法签名与关键字

  • 完整签名:protected void finalize() throws Throwable
  • 关键字:protected
  • 参数:无
  • 返回值:无
  • 核心作用:垃圾回收器(GC)回收对象前调用,用于资源释放

4.2 设计初衷

finalize()方法的设计初衷是在对象被回收前执行一些清理工作,比如关闭文件句柄、释放网络连接等。

4.3 为什么被废弃

finalize()方法存在严重的设计缺陷,已被官方废弃:

  • 执行时机不可预测:GC 何时运行、何时调用finalize()完全不确定
  • 可能导致对象复活:在finalize()中可以重新将对象赋值给引用,使其逃脱回收
  • 性能差finalize()的执行会严重影响 GC 性能
  • 容易造成内存泄漏:如果finalize()执行缓慢或抛出异常,会导致对象无法被回收

状态说明:JDK 9 标记为@Deprecated(since="9"),JDK 18 标记为forRemoval=true,未来版本将彻底移除。

4.4 替代方案

  • 首选方案:实现AutoCloseable接口,配合try-with-resources语法自动释放资源
  • 次选方案:使用java.lang.ref.Cleaner(JDK 9+)或PhantomReference进行安全的后置清理

5. 关键注意事项与最佳实践

5.1 equals () 与 hashCode () 必须同时重写

这是 Java 中最容易被违反的规则,也是导致HashMapHashSet等集合类出现 bug 的最常见原因。如果两个相等的对象哈希码不同,它们会被放入哈希表的不同桶中,导致集合无法正确找到对应的对象。

5.2 永远不要使用 finalize () 方法

finalize()方法已被官方废弃,存在严重的设计缺陷。任何需要资源释放的场景,都应该使用try-with-resources语法。

5.3 线程等待必须使用 while 循环

JVM 可能会在没有被notify()/notifyAll()唤醒的情况下,将等待的线程唤醒,这被称为 "虚假唤醒"。使用while循环可以在唤醒后再次检查条件是否满足,确保程序逻辑正确。

5.4 优先使用 notifyAll () 而非 notify ()

notify()只会随机唤醒一个线程,在多生产者多消费者场景下,极易导致信号丢失,造成所有线程永久等待。notifyAll()虽然会唤醒所有线程,但只有一个线程能抢到锁,其他线程会再次进入等待状态,虽然效率略低,但能保证程序的正确性。

5.5 避免使用 clone () 方法,推荐拷贝构造函数

clone()方法存在设计缺陷,需要实现Cloneable接口,且默认是浅拷贝。拷贝构造函数更加直观、灵活,且能实现深拷贝。

6. 总结

java.lang.Object是 Java 语言的基石,它定义了所有 Java 对象的通用行为。本文全面解析了 Object 类的 11 个核心方法,包括它们的签名、作用、用法和最佳实践。

掌握 Object 类的方法,不仅能帮助你写出更正确、更高效的 Java 代码,还能为你学习 Java 集合框架、并发编程等高级主题打下坚实的基础。希望这篇文章能让你对 Object 类有一个全新的认识。

更多推荐