答案是:==比较引用类型时本质比较内存地址,而非业务值;integer在-128~127范围因缓存复用导致==可能为true,超出该范围则新建对象使==返回false,形成“128陷阱”。

直接用双等号(==)比较两个对象元素,本质是在比它们在内存中的物理地址是否相同,而不是比业务上“值是否相等”。一旦这两个对象是分别创建、反序列化、解析或加载出来的——哪怕内容完全一样——它们大概率落在堆内存不同位置,== 就会返回 false,导致本该匹配成功的逻辑被跳过。这不是偶然错误,而是设计使然。
为什么叫“物理地址陷阱”
== 对引用类型而言,就是 CPU 层面对两个地址寄存器值的直接比对。它不看字段、不看内容、不关心业务含义,只认“是不是同一个内存块”。这个行为稳定、高效,但用错场景就极危险。
-
Integer a = 128; Integer b = 128;→a == b是false(各自 new 出来) -
Integer x = 100; Integer y = 100;→x == y可能是true(缓存复用)
同一套代码,仅因数值跨了 -128~127 边界,行为就突变,调试时很难复现。
常见掉坑的典型场景
- 从数据库查出一个
User对象,再用 JSON 解析前端传来的同 ID 用户数据,拿==判断是否“是同一个人” → 肯定失败 - Redis 存取后反序列化出的对象,和原始对象用
==比较 → 地址完全不同 - MyBatis 查询结果每次新建实体,集合里用
==手动遍历查找 → 查不到 -
switch (status)中status是Integer包装类,case 值写case 1:→ 实际依赖==,超出缓存范围就漏匹配
怎么安全地替代
- 数值型包装类(
Integer/Long等):优先拆成基本类型比,如a.intValue() == b.intValue();或统一用Objects.equals(a, b) - 字符串:永远用
.equals()或Objects.equals(),别信"abc" == "abc"的侥幸 - 自定义对象作匹配依据:必须重写
equals()和hashCode(),且确保逻辑一致;集合操作(如contains、remove)才真正有效 - null 安全:
Objects.equals(a, b)内部自动判空,不会 NPE,比a != null && a.equals(b)更简洁可靠
什么时候才能放心用 ==
只在明确需要判断“是不是同一个实例”时:
- 检查
obj == null - 枚举值比较:
day == DayOfWeek.MONDAY(JVM 保证单例) - 单例对象校验或监听器去重
- 字符串字面量之间比较(但一旦涉及
new、substring、trim()等操作,立刻失效)
本质上,== 是 JVM 的指针语义,不是业务的相等语义。把地址比较当值比较用,就是主动走进物理地址陷阱。











