== 比较的是对象引用(内存地址),不是内容;equals() 默认也等价于 ==,需重写以按业务字段比较,并必须同步重写 hashCode() 以满足哈希契约。

直接用 == 比较两个对象,到底在比什么
它比的是内存地址,不是内容。哪怕两个 String 字面值完全一样,如果一个是字面量、一个是 new String("abc"),== 就返回 false。
常见错误现象:user1 == user2 返回 false,但你明明看到字段一模一样;或者测试里用 assertThat(obj1 == obj2) 断言失败,以为逻辑错了,其实是误用了引用比较。
- 只有
String字面量、小范围整数(-128~127)的Integer等少数情况,==才可能碰巧“看起来对”——靠的是 JVM 的常量池或缓存机制,不是语义保证 - 自定义类几乎从不适用
==判断“相等”,除非你明确想确认是不是同一个实例(比如单例校验、状态机中的身份判断) - 集合操作如
list.contains(obj)、set.add(obj)内部都依赖equals(),不是==;用错会导致查不到、重复添加等问题
equals() 不重写就用 Object 默认实现,后果很实在
默认的 equals() 方法来自 Object 类,内部就是 return (this == obj); —— 和 == 完全等价。
所以如果你没重写,所有“按字段相等”的需求都会失效。比如两个 User 对象 id 和 name 都相同,user1.equals(user2) 依然返回 false。
立即学习“Java免费学习笔记(深入)”;
- IDE 自动生成的
equals()(如 IntelliJ 的Alt+Insert)通常靠谱,但要注意勾选哪些字段:ID 类字段一般要包含,时间戳、缓存字段、临时状态字段通常不该参与比较 - 如果类有继承关系,父类已重写
equals(),子类重写时必须调用super.equals(other),否则可能破坏对称性 - 别在
equals()里做耗时操作(比如读数据库、远程调用),它可能被高频调用(如HashMap.get())
重写 equals() 必须同步重写 hashCode(),不然 HashMap、HashSet 直接失灵
hashCode() 是 equals() 的配套契约:如果两个对象 equals() 返回 true,它们的 hashCode() 必须相同;反之不成立。
不遵守这个规则,对象放进 HashMap 后可能再也取不出来——因为哈希桶位置算错了。
- IDE 自动生成的
hashCode()通常和equals()用的字段一致,只要两者字段选择同步更新,基本不会出问题 - 避免用可变字段(如
status、lastModified)参与hashCode()计算,否则对象放入HashSet后改了字段,就再也找不到了 - 如果类是不可变的(
final字段 + 无 setter),hashCode()可以懒计算并缓存,提升性能
Objects.equals() 是 null 安全的快捷写法,但别滥用在核心逻辑里
写 Objects.equals(a, b) 能自动处理 a 或 b 为 null 的情况,比手写 a != null && a.equals(b) 简洁。
但它只是工具方法,底层还是调用你重写的 equals()。如果那个 equals() 本身写错了,Objects.equals() 也救不了。
- 适合用在参数校验、日志拼接、DTO 转换等非关键路径;不要用它掩盖
equals()实现缺陷 - 注意它不能替代字段级空检查:比如你想确保
user.getName()不为null再比较,得先判空,而不是依赖Objects.equals(user.getName(), "admin") - 泛型擦除下,
Objects.equals()对原始类型(int,boolean)会自动装箱,高频调用时有轻微 GC 开销,不过绝大多数场景可忽略
最容易被忽略的一点:重写 equals() 前,先想清楚“相等”的业务定义是什么。ID 相同就算相等?还是所有字段都得一致?是否允许部分字段忽略(比如创建时间)?这个定义一旦定下来,hashCode()、序列化、数据库唯一约束都要跟上,不是改两个方法就完事的。








