【避坑指南】Java Long包装类比较引发的异常——用户ID>127删除订单失败的BUG 【避坑指南】Java Long包装类比较引发的异常——用户ID127删除订单失败的BUG一、问题描述在最近的一个项目开发过程中我遇到了一个非常诡异的BUG现象用户ID2的用户可以正常删除自己的订单 ✅用户ID129的用户删除自己的订单时却提示不能删除他人订单 ❌明明是自己的订单为什么会被拦截难道系统认为这是别人的订单二、问题代码先来看出问题的代码OverridepublicvoiddeleteOrder(Longid){// 1.获取登录用户LonguserIdUserContext.getUser();// 2.查询订单OrderordergetById(id);if(ordernull){return;}// 3.判断订单所属用户与当前登录用户是否一致if(userId!order.getUserId()){// ❌ BUG就在这里// 不一致说明不是当前用户的订单结束thrownewBadRequestException(不能删除他人订单);}// 4.删除订单booleansuccessremoveById(id);if(!success){thrownewDbException(OPERATE_FAILED);}}三、DEBUG排查过程通过IDEA的调试模式我查看了运行时对象的详细信息调试截图显示发现问题两个Long对象的数值都是129应该相等但它们的内存地址不同15448 vs 15452使用!比较时比较的是对象引用地址而不是数值四、底层原理剖析1. Java包装类的缓存机制在Java中Long、Integer等包装类有一个缓存池机制。以Long为例查看JDK源码HotSpotIntrinsicCandidatepublicstaticLongvalueOf(longl){finalintoffset128;if(l-128l127){// 缓存范围returnLongCache.cache[(int)loffset];}returnnewLong(l);// 超出范围new新对象}核心逻辑-128 到 127从缓存池中获取同一个数值永远是同一个对象超出范围每次都会new Long()生成新对象2.!和equals()的区别比较方式比较内容适用场景/!内存地址引用是否相同基本类型比较equals()数值内容值是否相等包装类对象比较3. 问题复现场景一用户ID2在缓存范围内LonguserId2L;// 从缓存获取地址1234LongorderUserId2L;// 从缓存获取地址1234同一个对象userId!orderUserId// false地址相同判断正确✅场景二用户ID129超出缓存范围LonguserId129L;// new Long(129)地址15448LongorderUserId129L;// new Long(129)地址15452不同对象userId!orderUserId// true地址不同判断错误❌// 系统误判为他人订单抛出异常五、正确解决方案方案一使用equals()推荐OverridepublicvoiddeleteOrder(Longid){// 1.获取登录用户LonguserIdUserContext.getUser();// 2.查询订单OrderordergetById(id);if(ordernull){return;}// 3.判断订单所属用户与当前登录用户是否一致if(!userId.equals(order.getUserId())){// ✅ 正确比较数值// 不一致说明不是当前用户的订单结束thrownewBadRequestException(不能删除他人订单);}// 4.删除订单booleansuccessremoveById(id);if(!success){thrownewDbException(OPERATE_FAILED);}}方案二比较基本类型if(userId.longValue()!order.getUserId().longValue()){// ✅ 也可以thrownewBadRequestException(不能删除他人订单);}方案三Objects.equals()更安全if(!Objects.equals(userId,order.getUserId())){// ✅ 最安全避免NPEthrownewBadRequestException(不能删除他人订单);}七、扩展知识其他包装类的缓存范围包装类缓存范围说明Byte-128 ~ 127全部值Byte就这么多Short-128 ~ 127常用值Integer-128 ~ 127常用值可通过JVM参数调整Long-128 ~ 127常用值Float/Double无缓存浮点数精度问题不建议缓存Character0 ~ 127ASCII字符Booleantrue/false只有两个值八、避坑指南 错误写法// 错误包装类使用 或 !if(userIdorder.getUserId()){}if(userId!order.getUserId()){}// 错误包装类与null比较时可能NPEif(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()永远不要用或!如果你觉得这篇文章对你有帮助欢迎点赞、收藏、转发有问题欢迎在评论区交流讨论~