
Java 中所有类都隐式继承 Object,它的核心方法不是“可选知识点”,而是日常编码中每天都在调用却常被忽略契约的底层接口。不理解 equals 和 hashCode 的绑定关系,HashMap 里存进去就找不到;不重写 toString,日志里只看得到 com.example.User@1b6d3586 这种无意义字符串——这不是风格问题,是功能性缺陷。
为什么重写 equals 必须同时重写 hashCode
这是 Java 集合框架最常踩的坑:对象逻辑相等,但放进 HashSet 或作为 HashMap 的 key 后无法命中。
- 根本原因:
HashMap先用hashCode()定位桶(bucket),再用equals()在桶内比对。如果两个对象equals返回true,但hashCode不同,它们会被散列到不同桶里,get()永远查不到 - 错误示范:只重写
equals,hashCode仍走 Object 默认实现(基于内存地址),导致同一业务对象每次 new 出来哈希值都不同 - 正确做法:用
Objects.hash(field1, field2),它会自动处理null,且与equals中的字段完全一致
toString 不只是调试用,它是日志和序列化的第一道输出门
System.out.println(obj)、log.info("user: {}", user)、甚至 Jackson 序列化失败时的 fallback 描述,全依赖 toString 的返回值。
- 默认实现返回
类名@十六进制哈希码,对排查问题毫无帮助 - 重写时优先包含「不可变标识字段」,比如
User{id=123, name='张三'},而不是把敏感字段(密码、token)或大集合(orders列表)全塞进去 - 避免在
toString里触发副作用:不要调用远程接口、不要修改对象状态、不要抛异常(否则log.debug可能直接崩掉)
getClass() != instanceof:类型判断该用哪个
在 equals 方法里做类型检查时,用 getClass() != obj.getClass() 还是 !(obj instanceof MyClass),直接影响继承体系下的等价性语义。
立即学习“Java免费学习笔记(深入)”;
-
getClass()严格要求**运行时类完全相同**,适合“值对象”场景(如Point子类不应和父类equals) -
instanceof允许子类实例通过检查,但容易破坏对称性:若SubClass.equals(SuperClass)为true,反过来可能不成立 - 《Effective Java》明确推荐:除非你设计的是可扩展的开放类(极少见),否则一律用
getClass()做类型校验
最容易被跳过的其实是 clone() 的访问权限——它被声明为 protected,连子类外部都调用不了,更别说跨包使用;而现代 Java 已普遍用构造器复制或 record 类替代浅拷贝需求。真正需要深究的,永远是那三个方法之间咬合的契约:谁变了,谁必须跟着变,漏掉一个,整个对象行为就不可靠。






