【避坑指南】Java Long包装类比较引发的异常——用户ID>127删除订单失败的BUG
·
【避坑指南】Java Long包装类比较引发的异常——用户ID>127删除订单失败的BUG
一、问题描述
在最近的一个项目开发过程中,我遇到了一个非常诡异的BUG:
现象:
- 用户ID=2的用户,可以正常删除自己的订单 ✅
- 用户ID=129的用户,删除自己的订单时却提示"不能删除他人订单" ❌
明明是自己的订单,为什么会被拦截?难道系统认为这是别人的订单?
二、问题代码
先来看出问题的代码:
@Override
public void deleteOrder(Long id) {
// 1.获取登录用户
Long userId = UserContext.getUser();
// 2.查询订单
Order order = getById(id);
if (order == null) {
return;
}
// 3.判断订单所属用户与当前登录用户是否一致
if(userId != order.getUserId()){ // ❌ BUG就在这里!
// 不一致,说明不是当前用户的订单,结束
throw new BadRequestException("不能删除他人订单");
}
// 4.删除订单
boolean success = removeById(id);
if (!success) {
throw new DbException(OPERATE_FAILED);
}
}
三、DEBUG排查过程
通过IDEA的调试模式,我查看了运行时对象的详细信息:
调试截图显示:

发现问题:
- 两个Long对象的数值都是129,应该相等
- 但它们的内存地址不同(@15448 vs @15452)
- 使用
!=比较时,比较的是对象引用地址,而不是数值!
四、底层原理剖析
1. Java包装类的缓存机制
在Java中,Long、Integer等包装类有一个缓存池机制。以Long为例,查看JDK源码:
@HotSpotIntrinsicCandidate
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // 缓存范围
return LongCache.cache[(int)l + offset];
}
return new Long(l); // 超出范围,new新对象
}
核心逻辑:
- -128 到 127:从缓存池中获取,同一个数值永远是同一个对象
- 超出范围:每次都会
new Long(),生成新对象
2. != 和 equals() 的区别
| 比较方式 | 比较内容 | 适用场景 |
|---|---|---|
== / != |
内存地址(引用是否相同) | 基本类型比较 |
equals() |
数值内容(值是否相等) | 包装类对象比较 |
3. 问题复现
场景一:用户ID=2(在缓存范围内)
Long userId = 2L; // 从缓存获取,地址@1234
Long orderUserId = 2L; // 从缓存获取,地址@1234(同一个对象)
userId != orderUserId // false(地址相同,判断正确)✅
场景二:用户ID=129(超出缓存范围)
Long userId = 129L; // new Long(129),地址@15448
Long orderUserId = 129L; // new Long(129),地址@15452(不同对象)
userId != orderUserId // true(地址不同,判断错误)❌
// 系统误判为"他人订单",抛出异常
五、正确解决方案
方案一:使用 equals()(推荐)
@Override
public void deleteOrder(Long id) {
// 1.获取登录用户
Long userId = UserContext.getUser();
// 2.查询订单
Order order = getById(id);
if (order == null) {
return;
}
// 3.判断订单所属用户与当前登录用户是否一致
if(!userId.equals(order.getUserId())){ // ✅ 正确:比较数值
// 不一致,说明不是当前用户的订单,结束
throw new BadRequestException("不能删除他人订单");
}
// 4.删除订单
boolean success = removeById(id);
if (!success) {
throw new DbException(OPERATE_FAILED);
}
}
方案二:比较基本类型
if(userId.longValue() != order.getUserId().longValue()){ // ✅ 也可以
throw new BadRequestException("不能删除他人订单");
}
方案三:Objects.equals()(更安全)
if(!Objects.equals(userId, order.getUserId())){ // ✅ 最安全,避免NPE
throw new BadRequestException("不能删除他人订单");
}
七、扩展知识:其他包装类的缓存范围
| 包装类 | 缓存范围 | 说明 |
|---|---|---|
Byte |
-128 ~ 127 | 全部值(Byte就这么多) |
Short |
-128 ~ 127 | 常用值 |
Integer |
-128 ~ 127 | 常用值,可通过JVM参数调整 |
Long |
-128 ~ 127 | 常用值 |
Float/Double |
无缓存 | 浮点数精度问题,不建议缓存 |
Character |
0 ~ 127 | ASCII字符 |
Boolean |
true/false | 只有两个值 |
八、避坑指南
🚫 错误写法
// 错误:包装类使用 == 或 !=
if (userId == order.getUserId()) { }
if (userId != order.getUserId()) { }
// 错误:包装类与null比较时可能NPE
if (userId.intValue() > 100) { } // userId为null时报错
✅ 正确写法
// 推荐:使用equals()
if (userId.equals(order.getUserId())) { }
// 更安全:使用Objects.equals()
if (Objects.equals(userId, order.getUserId())) { }
// 比较基本类型
if (userId.longValue() > 100L) { }
💡 最佳实践
- 数据库ID、金额等Long类型字段比较,一律使用
equals() - 涉及null值比较时,使用
Objects.equals() - 如果确定不为null,可以使用
.longValue()或.intValue() - 开启IDEA警告检查,IDE会提示包装类比较问题
九、总结
这个BUG虽然简单,但非常隐蔽:
- 小数字用户正常,大数字用户异常,容易被误认为是权限或数据问题
- 调试时数值相同,但地址不同,需要深入理解Java包装类机制
- 修复成本极低,但排查成本很高
核心教训:
Java中所有包装类(Long、Integer等)的比较,必须使用
equals(),永远不要用==或!=!
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发!有问题欢迎在评论区交流讨论~
更多推荐


所有评论(0)