Object类
Object类
在 Java 的世界里,Object
是所有类的顶级父类。不管你写的是学生类、订单类,还是数据库连接类,只要是类,就间接或直接继承了 Object
。这意味着 Object
中定义的方法是每个 Java 对象都拥有的“基础能力”。
作为一名 Java 后端开发者,无论你是刚入门还是有一定经验,理解和正确使用 Object
类中的几个核心方法,对于编写高质量、可维护的代码至关重要。今天我们重点介绍以下几个方法:
toString()
:对象转字符串,用于调试与日志输出。equals()
与hashCode()
:对象比较与集合处理的核心。wait()
、notify()
与notifyAll()
:线程通信的基础方法。
一、toString()
方法作用:
toString()
方法的作用是返回对象的字符串表示形式,默认实现返回的格式是:
类的全名@哈希值的十六进制表示
例如:
Student student = new Student();
System.out.println(student);
输出可能是:
com.example.Student@1a2b3c4d
为什么要重写它?
默认的输出对我们了解对象的信息几乎没有帮助。在开发中我们往往需要查看对象的实际属性内容,这就需要重写 toString()
方法。
示例:
public class Student {private String name;private int age;@Overridepublic String toString() {return "Student{name='" + name + "', age=" + age + "}";}
}
这样打印对象时就能看到具体信息了:
Student{name='家宇', age=20}
建议:
- 永远在实体类(POJO)中重写
toString()
,尤其是用于日志、调试、打印输出时。 - 搭配 Lombok 的
@ToString
注解可以快速实现。
二、equals() 与 hashCode() —— 对象是否相等的“证据”
equals() 方法的默认行为:
默认是比较内存地址是否相同(也就是两个对象是否是“同一个”)。
Object obj1 = new Object();
Object obj2 = new Object();
System.out.println(obj1.equals(obj2)); // false
实际场景:对象内容相同,也要被认为是“相等”
例如我们要判断两个学生是否是同一个人,应该比较的是姓名和年龄等关键字段,而不是内存地址。
equals() 示例:
@Override
public boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Student student = (Student) obj;return age == student.age && Objects.equals(name, student.name);
}
它返回一个整数,用作对象的“哈希签名”,常用于:
HashMap
查找键值对HashSet
去重判断HashTable
的存储位置定位
为什么要一起重写?
因为 Java 的集合类比如 HashMap
判断两个键是否相等,是先比 hashCode()
,再比 equals()
。你只重写了 equals()
不重写 hashCode()
,集合类的行为就会出错。
示例():
@Override
public int hashCode() {return Objects.hash(name, age);
}
💡 实践建议:
- 每当你重写
equals()
,必须同时重写hashCode()
。 - IDE(如 IntelliJ IDEA)可自动生成这两个方法。
三、wait、notify 和 notifyAll —— 线程通信的基石
基本介绍:
这三个方法是 Object
类中与线程协作相关的方法,只能在同步代码块或方法中使用。作用是:
方法 | 含义 |
---|---|
wait() | 当前线程进入“等待状态”,释放锁 |
notify() | 唤醒一个正在等待这个对象锁的线程 |
notifyAll() | 唤醒所有等待的线程 |
使用场景:生产者-消费者模型
消费者发现没有商品 → wait()
生产者放入商品 → notify()
唤醒消费者
示例代码片段:
Synchronized (lock) {while (queue.isEmpty()) {lock.wait();}Object item = queue.remove();lock.notify();
}
⚠ 注意事项:
- 必须在同步块中调用,否则会抛
IllegalMonitorStateException
。 wait()
会释放锁,sleep()
不会。notify()
唤醒的线程是随机的,多线程协作时推荐使用notifyAll()
。
总结
方法 | 用途 | 是否常重写 | 使用场景 |
---|---|---|---|
toString() | 输出对象信息 | 是 | 打印日志、调试 |
equals() | 对象内容比较 | 是 | 判断业务对象是否“相等” |
hashCode() | 哈希容器索引 | 是 | HashMap、HashSet |
wait() 、notify() | 线程通信 | 否 | 多线程协作模型 |
==和equals有什么区别?
一句话总结:
==
比较的是两个变量是否指向同一个内存地址equals()
比较的是两个对象“所表示的内容”是否一致(可重写)
适用场景详解
基本数据类型
对于基本类型(如 int
, char
, double
等):
int a = 5;
int b = 5;
System.out.println(a == b); // true
此时 ==
是直接比较两个变量的值,等价于数学意义上的“是否相等”。
⚠️ equals() 不适用于基本类型,除非使用包装类(如 Integer、Double 等)。
引用类型(对象)
String str1 = new String("java");
String str2 = new String("java");System.out.println(str1 == str2); // false:引用不同
System.out.println(str1.equals(str2)); // true:内容相同
解释:
==
判断的是内存地址:两个new
出来的对象肯定不一样。equals()
被String
类重写,比较的是字符串内容。
深入 equals() 与 hashCode()
默认行为:
在 Object
中,equals()
默认行为是:
public boolean equals(Object obj) {return this == obj;
}
也就是说,如果你不重写 equals()
,它其实就等于 ==
。
为什么要重写 equals()?
举例:比较两个学生对象是否“相等”,业务上我们关注的是“姓名”和“身高”是否一致。
Student s1 = new Student("家宇", 180);
Student s2 = new Student("家宇", 180);System.out.println(s1.equals(s2)); // 默认 false:两个对象不同
通过重写 equals()
方法,可以实现基于属性内容的比较逻辑。
@Override
public boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Student student = (Student) obj;return height == student.height && Objects.equals(name, student.name);
}
hashCode():哈希集合的关键辅助方法
默认行为:
hashCode()
方法返回一个 int
整数,用于描述该对象的哈希值。默认实现与对象的内存地址有关。
Object obj = new Object();
System.out.println(obj.hashCode()); // 类似 1292482381
为什么 hashCode() 很重要?
在使用 HashMap
、HashSet
、Hashtable
这类集合时:
- 首先使用
hashCode()
判断对象是否“可能相同” - 如果哈希值一样,再调用
equals()
进行最终确认
如果你只重写了 equals()
而没有重写 hashCode()
,集合类将认为两个内容相同的对象是两个不同的元素!
集合中的经典陷阱
情景描述:
Set<Student> students = new HashSet<>();Student s1 = new Student("家宇", 180);
Student s2 = new Student("家宇", 180);students.add(s1);
students.add(s2);
你可能期望只有一个元素,但集合实际存储了两个。
原因分析:
s1.equals(s2)
为true
(你重写了 equals)- 但没有重写
hashCode()
,它们的哈希值不一致 - HashSet 认为是两个不同的对象 → 都加入集合正确实践方式:重写 equals() + hashCode()
public class Student {private String name;private int height;@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Student student = (Student) obj;return height == student.height && Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, height);}
}
现在 HashSet
会正确识别两个属性相同的对象,避免重复。
equals() 与 hashCode() 的契约关系
Java 官方文档规定:
- 如果两个对象通过
equals()
判断相等,则它们的hashCode()
也必须一致 - 如果两个对象的
hashCode()
不一样,它们一定不会被认为是相等
否则,将破坏集合类的正常功能,尤其是在哈希集合中。