128陷阱这个名字,Java 服务端开发基本都躲不过。它说的不是某个安全漏洞,也不是网络协议问题,而是 Integer 自动装箱之后用 == 比较时出现的“薛定谔式”结果:同样的代码,数字换成 100 是 true,换成 128 就变成 false。很多人第一次遇到时都会怀疑 JVM 是不是有问题,或者编译器是不是抽风了。今天我就把这件事从现象到源码、从踩坑到修复彻底说清楚,同时给你一套以后写代码不会再犯同类错误的方案。
1. 线上翻车现场:为什么 status 等于 128 时总走错分支
先说一个真实场景。早几年我做过一个交易对账系统,上游返回的订单状态是 Integer 类型,代码里有一行判断:
java复制if (order.getStatus() == 128) {
// 走“对账异常”分支
}
当时同事写这行代码的时候,状态值是 128 的情况一直没触发过,所以线上跑得很正常。直到某天大促期间真的来了一笔状态为 128 的订单,系统没有走进这个分支,反而走到了默认分支,导致告警没上报、补偿任务没触发,最后靠日志捞数据人工处理才平掉。
排查了很久,最后定位到这行 == 比较上。单看语法没什么问题,order.getStatus() 返回的是 Integer 对象,右边的 128 是 int 基本类型,理论上两者比较时会把对象拆箱成数值。但真正的坑还有一层:getStatus() 的值在序列化和反序列化之后是一个新的 Integer 对象,而左边的对象和右边经自动装箱产生的对象不是同一个引用。
1.1 一段让所有人沉默的最小复现代码
我后来把问题抽成了一段最简短的代码:
java复制Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false
Integer c = 127;
Integer d = 127;
System.out.println(c == d); // true
如果你没了解过 Integer 缓存,看到这个输出会觉得离谱:127 引用相等,128 却不相等。更离谱的是,System.out.println(a == b) 的结果并不是因为 128 > 127 这个数学大小,而是因为自动装箱流程在背后偷偷做了不同的事。
把数值往两边扩一下,你会发现规律更明显:
java复制Integer x = -128;
Integer y = -128;
System.out.println(x == y); // true
Integer m = -129;
Integer n = -129;
System.out.println(m == n); // false
负数和正数同样存在边界,两边都有一条清晰的分界线:-128 到 127 这一段是“安全区”,在这个范围内的同值装箱返回同一个实例;超出这个范围后,每次装箱都会 new 一个新对象。上线是 128,所以大家习惯把这种现象叫做 128 陷阱。它真正的本质是对象引用比较和数值比较混用后造成的幻觉。
1.2 同样的数值,换个写法结果又不一样
这个陷阱最迷惑人的地方在于:它不是“只要等于 128 就 false”。换成下面这些写法,结果会完全相反。
java复制Integer a = 128;
int b = 128;
System.out.println(a == b); // true
Integer c = new Integer(128);
Integer d = new Integer(128);
System.out.println(c == d); // false
第一条语句返回 true,是因为当 == 的一侧是基本类型 int 时,编译器会强制把另一侧的 Integer 拆箱成 int,然后比较 128 和 128 是否数值相等。这已经不是引用比较,而是基本类型数值比较。
第二条语句返回 false 更直白:new Integer(128) 明确指出每次都要新建一个对象,跟缓存无关。所以在排查“为什么同样写 == 有人对有人错”的时候,先分清楚参与比较的双方到底是对象还是基本类型,这比背结论重要得多。
| 代码写法 | 比较结果 | 关键原因 |
|---|---|---|
Integer a=127; Integer b=127; a==b |
true | 自动装箱读取缓存 |
Integer c=128; Integer d=128; c==d |
false | 自动装箱创建两个新对象 |
Integer e=128; int f=128; e==f |
true | 触发拆箱,做数值比较 |
new Integer(128) == new Integer(128) |
false | 强制创建两个独立对象 |
| `Integer g=null; g== |
