重写equals必须严格遵守五条契约:自反性(this==obj优先判断)、对称性(用getclass()替代instanceof)、传递性与一致性(仅用不可变字段比较)、非空性(obj==null返回false)、hashcode同步(用objects.hash且逻辑一致)。

重写 equals 方法不是加个 if 判断就完事,而是要让对象在集合、缓存、比较等场景下行为可靠。这五条契约是 Java 规范强制要求,不是可选建议——违反任何一条,HashSet 找不到元素、HashMap.get() 返回 null、ArrayList.contains() 失败,都是直接后果。
自反性:自己必须等于自己
任意非 null 对象 x,调用 x.equals(x) 必须返回 true。
- 常见破环点:字段比较前做了
trim()或toLowerCase(),但只对参数对象操作,没对this做同样处理 - 正确做法:若对参数做归一化(如忽略大小写),
this也必须同步归一化,或统一在比较前对双方处理 - 最稳妥写法:先判引用相等 ——
if (this == obj) return true;,天然满足自反性
对称性:你认我,我也得认你
如果 a.equals(b) 是 true,那么 b.equals(a) 也必须是 true。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 最大陷阱:用
instanceof判断类型时不对等。例如父类Person的equals接受Student子类实例,但Student.equals(new Person())却因类型检查失败返回false - 解决思路:用
getClass() == obj.getClass()替代instanceof,确保类型完全一致;或在子类中显式兼容父类(需双向支持) - 不推荐“单向互通”:比如
CaseInsensitiveString同时接受String,但String.equals(cis)仍走原逻辑,必然破坏对称性
传递性与一致性:别让相等关系“飘忽不定”
传递性指:若 x.equals(y) 且 y.equals(z) 都为 true,则 x.equals(z) 也必须为 true。
一致性指:只要参与比较的字段没变,反复调用结果不能变。
- 传递性破环典型场景:继承 + 不同精度比较。例如
Point类和带color字段的ColoredPoint,若Point.equals(ColoredPoint)忽略颜色,而ColoredPoint.equals(ColoredPoint)比颜色,就会导致 A=B、B=C 成立但 A≠C - 一致性风险:在
equals中读取外部状态(如当前时间、随机数、文件内容)、或调用可能改变自身状态的方法 - 关键原则:只基于不可变字段或稳定字段比较;避免在
equals中做任何副作用操作
非空性:对 null 要有明确态度
任何对象调用 obj.equals(null) 必须返回 false,绝不能抛 NullPointerException。
- 标准写法开头加:
if (obj == null) return false; - 后续字段访问前,也要确保不触发 NPE。例如比较字符串字段
name,应写成Objects.equals(this.name, other.name),它内部已处理null安全 - 不要依赖 IDE 自动生成的
equals就万事大吉——检查生成代码是否包含null判定,尤其当字段本身可为空时
别忘了 hashCode:equals 和 hashCode 必须同进退
如果两个对象 equals 返回 true,它们的 hashCode 必须相同;反之不成立。
-
hashCode必须使用和equals完全相同的字段计算 - 推荐用
Objects.hash(field1, field2, ...),简洁且自动处理null - 如果
equals中用了归一化逻辑(如忽略空格),hashCode也要用归一化后的值计算,否则哈希表定位失败
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










