必须同时重写 equals 和 hashcode,因为若 a.equals(b) 为 true,则 a.hashcode() 必须等于 b.hashcode();仅重写 equals 会导致 hashset 重复、hashmap 键失效等问题。

Java 中重写 equals 和 hashCode 不是为了“修改 Object 类本身”,而是**在自定义类中覆盖(override)这两个继承自 Object 的方法**,让对象按业务逻辑而非内存地址来判断相等性,并确保哈希集合(如 HashMap、HashSet)行为正确。
为什么必须一起重写
这两个方法有强契约约束:
- 如果
a.equals(b)返回true,那么a.hashCode()必须等于b.hashCode() - 如果
hashCode()不同,equals()一定返回false(这是哈希表快速排除的依据) - 只重写
equals而不重写hashCode,会导致对象在 HashSet 中重复、HashMap 中键失效等严重问题
重写 equals 的标准步骤
以 Person 类为例(含 name 和 age 字段):
- 先用
this == obj判断是否为同一引用,是则直接返回true - 检查参数是否为
null,或是否属于本类类型(getClass() != obj.getClass()),不满足则返回false - 强制转换后,逐个比较关键字段:基本类型用
==,引用类型用Objects.equals(a, b)(自动处理 null) - 确保满足自反性、对称性、传递性、一致性、非空性五项原则
示例代码:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
重写 hashCode 的推荐方式
核心是:**参与 equals 比较的字段,也必须参与 hashCode 计算**。
- 最安全简洁的方式是使用
Objects.hash(field1, field2, ...),它会自动处理 null 并组合散列值 - 避免手写复杂算法(如乘法加法链),除非有特殊性能或分布要求
- 不要在
hashCode中使用可变字段——一旦对象加入 HashSet 后修改了这些字段,哈希桶位置就错乱了
对应上面的 Person 类:
@Override
public int hashCode() {
return Objects.hash(name, age);
}
实际开发中的注意事项
很多坑都源于细节疏忽:
- IDE(如 IntelliJ IDEA 或 Eclipse)可自动生成符合规范的
equals和hashCode,建议优先使用,再人工核对字段选择 - Lombok 的
@EqualsAndHashCode注解能一键生成,但要注意指定include或exclude字段,避免误含数据库 ID 或时间戳等易变字段 - 若类有继承关系,父类已重写
equals/hashCode,子类扩展字段时需调用super.equals()和super.hashCode(),并确保类型检查用instanceof还是getClass()——通常用后者更严格
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











