重写 equals 和 hashcode 是为保障自定义对象在多态集合中正确识别、去重和查找;需在抽象父类定义核心字段并实现,子类通过 super.equals() 和 super.hashcode() 继承扩展,确保语义一致与哈希契约。

在 Java 多态体系中,重写 equals 和 hashCode 不是为了“支持多态”,而是为了确保自定义对象在集合(如 HashMap、HashSet)中能正确参与多态行为——即用父类引用操作子类实例时,集合仍能按业务语义准确识别、去重、查找。
多态场景下 equals 必须满足语义一致性
当父类声明、子类实例化(如 Shape s = new Circle(...)),且该对象作为集合 key 或元素时,equals 的判断逻辑必须覆盖所有可能参与比较的子类。否则会出现:同一业务含义的对象被误判为不等。
- 在抽象父类中定义参与比较的核心字段(如
id、type),并基于这些字段实现equals - 子类重写
equals时,先调用super.equals(other),再检查自有字段(如radius、color) - 务必使用
instanceof判断类型,避免Class.isInstance或强制转型异常;对null和自身引用(this == obj)做前置校验
hashCode 必须与 equals 严格同步且可继承
如果两个对象 equals 返回 true,它们的 hashCode 必须相同——这是哈希集合正常工作的前提。在多态继承链中,这个契约需逐级延续。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 抽象类的
hashCode只计算自身参与equals的字段,例如Objects.hash(id, name) - 子类重写
hashCode时,不能只算自有字段,而应组合父类哈希值:Objects.hash(super.hashCode(), radius, color) - 漏掉任一
equals中使用的字段,会导致相同业务对象被散列到不同桶中,HashMap.get()找不到、HashSet.contains()返回false
集合操作中多态引用的实际影响
使用父类引用存放子类对象(如 Set<shape> shapes = new HashSet(); shapes.add(new Circle(...));</shape>),集合内部调用的是运行时实际类型的 equals 和 hashCode。因此:
- 若子类未重写这两个方法,会退化为
Object默认行为(地址比较),导致逻辑错误 - 若只有父类重写了但子类没重写,子类实例的
hashCode可能与父类不一致,破坏契约 - 推荐在抽象类中提供
final的equals模板(含instanceof和字段比对),子类只需扩展自有字段逻辑
验证是否真正适配多态集合
写一个简单测试,覆盖父类引用、不同子类实例、集合增删查全流程:
- 向
HashSet<shape></shape>添加两个内容相同的Circle实例,检查 size 是否为 1 - 用
Circle实例作 key 存入HashMap<shape string></shape>,再用另一个相等的Circle实例调用get(),确认能命中 - 混合添加
Circle和Square,确保它们即使有相同id也不被误判相等(依赖子类正确扩展equals)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










