Java Object 的几个根方法
更新时间:2026-09-21。equals/hashCode 直接决定 HashMap 行为,原理见 HashMap 底层原理;不可变设计见 final 与不可变。
一、== 与 equals
==:基本类型比值;引用类型比地址(是不是同一个对象)。equals:Object 默认实现就是==,但 String/包装类等重写成比较内容。
String a = new String("x"), b = new String("x");
a == b; // false:两个对象
a.equals(b); // true:内容相同值类若不重写 equals,比较的仍是地址,放进集合去重会失效。
二、equals 的五条约定
重写必须满足:自反(x.equals(x) 为真)、对称(x.equals(y) == y.equals(x),父子类混比最易破坏)、传递、一致(多次调用稳定)、x.equals(null) 为 false。
标准套路:先判 ==、再判 null 与类型(getClass 或 instanceof)、再逐字段比较。
三、hashCode:重写 equals 必须一起重写
约定:equals 相等的两个对象 hashCode 必须相等;不相等的对象 hashCode 尽量分散。
只重写 equals 不重写 hashCode 是最经典 bug:HashMap/HashSet 先按 hashCode 定位桶,两个「内容相等但 hashCode 不同」的对象落到不同桶,集合认为它们不相等——put 进两个「相同」键、contains 查不到。
record Point(int x, int y) {} // record 自动生成正确的 equals/hashCode/toString现代 Java 优先用 record(值对象自动正确实现三方法),或让 IDE/Lombok 生成,别手写漂移。
四、toString
默认是 类名@十六进制hash,没信息量。重写(或 record/IDE 生成)后日志、调试、异常消息直接可读。注意别在 toString 里打印敏感字段(密码、身份证)。
五、clone:能不用就不用
Object.clone() 是浅拷贝:基本类型复制值,引用字段只复制引用,新旧对象共享内部集合/数组,改一边另一边也变。而且 Cloneable 是设计怪异的标记接口。
更推荐:拷贝构造/拷贝工厂 new User(old),意图清晰;需要深拷贝就手动复制可变字段,或经 JSON/序列化中转。
六、finalize 已废弃
finalize() 执行时机不保证、可能拖慢 GC、还能复活对象,Java 9 起 @Deprecated、Java 18 起默认禁用。资源清理用 try-with-resources / AutoCloseable 或 Cleaner。
七、常见坑
- 只重写 equals 忘了 hashCode:HashMap/HashSet 行为诡异,必须成对重写。
- equals 里用 getClass 与子类比较:破坏对称性;想兼容子类用 instanceof 但要谨慎 LSP。
- 把可变字段纳入 hashCode:对象进 HashMap 后字段变化导致再也找不到(键应不可变)。
- 以为 clone 是深拷贝:默认浅拷贝,共享可变成员。
- 重写参数写成具体类型而非 Object(
equals(Point p)):这是重载不是重写,加@Override能立刻暴露。
总结
== 对引用比地址、equals 默认也是比地址但值类应重写为比内容(满足自反对称传递一致非空五约定),核心铁律是重写 equals 必须同时重写 hashCode,否则相等对象落在不同哈希桶导致 HashMap/HashSet 去重失败;优先用 record 或 IDE/Lombok 生成三方法,toString 只为可读且别打印敏感信息,clone 只是浅拷贝且接口设计糟糕、应改用拷贝构造并手动深拷贝可变字段,finalize 已废弃、资源释放用 try-with-resources。