重写 equals 前必须重写 hashcode,否则 hashmap、hashset 中会出现查找失败或重复添加;equals 签名须为 public boolean equals(object obj),首行检查 this == obj,再判 null 和 getclass(),字段比较用 objects.equals,数组用 arrays.equals,且参与 equals 的字段必须参与 hashcode 计算。

重写 equals 前必须先重写 hashCode
Java 中如果只重写 equals 而不重写 hashCode,会导致对象在 HashMap、HashSet 等集合中行为异常:两个逻辑相等的对象可能被散列到不同桶里,从而查不到、重复添加。
- 违反
Object.hashCode合约:相等的对象必须有相同哈希值 - 常见错误现象:
set.contains(obj)返回false,尽管obj.equals(existing)是true - IDE(如 IntelliJ)生成的
equals通常会同步生成hashCode,但手动写时极易漏掉 - 若字段参与
equals比较,就必须参与hashCode计算——哪怕只是简单相加或用Objects.hash(...)
equals 方法签名和空值检查不能错
签名必须是 public boolean equals(Object obj),少一个 Object 类型参数,或写成 equals(MyClass other),就不是重写而是重载,运行时根本不会调用你写的版本。
- 第一行必须做
if (this == obj) return true;:处理自反性,也避免后续空指针 - 紧接着
if (obj == null || getClass() != obj.getClass()) return false;:既防NullPointerException,又保证类型严格一致(用getClass()而非instanceof,除非你明确支持子类对等) - 错误示例:
if (obj instanceof MyClass)在继承场景下可能破坏对称性,比如SubClass.equals(MyClass)为true,但反过来不成立
字段比较要区分基本类型、引用类型和 null 安全
字段逐个比较时,不能直接用 == 或 .equals() 一概而论,否则会触发空指针或误判。
- 基本类型(
int、boolean等)用== - 引用类型(包括包装类、
String、自定义对象)统一用Objects.equals(field1, field2):它内部已处理任一参数为null的情况 - 数组字段要用
Arrays.equals(arr1, arr2),而不是.equals()(那是引用比较) - 忽略某些字段?可以,但得有明确业务理由(比如时间戳、ID 自动生成字段),并在文档里说明“该字段不影响逻辑相等性”
别忘了测试对称性、传递性和一致性
这些不是理论要求,而是真实踩坑点。JVM 不校验,但业务出错时很难定位。
- 对称性失败典型场景:用了
instanceof+ 子类,或一方字段多做了非空校验另一方没做 - 传递性问题常出现在浮点数比较(没用
Double.compare)、或嵌套对象的equals实现不一致 - 一致性要求:只要参与比较的字段没变,多次调用
equals必须返回相同结果。所以别在equals里读数据库、改状态、或依赖随机/时间等可变值 - 建议写几个最小单元测试:互换参数顺序、加入 null、构造两个仅一个字段不同的实例,观察是否符合预期
最容易被忽略的是:当类被用作 Map 的 key 或放入 Set 后,再修改影响 equals 判断的字段——这会让集合内部结构失效,且没有任何警告。








