news 2026/7/27 17:03:39

`==`、`equals` 和 Integer 缓存:为什么 `128 == 128` 是 false

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
`==`、`equals` 和 Integer 缓存:为什么 `128 == 128` 是 false

前言

先看一段能让人怀疑人生的代码:

Integera=127,b=127;System.out.println(a==b);// trueIntegerc=128,d=128;System.out.println(c==d);// false

同样的写法,只是把数值从127改成128==的结果就从true翻成了false。第一次见到的人几乎都会愣一下:这不科学。

其实一点都不玄。背后是两个知识点的叠加:==比的到底是什么,以及Integer有个-128~127的缓存。这篇文章把这两点讲透,看完你不仅能解释 127/128,还能躲开一整类"包装类型比较"的坑。

环境说明:本文基于 JDK 8。


一、先复现

把几种情况放一起,现象更清楚:

publicclassIntegerEqualsDemo{publicstaticvoidmain(String[]args){Integera=127,b=127;System.out.println("127 == 127 : "+(a==b));// trueIntegerc=128,d=128;System.out.println("128 == 128 : "+(c==d));// falseIntegere=newInteger(127),f=newInteger(127);System.out.println("new 127 : "+(e==f));// falseSystem.out.println("equals : "+c.equals(d));// true}}

输出:

127 == 127 : true 128 == 128 : false new 127 : false equals : true

三个反直觉的点:

  • 127 == 127true128 == 128却是false
  • new Integer(127) == new Integer(127)都是false——明明值一样;
  • 但只要改用equals,全都是true

问题就一个:==到底在比什么,为什么它对Integer这么"不讲道理"?


二、根因

2.1==比引用,equals比内容

这是总纲,记住这一句能解决一大半问题:

  • 基本类型intlongdouble…),==比的是
  • 引用类型(所有对象,包括Integer),==比的是引用地址——也就是"是不是同一个对象",而不是值相不相等。

Integer是对象,所以c == d问的其实是"cd是不是指向同一个对象",而不是"它们的值相不相等"。new Integer(127) == new Integer(127)false就是这个道理:new了两次,是两个不同的对象,地址自然不同,哪怕值都是 127。

equalsInteger重写成了比较内部的int,所以只要值相等就返回true

那问题来了:cd又没new,只是Integer c = 128;,为什么也是两个对象?这就要说到自动装箱。

2.2 自动装箱:Integer a = 127背后发生了什么

Integer a = 127;这行,等号右边是int,左边是Integer,编译器会自动帮你"装箱",实际编译成:

Integera=Integer.valueOf(127);

注意,是Integer.valueOf(127),不是new Integer(127)这个区别是全部谜题的钥匙。

2.3IntegerCache-128~127返回同一个缓存对象

Integer.valueOf的源码(JDK 8):

publicstaticIntegervalueOf(inti){if(i>=IntegerCache.low&&i<=IntegerCache.high)returnIntegerCache.cache[i+(-IntegerCache.low)];// 命中缓存,返回同一个对象returnnewInteger(i);// 超出范围,new 新对象}

Integer内部维护了一个静态缓存IntegerCache,默认缓存-128127这 256 个整数对应的Integer对象。逻辑很直白:

  • 值在-128~127之间:直接返回缓存里同一个对象;
  • 超出这个范围:new一个对象。

到这里,127/128 的反转就彻底解释清楚了:

  • Integer a = 127, b = 127;→ 两次valueOf(127)都命中缓存,返回的是同一个对象a == b自然true
  • Integer c = 128, d = 128;→ 128 超出缓存范围,两次各new一个新对象c == d就是false

new Integer(127)是显式new绕过了缓存,所以哪怕在 127 也照样是两个对象,==false

一张图看清valueOf的分支和缓存范围:


三、正解

结论很简单:包装类型比较值,一律用equals,别用==

Integerc=128,d=128;// 有坑:比的是对象地址,结果随数值大小变化System.out.println(c==d);// false// 正解:比的是值,永远可靠System.out.println(c.equals(d));// true

几个补充:

  • 想用==也行,但要先拆箱成基本类型c.intValue() == d.intValue(),或者让其中一边是int(见下面第四节)。
  • 注意equals的空指针:如果变量可能为nullc.equals(d)会 NPE。用java.util.Objects.equals(c, d)更安全,它内部做了 null 判断。
  • 同类陷阱LongShortByteCharacter都有类似缓存,DoubleFloat没有缓存(浮点值域连续,缓存无意义)。另外String有字符串常量池,==也有类似的坑——都是"引用比较"惹的祸。

四、常见误区与面试高频问答

Q:为什么缓存范围偏偏是-128~127?能改吗?

这是一个字节(byte)能表示的范围,也是实践中小整数最常用的区间,缓存它们收益最高。上界可以通过 JVM 参数-XX:AutoBoxCacheMax=<size>调大(下界固定-128)。调大后,原本128 == 128false的结果可能变成true——这也从侧面证明了它就是缓存在起作用。

Q:基本类型int之间用==有问题吗?

没有。基本类型的==比的就是值,128 == 128(两个int)永远是true。坑只出现在包装类型上。

Q:Integerint==比会怎样?

Integerc=128;intx=128;System.out.println(c==x);// true

==一边是基本类型、一边是包装类型时,包装类型会自动拆箱成基本类型,于是比的是,结果反而"正常"了。也就是说Integer == int是安全的,Integer == Integer才有坑。

Q:既然equals更可靠,==是不是就没用了?

不是。判断"是不是同一个对象"(比如判断单例、判断引用是否被重新赋值)就得用====equals是两个不同的问题:==问"是不是同一个",equals问"值是不是相等"。选哪个取决于你要问什么。

Q:为什么阿里手册强制包装类之间比较用equals

正是因为本文这个坑。用==比较包装类,结果会随数值是否落在缓存范围而变化——127对、128错,这种"时对时错"的 bug 极难排查,还很容易在测试时用小数据蒙混过关、上线用大数据翻车。统一用equals就彻底避开了。


总结

128 == 128false,不是 JVM 有 bug,而是两个机制叠加的必然结果:

  • ==引用类型比的是对象地址(是不是同一个对象),不是值;equals才比值。
  • Integer a = 127会自动装箱成Integer.valueOf(127),而valueOf-128~127返回缓存的同一个对象,超出范围则new新对象。
  • 所以127命中缓存是同一个对象(==true),128超范围是两个对象(==false);new Integer绕过缓存,永远是新对象。

一句话记忆:==比地址、equals比值;Integer只缓存-128~127,超了就是两个对象——包装类型比较,永远用equals

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/27 17:03:35

汽车电子PMIC实战:TPS6591x-Q1电源管理与DRA71x/DRA79x系统设计

1. 项目概述与PMIC核心价值在嵌入式系统&#xff0c;尤其是汽车电子和工业控制领域&#xff0c;电源设计从来都不是简单的“供电”问题。它关乎着整个系统的稳定性、可靠性和长期运行的寿命。一块高性能的处理器&#xff0c;比如TI的DRA71x或DRA79x系列&#xff0c;其内部集成了…

作者头像 李华
网站建设 2026/7/27 17:03:16

谁才是真正的王者?采购供应链系统品牌盘点

采购数据承载着企业物料配额、成本结构与供应商底价等核心商业机密&#xff0c;是企业经营的核心保密资产。随着数据安全合规要求持续收紧、企业内部异构系统对接需求日趋复杂&#xff0c;公有云SaaS模式已难以完全覆盖中大型企业的采购数字化诉求&#xff0c;支持私有化部署的…

作者头像 李华
网站建设 2026/7/27 17:00:32

从模板到创作:StoryPad 7大日记模板助你开启写作之旅

从模板到创作&#xff1a;StoryPad 7大日记模板助你开启写作之旅 【免费下载链接】spooky-mb An open source diary & journal app with over 100k downloads &#x1fab4; 项目地址: https://gitcode.com/gh_mirrors/sp/spooky-mb StoryPad 是一款拥有超过 10 万次…

作者头像 李华
网站建设 2026/7/27 17:00:12

Gorpc完全指南:高性能Go RPC库如何解决高负载服务的3大痛点

Gorpc完全指南&#xff1a;高性能Go RPC库如何解决高负载服务的3大痛点 【免费下载链接】gorpc Simple, fast and scalable golang rpc library for high load 项目地址: https://gitcode.com/gh_mirrors/go/gorpc Gorpc是一款为高负载项目设计的简单、快速且可扩展的Go…

作者头像 李华
网站建设 2026/7/27 16:59:11

TMS320DM6467引脚复用配置详解:CRGEN、UART与GPIO实战指南

1. 项目概述与核心价值 在嵌入式硬件开发&#xff0c;尤其是基于复杂SoC&#xff08;片上系统&#xff09;的设计中&#xff0c;引脚复用&#xff08;Pin Multiplexing&#xff09;是每个工程师都必须熟练掌握的核心技能。它直接决定了你的硬件设计能否成功点亮&#xff0c;以及…

作者头像 李华