Object 是 Java 所有类的「根类」,所有类都直接 / 间接继承 Object,其 11 个核心方法定义了 Java 对象的通用行为(如相等性判断、哈希计算、垃圾回收等)。本文从「方法原理→重写规则→实战场景」全维度拆解,帮你彻底掌握 Object 类的核心用法。

一、Object 类核心定位

  • 继承关系:所有类默认继承 java.lang.Object(无需显式写 extends Object);
  • 核心价值:提供所有 Java 对象的通用方法,是 Java 面向对象的基础;
  • 11 个核心方法toString()equals()hashCode()getClass()clone()finalize()(已废弃)、wait()/wait(long)/wait(long, int)notify()notifyAll()

二、11 个核心方法逐一拆解

1. toString ():对象的字符串表示

(1)默认实现

java

运行

// Object 源码
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

默认返回「类全限定名 + @ + 哈希值十六进制」,如 com.example.User@15db9742,可读性差。

(2)重写规则

必须返回有意义的字符串(包含对象核心属性),是调试 / 日志的核心方法。

(3)实战示例

java

运行

public class User {
    private String name;
    private Integer age;

    // 重写toString()
    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }

    // 构造器+getter/setter省略
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        User user = new User("张三", 20);
        System.out.println(user); // 自动调用toString(),输出:User{name='张三', age=20}
    }
}

2. equals ():对象相等性判断

(1)默认实现

java

运行

// Object 源码
public boolean equals(Object obj) {
    return (this == obj);
}

默认用 == 比较对象引用(是否指向同一内存地址),而非对象内容。

(2)重写规则(必须遵守)
  • 自反性x.equals(x) 必须返回 true;
  • 对称性x.equals(y)y.equals(x)
  • 传递性x.equals(y)y.equals(z)x.equals(z)
  • 一致性:多次调用结果一致(无修改的前提下);
  • 非空性x.equals(null) 必须返回 false。
(3)重写模板(结合 hashCode)

java

运行

@Override
public boolean equals(Object o) {
    // 1. 引用相同,直接返回true
    if (this == o) return true;
    // 2. 为空或类型不同,返回false
    if (o == null || getClass() != o.getClass()) return false;
    // 3. 强转并比较核心属性
    User user = (User) o;
    return Objects.equals(name, user.name) && Objects.equals(age, user.age);
}

✅ 推荐用 Objects.equals() 避免空指针(Objects.equals(a,b) 会先判空)。

3. hashCode ():对象哈希值计算

(1)默认实现

返回对象的内存地址映射值(不同 JVM 实现不同),保证同一对象哈希值固定。

(2)核心规则(与 equals 强绑定)
  • x.equals(y) = true,则 x.hashCode() 必须等于 y.hashCode()
  • x.hashCode() = y.hashCode()x.equals(y) 不一定为 true(哈希冲突);
  • 重写 equals() 必须重写 hashCode(),否则违反 HashMap/HashSet 等集合的规范。
(3)重写示例

java

运行

@Override
public int hashCode() {
    // 用Objects.hash()自动处理空值,组合核心属性
    return Objects.hash(name, age);
}
(4)反例(必避坑)

java

运行

// 错误:只重写equals,不重写hashCode
public class BadUser {
    private String id;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BadUser badUser = (BadUser) o;
        return Objects.equals(id, badUser.id);
    }
    // 未重写hashCode()
}

// 测试:HashMap 中无法正确识别相等对象
public static void main(String[] args) {
    BadUser u1 = new BadUser();
    u1.setId("001");
    BadUser u2 = new BadUser();
    u2.setId("001");
    
    Map<BadUser, String> map = new HashMap<>();
    map.put(u1, "张三");
    System.out.println(map.get(u2)); // null(哈希值不同,定位不到)
}

4. getClass ():获取对象的 Class 实例

(1)源码与特性

java

运行

// Object 源码(native 方法,底层C实现)
public final native Class<?> getClass();
  • final 方法,不可重写;
  • 返回对象的运行时类(Class 对象),是反射的核心入口;
  • 同一类的所有实例共享同一个 Class 对象。
(2)实战示例(反射 / 类型判断)

java

运行

public static void main(String[] args) {
    User user = new User("张三", 20);
    // 1. 反射入口
    Class<? extends User> clazz = user.getClass();
    System.out.println(clazz.getName()); // com.example.User
    
    // 2. 类型判断(比 instanceof 更严格,不考虑子类)
    User user2 = new User("李四", 25);
    System.out.println(user.getClass() == user2.getClass()); // true
    
    // 3. 避免 instanceof 的子类误判
    class SubUser extends User {
        public SubUser(String name, Integer age) {
            super(name, age);
        }
    }
    SubUser subUser = new SubUser("王五", 30);
    System.out.println(subUser instanceof User); // true
    System.out.println(subUser.getClass() == User.class); // false
}

5. clone ():对象克隆

(1)源码与前提

java

运行

// Object 源码
protected native Object clone() throws CloneNotSupportedException;
  • protected 方法,需重写为 public
  • 实现克隆的类必须实现 Cloneable 接口(标记接口,无方法),否则抛 CloneNotSupportedException
(2)浅克隆 vs 深克隆

表格

类型 核心特点 适用场景
浅克隆 仅复制对象引用,对象内部的引用类型共享 无嵌套引用类型的简单对象
深克隆 复制对象及所有嵌套引用类型对象 有嵌套引用类型的复杂对象
(3)实战示例

java

运行

// 浅克隆示例
public class Address implements Cloneable {
    private String city;
    // 构造器+getter/setter+toString省略
    public Address(String city) {
        this.city = city;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class User implements Cloneable {
    private String name;
    private Address address; // 嵌套引用类型

    // 浅克隆:仅复制address引用
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // 深克隆:手动复制嵌套对象
    public Object deepClone() throws CloneNotSupportedException {
        User clone = (User) super.clone();
        clone.address = (Address) address.clone(); // 克隆嵌套对象
        return clone;
    }

    // 测试
    public static void main(String[] args) throws CloneNotSupportedException {
        User user = new User("张三", new Address("北京"));
        
        // 浅克隆:address引用共享
        User shallowClone = (User) user.clone();
        shallowClone.getAddress().setCity("上海");
        System.out.println(user.getAddress().getCity()); // 上海(原对象被修改)
        
        // 深克隆:address独立
        User deepClone = (User) user.deepClone();
        deepClone.getAddress().setCity("广州");
        System.out.println(user.getAddress().getCity()); // 上海(原对象不受影响)
    }
}

6. finalize ():垃圾回收前的清理(已废弃)

(1)源码与特性

java

运行

// Object 源码(JDK9+ 标记为 @Deprecated)
protected void finalize() throws Throwable { }
  • 垃圾回收器回收对象前调用,用于释放非 Java 资源(如文件句柄、网络连接);
  • 执行时机不确定、可能不执行(JVM 退出前可能不触发 GC),已被 java.lang.ref.Cleaner 替代。
(2)替代方案

java

运行

// 用 Cleaner 实现资源清理(推荐)
public class Resource implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();
    private final Cleaner.Cleanable cleanable;

    public Resource() {
        this.cleanable = cleaner.register(this, () -> {
            // 清理逻辑(如关闭文件、释放连接)
            System.out.println("资源被清理");
        });
    }

    @Override
    public void close() {
        cleanable.clean(); // 手动触发清理
    }

    public static void main(String[] args) {
        try (Resource resource = new Resource()) {
            // 使用资源
        } // 自动调用close()
    }
}

7. wait ()/wait (long)/wait (long, int):线程等待

(1)核心作用

让当前线程释放对象锁,进入等待队列,直到被 notify()/notifyAll() 唤醒或超时。

(2)使用规则(必遵守)
  • 必须在 synchronized 代码块 / 方法中调用(持有对象锁),否则抛 IllegalMonitorStateException
  • 调用后线程释放锁,被唤醒后需重新竞争锁。
(3)实战示例(生产者 - 消费者)

java

运行

public class QueueDemo {
    private final Queue<String> queue = new LinkedList<>();
    private static final int MAX_SIZE = 5;

    // 生产者
    public synchronized void produce(String data) throws InterruptedException {
        while (queue.size() == MAX_SIZE) {
            wait(); // 队列满,等待
        }
        queue.add(data);
        System.out.println("生产:" + data);
        notifyAll(); // 唤醒消费者
    }

    // 消费者
    public synchronized String consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // 队列空,等待
        }
        String data = queue.poll();
        System.out.println("消费:" + data);
        notifyAll(); // 唤醒生产者
        return data;
    }

    public static void main(String[] args) {
        QueueDemo demo = new QueueDemo();
        // 生产者线程
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    demo.produce("数据" + i);
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();

        // 消费者线程
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    demo.consume();
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();
    }
}

8. notify ()/notifyAll ():唤醒等待线程

(1)核心区别

表格

方法 作用 注意事项
notify() 唤醒对象等待队列中的一个随机线程 可能导致线程饥饿(某线程一直不被唤醒)
notifyAll() 唤醒对象等待队列中的所有线程 无饥饿问题,推荐优先使用
(2)使用规则
  • 必须在 synchronized 代码块 / 方法中调用;
  • 唤醒后线程不会立即执行,需等待当前线程释放锁后竞争锁。

三、核心方法重写优先级

表格

优先级 方法 重写必要性 核心场景
最高 toString() 必重写 调试、日志、打印对象
最高 equals() 必重写 对象相等性判断、集合存储
最高 hashCode() 必重写 与 equals 绑定,集合存储
clone() 按需重写 对象克隆(浅 / 深克隆)
finalize() 不推荐重写 资源清理(用 Cleaner 替代)
特殊 wait/notify 几乎不重写 线程同步(直接使用)
特殊 getClass() 不能重写 反射、类型判断

四、常见面试题

  1. Object 类有哪些核心方法?核心:toString ()、equals ()、hashCode ()、getClass ()、clone ()、finalize ()、wait ()/notify ()/notifyAll ();扩展:wait (long)、wait (long, int)。

  2. equals () 和 == 的区别?

    • ==:基本类型比较值,引用类型比较内存地址;
    • equals():默认等价于 ==,重写后可比较对象内容。
  3. 重写 equals () 为什么必须重写 hashCode ()?保证相等的对象哈希值相同,否则 HashMap/HashSet 等集合无法正确识别相等对象(哈希冲突导致查找失败)。

  4. wait () 和 sleep () 的区别?

    • wait ():Object 方法,释放对象锁,需在 synchronized 中调用;
    • sleep ():Thread 静态方法,不释放锁,可在任意位置调用。
  5. clone () 的浅克隆和深克隆区别?浅克隆仅复制对象引用,嵌套对象共享;深克隆复制所有嵌套对象,完全独立。

五、实战避坑指南

  1. equals () 重写避坑

    • 避免用 instanceof(子类会破坏对称性),优先用 getClass() == o.getClass()
    • Objects.equals() 避免空指针。
  2. hashCode () 重写避坑

    • 尽量组合多个核心属性(减少哈希冲突);
    • 保证 equals 相等的对象哈希值一定相等。
  3. wait/notify 避坑

    • while 循环判断条件(而非 if),避免虚假唤醒;
    • 优先用 notifyAll() 而非 notify(),避免线程饥饿。
  4. clone () 避坑

    • 嵌套引用类型必须实现深克隆,否则数据共享导致 bug;
    • 重写时修改访问修饰符为 public

总结

  1. 核心基础:Object 是所有 Java 类的父类,11 个方法定义了对象的通用行为;
  2. 重写核心:toString ()、equals ()、hashCode () 是必重写方法,需严格遵守规则;
  3. 线程相关:wait ()/notify ()/notifyAll () 是线程同步的核心方法,必须在 synchronized 中调用;
  4. 避坑关键:equals/hashCode 强绑定、wait 用 while 判断条件、clone 区分浅 / 深克隆。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐