必须测试equals和hashCode的契约、边界及集合场景:自反性、对称性、传递性、一致性、null安全;确保a.equals(b)为true时a.hashCode()==b.hashCode();验证HashSet/HashMap中去重与查找正确性。

光写对不等于测对。要真正验证 equals 和 hashCode 的行为是否可靠,得用测试覆盖契约、边界和集合场景三类关键点。
验证 equals 的五大契约必须逐条检查
不能只写个 p1.equals(p2) 看返回 true 就完事。每条原则都要独立构造用例:
-
自反性:对任意非 null 对象 x,断言
x.equals(x)为 true;还要测x.equals(null)必须为 false -
对称性:准备两个不同实例 x 和 y,断言
x.equals(y) == y.equals(x);特别注意类型不同时(比如子类 vs 父类),应返回 false 而非抛异常 -
传递性:找三个对象 x、y、z,满足
x.equals(y)和y.equals(z)均为 true,再验证x.equals(z)是否也为 true - 一致性:在字段未修改前提下,对同一对对象反复调用 equals,结果必须稳定;可循环 10 次断言结果不变
-
null 安全:所有可能传入 null 的位置(参数、字段值)都要覆盖,确保不抛
NullPointerException
确保 hashCode 与 equals 严格同步
这是最容易漏掉也最致命的一环。测试重点不是“算得对不对”,而是“是否守约”:
- 只要
a.equals(b)返回 true,就必须断言a.hashCode() == b.hashCode() - 反过来不强制,但可以加一条辅助检查:若
a.hashCode() != b.hashCode(),则a.equals(b)必须为 false(违反即 bug) - 对同一个对象多次调用
hashCode(),结果必须完全一致(哪怕中间改了无关字段) - 如果对象被放入 HashSet 后修改了参与 equals/hashCode 的字段,后续
contains()应该失效——这个“失效预期”本身也要测,证明你理解了哈希桶冻结机制
真实集合场景必须跑通
脱离 HashMap/HashSet 的测试是纸上谈兵。必须验证业务对象在集合中表现符合直觉:
立即学习“Java免费学习笔记(深入)”;
- 把两个逻辑相等的对象先后 add 进 HashSet,最终 size 应为 1;用
contains()查第二个对象应返回 true - 作为 HashMap 的 key 存一对值,再用另一个相等对象 get,必须能取到原 value
- 用 ArrayList 存多个相等对象,再转成 HashSet,大小应自动去重;再转回 List 并去重(
new ArrayList(new HashSet(list)))应保持元素唯一 - 如果类用了 Lombok 的
@EqualsAndHashCode,要确认它没把数据库 ID 或时间戳这类可变字段包含进去——手动写个测试,改 ID 后看hashCode()是否变了
用工具辅助但别依赖自动生成
IDE 或 Lombok 生成的代码省事,但容易埋雷:
- 检查生成的
hashCode()是否只用了equals()中实际比较的字段;多一个或少一个都会破约 - 数组字段必须用
Arrays.hashCode(),不能直接调array.hashCode();集合字段要用list.hashCode()(它已重写),但要注意顺序敏感 - 推荐在测试里加一行:打印出两个相等对象的
hashCode()值,肉眼确认相同;再打印不等对象的哈希值,观察分布是否不太集中(避免全返回 0 或常量) - 写一个通用断言方法,比如
assertEqualObjects(a, b),内部自动执行 equals + hashCode 一致性校验,复用到所有实体类测试中


















