重写 equals 方法必须严格遵守自反性、对称性、传递性、一致性及 null 处理五大契约:先用 this == obj 保证自反性;用 getclass() 而非 instanceof 维护对称性;避免子类特有字段破坏传递性;equals 与 hashcode 必须一致且安全处理 null。

重写 equals 方法时,必须严格遵守自反性、对称性、传递性、一致性以及对 null 的处理这五大契约。仅靠“字段相等就返回 true”远远不够,容易在继承、多态或集合操作中引发逻辑错误或运行时异常。
自反性:确保对象永远等于自身
自反性要求 x.equals(x) 必须返回 true(前提是 x 非 null)。常见错误是未做非空校验或过早返回 false。
- 第一步总是用
this == obj判断是否为同一引用,直接返回true—— 这既高效又天然满足自反性 - 若跳过该判断,而先检查
obj == null或类型转换,可能因后续逻辑误判导致违反自反性(例如误将自身当作不合法参数拒绝)
对称性:避免子类与父类比较时失衡
对称性要求 x.equals(y) 与 y.equals(x) 返回结果一致。问题常出现在不严谨的类型检查上,尤其涉及继承关系时。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 使用
getClass() == obj.getClass()而非instanceof—— 后者在子类重写equals时易破坏对称性(如SubClass.equals(SuperClass)为true,但反向调用却为false) - 若业务确实需要跨类型比较(如所有
Shape子类都可与抽象Shape实例比较),则必须在所有相关类中统一实现、双向兼容,否则宁可放弃这种设计
传递性:防止类型层级中出现逻辑断层
传递性要求:若 x.equals(y) 且 y.equals(z),则必须有 x.equals(z)。它最容易在引入新子类或添加字段时被破坏。
- 当类可被继承时,不要在
equals中加入子类特有字段的比较;否则Parent.equals(Child1)和Child1.equals(Child2)可能都为true,但Parent.equals(Child2)却为false - 推荐做法:将类声明为
final,或采用组合替代继承;若必须可继承,应只基于父类已有字段判断,并明确文档说明子类不得扩展equals语义 - 若字段本身是可变对象(如
List),需确保其equals实现也满足契约,否则传递性会间接失效
配套要点:hashCode 与 null 安全不可遗漏
equals 契约生效的前提是与其配套的 hashCode 保持一致:相等的对象必须有相同哈希码;同时,任何重写都必须正确处理 null 参数。
- 在
equals开头加if (obj == null) return false;—— 这是防御性编程底线 - 重写
hashCode时,只使用参与equals判断的相同字段,且用Objects.hash(...)简化实现并避免空指针 - 避免在
equals中调用可能抛出异常的方法(如未判空的obj.toString()),否则null检查形同虚设
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










