Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

    • Java概述
    • Java语法
    • 面向对象编程
    • Java数组
    • String字符串
    • 异常处理
    • 基本类型与包装类
    • Object类与常用方法
      • 1. Object 在类型体系中的位置
      • 2. Object 常用方法
      • 3. toString
      • 4. equals
      • 5. hashCode
      • 6. equals 和 hashCode 与 HashMap
      • 7. getClass 与 instanceof
      • 8. clone
      • 9. wait、notify、notifyAll
      • 10. finalize
      • 11. Lombok 与 Object 方法
      • Tips 快问快答
    • 泛型
    • 枚举与注解
    • 反射
    • 值传递与引用
    • Lambda表达式与函数式接口
    • Stream API
    • Optional与空值处理
    • 日期时间API
    • Java现代语法特性
  • Java集合

  • Java并发

  • Java IO

  • JVM

目录

Object类与常用方法

Object 是 Java 类继承体系的根类。只要一个类没有显式继承其他类,就默认继承 Object。

public class User {
}

等价于:

public class User extends Object {
}

理解 Object 的方法,能帮助你掌握对象比较、哈希容器、日志输出、类型判断、克隆和线程协作等基础机制。

# 1. Object 在类型体系中的位置

Object
├─ String
├─ Number
│  ├─ Integer
│  ├─ Long
│  └─ Double
├─ Throwable
│  ├─ Exception
│  └─ Error
└─ 自定义类
   └─ User

任何对象都可以赋值给 Object 引用:

Object value = "hello";
Object user = new User();

但 Object 引用只能直接调用 Object 定义的方法。如果需要调用子类方法,要进行类型判断和转换。

if (value instanceof String text) {
    System.out.println(text.length());
}

# 2. Object 常用方法

方法 作用
toString() 返回对象字符串表示
equals(Object obj) 判断对象是否相等
hashCode() 返回对象哈希值
getClass() 返回运行时类对象
clone() 克隆对象
wait() 让当前线程等待
notify() 唤醒一个等待线程
notifyAll() 唤醒所有等待线程
finalize() 对象回收前回调,已不推荐使用

其中 equals、hashCode、toString 是日常业务开发最常重写的方法。

# 3. toString

toString() 用于返回对象的文本表示。

默认实现通常类似:

类名@哈希值十六进制

示例:

User user = new User();
System.out.println(user); // com.example.User@1b6d3586

更有用的做法是重写:

public class User {
    private Long id;
    private String name;

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

开发建议:

  • 日志中输出对象时,toString 应包含关键定位字段。
  • 不要在 toString 中输出密码、密钥、身份证等敏感信息。
  • 不要在 toString 中触发远程调用或复杂计算。

# 4. equals

Object 默认的 equals 与 == 类似,比较引用是否相同。

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

很多业务对象需要按业务字段判断相等。

public class User {
    private Long id;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof User other)) {
            return false;
        }
        return Objects.equals(this.id, other.id);
    }
}

equals 必须满足约定:

约定 含义
自反性 x.equals(x) 必须为 true
对称性 x.equals(y) 与 y.equals(x) 结果一致
传递性 x=y 且 y=z,则 x=z
一致性 对象状态不变时,多次调用结果一致
非空性 x.equals(null) 必须为 false

# 5. hashCode

hashCode() 返回对象的哈希值,常用于哈希容器。

如果重写 equals,通常必须重写 hashCode。

规则:

规则 说明
equals 相等 hashCode 必须相等
hashCode 相等 equals 不一定相等
对象状态不变 hashCode 应保持稳定

示例:

public class User {
    private Long id;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof User other)) {
            return false;
        }
        return Objects.equals(id, other.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

# 6. equals 和 hashCode 与 HashMap

HashMap 查找 key 的过程可以简化为:

put(key, value)
   │
   ▼
计算 key.hashCode()
   │
   ▼
定位桶位置
   │
   ▼
如果桶中已有元素,用 equals 判断是否同一个 key

如果只重写 equals 不重写 hashCode,可能出现“明明相等却找不到”的问题。

Set<User> users = new HashSet<>();
users.add(new User(1L));
users.contains(new User(1L)); // 可能是 false

原因是两个相等对象的哈希值不同,被放到不同桶里。

关系图:

equals 相等
   │
   ▼
hashCode 必须相等
   │
   ▼
才能在 HashMap / HashSet 中稳定工作

# 7. getClass 与 instanceof

getClass() 返回对象运行时类型:

Object value = "hello";
System.out.println(value.getClass()); // class java.lang.String

instanceof 判断对象是否属于某个类型或其子类型:

if (value instanceof CharSequence) {
    // String 实现了 CharSequence
}

在 equals 中,两种写法语义不同:

if (obj == null || getClass() != obj.getClass()) {
    return false;
}

表示必须完全同类才相等。

if (!(obj instanceof User other)) {
    return false;
}

表示子类对象也可能参与相等比较。

选择时要考虑继承关系。如果类不是为了继承设计,使用 getClass 更严格;如果希望父子类按统一业务字段比较,使用 instanceof。

# 8. clone

clone() 用于创建对象副本,但它有几个历史包袱:

  • Object.clone() 是 protected。
  • 类必须实现 Cloneable 接口,否则可能抛 CloneNotSupportedException。
  • 默认是浅拷贝。

示例:

public class User implements Cloneable {
    private String name;

    @Override
    public User clone() {
        try {
            return (User) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }
}

浅拷贝:

原对象.address ─────┐
                    ▼
                 Address
                    ▲
副本.address ───────┘

如果对象内部有可变引用字段,浅拷贝后两个对象会共享内部对象。

实际开发中更推荐:

  • 使用拷贝构造器。
  • 使用静态工厂方法。
  • 使用序列化框架或映射工具做明确复制。
public User(User source) {
    this.id = source.id;
    this.name = source.name;
    this.address = new Address(source.address);
}

# 9. wait、notify、notifyAll

这三个方法用于线程间协作,必须在持有对象监视器锁时调用。

synchronized (lock) {
    while (!ready) {
        lock.wait();
    }
}

唤醒:

synchronized (lock) {
    ready = true;
    lock.notifyAll();
}

等待唤醒模型:

线程 A 持有 lock
  │ 条件不满足
  ▼
调用 wait,释放 lock,进入等待队列

线程 B 持有 lock
  │ 修改条件
  ▼
调用 notifyAll,唤醒等待线程

线程 A 重新竞争 lock
  │
  ▼
条件满足后继续执行

为什么用 while 而不是 if:

  • 线程可能被虚假唤醒。
  • 多个线程被唤醒后条件可能已被其他线程消费。
  • 重新获得锁时必须再次检查条件。

现代并发开发中,更常使用 java.util.concurrent 包下的工具,例如 Lock、Condition、CountDownLatch、BlockingQueue。

# 10. finalize

finalize() 曾经用于对象被 GC 前的清理回调,但已经不推荐使用。

问题:

  • 执行时机不确定。
  • 可能造成对象复活。
  • 影响 GC 性能。
  • 容易导致资源释放不及时。

释放文件、网络连接、数据库连接等资源,应使用 try-with-resources 或显式关闭。

try (Connection connection = dataSource.getConnection()) {
    // use connection
}

# 11. Lombok 与 Object 方法

项目中常用 Lombok 自动生成 toString、equals、hashCode。

@Data
public class UserDTO {
    private Long id;
    private String name;
}

注意:

  • 实体类使用 @Data 时,要确认 equals/hashCode 是否包含可变字段。
  • 双向关联对象生成 toString 可能递归。
  • 敏感字段需要排除。
  • JPA 实体的 equals/hashCode 要谨慎设计。

# Tips 快问快答

Q:所有 Java 类都继承 Object 吗? A:普通类最终都继承 Object。接口不继承 Object,但接口实现类仍然是对象。

Q:重写 equals 必须重写 hashCode 吗? A:通常必须。否则哈希集合和哈希映射可能工作异常。

Q:hashCode 相同代表对象相等吗? A:不代表。哈希冲突允许存在,还需要 equals 判断。

Q:toString 可以输出所有字段吗? A:不一定。敏感字段、过大字段、可能递归的字段都应避免直接输出。

Q:getClass 和 instanceof 哪个更适合 equals? A:取决于相等语义。需要严格同类用 getClass,允许子类型参与比较用 instanceof。

Q:clone 是深拷贝吗? A:默认不是。Object.clone() 是浅拷贝。

Q:为什么 wait 必须在 synchronized 中调用? A:因为调用线程必须持有对象监视器锁,才能释放锁并进入该对象等待队列。

Q:为什么等待条件要用 while 判断? A:防止虚假唤醒和条件被其他线程改变。

Q:finalize 能用来释放资源吗? A:不推荐。它执行时机不确定,应使用 try-with-resources 或显式关闭。

Q:Object obj = "abc" 后能直接调用 length() 吗? A:不能。编译期类型是 Object,只能调用 Object 方法;需要类型判断并转换为 String。

上次更新: 2026/06/24, 16:28:28
基本类型与包装类
泛型

← 基本类型与包装类 泛型→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式