源本科技 | 码上会

Java 中的 Object 类

2025/12/27
43
0

学习目标

  • 理解 Object 类在 Java 类体系中的核心地位

  • 掌握 Object 类提供的关键方法及其用途

  • 能够正确重写 toString()equals()hashCode() 等方法

  • 了解对象克隆(clone())与垃圾回收(finalize())机制

  • 初步认识线程通信相关方法(wait() / notify()


什么是 Object 类

java.lang.ObjectJava 所有类的根类。无论你是否显式继承,每一个 Java 类都直接或间接地继承自 Object。这意味着所有对象都天然具备 Object 提供的一组通用行为。

为什么需要 Object 类?

  • 作为所有类的共同祖先,提供统一接口

  • 定义所有对象共有的基础操作(如字符串表示、相等性判断)

  • 支持哈希结构(如 HashMapHashSet)的底层机制

  • 提供线程间通信的基本能力(wait()notify()

  • 实现对象复制(clone())和资源清理(finalize()


核心方法

1 toString()

对象的字符串表示

默认返回格式:类名@十六进制哈希码(如 Person@13f79d8)。建议重写以提供更有意义的输出。

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

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

    public static void main(String[] args) {
        Person p = new Person("小明");
        System.out.println(p.toString()); // 输出:Person{name='小明'}
        System.out.println(p.hashCode()); // 输出:例如 123456789
    }
}

提示System.out.println(obj) 会自动调用 obj.toString()


2 hashCode()

对象的哈希值

  • 返回一个 int 值,用于哈希表(如 HashMap)快速定位对象

  • 重要规则:如果两个对象通过 equals() 判定为相等,则它们的 hashCode() 必须相同

class Employee {
    int id = 101;

    @Override
    public int hashCode() {
        return id * 31; // 常见做法:使用质数(如 31)参与计算
    }

    public static void main(String[] args) {
        Employee e = new Employee();
        System.out.println(e.hashCode()); // 输出:3131
    }
}

注意:hashCode() 不是内存地址!尽管默认实现可能与地址有关,但语义上它只是一个用于哈希的整数。


3 equals(Object obj)

对象相等性判断

  • 默认行为:比较引用是否相同(即 ==

  • 通常需要重写以实现基于内容的比较

class Book {
    String title;

    Book(String title) {
        this.title = title;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        
        Book other = (Book) obj;
        return this.title.equals(other.title);
    }

    public static void main(String[] args) {
        Book b1 = new Book("Java 编程思想");
        Book b2 = new Book("Java 编程思想");
        System.out.println(b1.equals(b2)); // 输出:true
    }
}

最佳实践:重写 equals() 时,必须同时重写 hashCode(),以保证哈希集合的正确性。


4 getClass()

获取运行时类信息

返回当前对象的实际类型(Class 对象),常用于反射。

public class Main {
    public static void main(String[] args) {
        Object o = new String("Hello");
        Class<?> clazz = o.getClass();
        System.out.println("对象 o 的实际类型是: " + clazz.getName());
        // 输出:java.lang.String
    }
}

5 finalize()

对象销毁前的清理(已过时)

  • 在垃圾回收器回收对象前调用

  • 不推荐使用:从 Java 9 开始标记为 @Deprecated,Java 18+ 已移除

  • 替代方案:使用 try-with-resourcesCleaner

警告:不要依赖 finalize() 执行关键清理逻辑!


6 clone()

对象克隆

  • 创建并返回当前对象的副本

  • 类必须实现 Cloneable 接口,否则抛出 CloneNotSupportedException

  • 默认是浅拷贝(shallow copy)

class Student implements Cloneable {
    int id = 1;
    String name = "张三";

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone(); // 调用 Object 的 clone()
    }

    public static void main(String[] args) throws Exception {
        Student s1 = new Student();
        Student s2 = (Student) s1.clone();
        System.out.println(s1.name); // 张三
        System.out.println(s2.name); // 张三
    }
}

浅拷贝 vs 深拷贝

  • 浅拷贝:仅复制对象本身,内部引用仍指向原对象

  • 深拷贝:递归复制所有嵌套对象(需手动实现)


7 并发方法

wait()notify()notifyAll()

用于线程间协作,必须在 同步代码块(synchronized) 中调用。

方法

作用

wait()

使当前线程等待,释放锁

notify()

唤醒一个等待的线程

notifyAll()

唤醒所有等待的线程

这些方法属于高级并发编程内容,后续在多线程专题中深入讲解。


综合示例

完整重写 Object 方法

class Book implements Cloneable {
    private String title;
    private String author;
    private int year;

    public Book(String title, String author, int year) {
        this.title = title;
        this.author = author;
        this.year = year;
    }

    @Override
    public String toString() {
        return title + " by " + author + " (" + year + ")";
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Book other = (Book) obj;
        return year == other.year &&
               title.equals(other.title) &&
               author.equals(other.author);
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + title.hashCode();
        result = 31 * result + author.hashCode();
        result = 31 * result + year;
        return result;
    }

    @Override
    public Book clone() {
        try {
            return (Book) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError("Clone 不应失败"); // 因为实现了 Cloneable
        }
    }

    // getter 省略...

    public static void main(String[] args) {
        Book b1 = new Book("深入理解计算机系统", "Randal Bryant", 2010);
        Book b2 = b1.clone();

        System.out.println(b1);                    // 深入理解计算机系统 by Randal Bryant (2010)
        System.out.println(b2);                    // 同上
        System.out.println("b1 equals b2: " + b1.equals(b2)); // true
        System.out.println("b1 hash: " + b1.hashCode());
        System.out.println("b2 hash: " + b2.hashCode());     // 相同

        b1 = null;
        System.gc(); // 可能触发 finalize(不保证)
    }
}

重点总结

方法

是否需重写

关键点

toString()

✅ 强烈建议

提供可读的对象描述

equals()

✅ 按需

实现业务逻辑相等性

hashCode()

✅ 与 equals() 配套

相等对象必须哈希值相同

clone()

⚠️ 谨慎使用

注意浅 / 深拷贝问题

finalize()

❌ 避免使用

已废弃,不可靠

getClass()

❌ 无需重写

用于反射

wait/notify

❌ 不重写

用于线程同步

黄金法则只要重写了 equals(),就必须重写 hashCode()


思考题

  1. 为什么 Object 类中的 clone() 方法是 protected 的?这对接口设计有何影响?

  2. 如果两个对象 a.equals(b) 返回 true,但 a.hashCode() != b.hashCode(),会发生什么问题?

  3. 在现代 Java 开发中,有哪些方式可以替代 finalize() 来管理资源?