== 比较引用是否指向同一内存地址,equals() 比较内容是否逻辑相等;字符串字面量因常量池可能使 == 为 true,但应始终用 equals() 判断内容;自定义类需重写 equals() 和 hashCode() 才能按业务逻辑比较。
区分
和
的关键,不在于“哪个更好”,而在于“它们本来就在做不同的事”:一个看是不是同一个东西(地址),一个看内容是不是一样(逻辑相等)。
字符串比较:常量池让 == 有时“碰巧”为 true
String 是最典型的例子,但容易让人误解。根本原因在于 Java 的字符串常量池机制:
比较的是两个引用是否指向堆中同一块内存地址。比如
一定是
,因为每次
都新建对象,地址不同。
在 String 类中已被重写,逐字符比内容。所以上面那行的
返回
。
但像
这种字面量赋值,JVM 会把它们放进常量池并复用同一对象,此时
也返回
——这不是 equals 生效了,而是地址真相同了。
别依赖这种“碰巧”。生产代码中判断字符串内容是否一致,一律用
;用
只适合极少数场景,比如确认是否是某个特定的空字符串常量或单例标识。
自定义对象比较:默认 equals() 和 == 完全等价
如果你写了一个类,比如
,没动过
方法,那么它的行为就和
类里的一模一样:
—— 看,底层就是
。
所以
一定返回
,哪怕所有字段都一样,因为是两个不同对象。
要让它按业务规则比较(比如姓名+年龄相同就算相等),必须手动重写
,且必须同时重写
,否则放进
或
会出现存进去却找不到的问题。
什么时候该用 ==,什么时候该用 equals()?
看目的,不看类型:
想确认两个变量是不是指向同一个实例(比如检查回调是否重复注册、校验单例对象、判断状态机当前是否处于某个固定实例状态),用
。
想确认两个对象在业务意义上是否“相等”(比如两个订单金额和商品清单一样、两个用户身份证号相同),必须用重写后的
。
基本类型(
、
等)只能用
,没有
方法;包装类(
、
)建议统一用
,避免因缓存(如 -128~127)导致行为不一致。
为防空指针,推荐用
,它内部自动处理了
安全问题。
==equals()==new String("hello") == new String("hello")falsenewequals()equals()trueString s1 = "hello"; String s2 = "hello";s1 == s2trueequals()==Personequals()Objectpublic boolean equals(Object obj) { return (this == obj); }==new Person("张三", 25).equals(new Person("张三", 25))falseequals()hashCode()HashMapHashSet==equals()intchar==equals()IntegerCharacter.equals()Objects.equals(a, b)null