程序员Feri一名12年+的程序员,做过开发带过团队创过业,擅长Java、嵌入式、鸿蒙、人工智能等,专注于程序员成长那点儿事,希望在成长的路上有你相伴!君志所向,一往无前!
问题
今天下班刚想溜,移动端的兄弟就火急火燎地跑过来:"Feri哥,快看看这个接口,返回数据的childs字段总是空的!"
我接过屏幕一看,好家伙,这数据结构分明是棵树,正常情况下childs里应该挂满叶子节点才对,可现在却光秃秃的,像被秋风扫过的树枝。
我心里一沉:"不可能啊,这个接口上线半年了,从来没出过问题。" 可事实就摆在眼前,接口确实返回了空数据。
排查过程
第一回合:代码审查
我迅速打开IDE,定位到数据组装的代码块:
if(type.getParentId()>0){
//子类
for(TypeItem i:items){
System.err.println(i.getId()+"<---->"+type.getParentId()+"=="+(i.getId() == type.getParentId()));
if(i.getId() == type.getParentId()){
i.getChilds().add(item);
}
}
}else {
items.add(item);
}
这段代码逻辑很清晰:如果当前节点有父节点,就把它添加到父节点的childs列表中;否则作为根节点。
仔细一看,问题出在这行:
if(i.getId() == type.getParentId())
第二回合:Integer的秘密
我一拍大腿:"原来是Integer比较的坑!" 这里的i.getId()
和type.getParentId()
返回的都是Integer对象,而==
比较的是对象引用,不是值!
为了验证我的猜想,我加了些调试日志:
System.err.println("i.getId()类型: " + i.getId().getClass());
System.err.println("type.getParentId()类型: " + type.getParentId().getClass());
System.err.println("i.getId()内存地址: " + System.identityHashCode(i.getId()));
System.err.println("type.getParentId()内存地址: " + System.identityHashCode(type.getParentId()));
运行后发现:
-
当ID在-128~127之间时,两个对象的内存地址相同
-
当ID大于127时,内存地址不同
这就解释了为什么之前测试一直正常——我们的测试数据ID都比较小,刚好落在Integer缓存范围内!
解决方案
把==
换成equals()
方法:
if(type.getParentId()>0){
//子类
for(TypeItem i:items){
System.err.println(i.getId()+"<---->"+type.getParentId()+"=="+(i.getId().equals(type.getParentId())));
if(i.getId().equals(type.getParentId())){
i.getChilds().add(item);
}
}
}else {
items.add(item);
}
改完后重启服务,奇迹出现了:
childs字段终于有数据了,就像春天来了,光秃秃的树枝上又长出了新芽!
为什么会这样?
这要从Java的Integer缓存机制说起。Java为了优化性能,会缓存-128到127之间的Integer对象,就像一个"对象池"。当你创建这个范围内的Integer对象时,实际上是从缓存中拿的同一个对象。
举个栗子:
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true,因为指向同一个缓存对象
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false,因为超出缓存范围,创建了新对象
而equals()
方法会比较两个对象的值,不管它们是不是同一个对象:
System.out.println(c.equals(d)); // true,值都是200
总结
这个坑告诉我们:
-
**Integer比较要用equals()**:比较值相等时,永远用equals(),除非你真的想比较对象引用
-
缓存机制要牢记:Java会缓存-128~127的Integer对象,超出范围就会创建新对象
-
测试数据要全面:测试时不仅要覆盖正常情况,还要考虑边界条件
最后送大家一句话:
程序就像江湖,每个程序员都是侠客。即使你是老江湖,也难免会踩坑。重要的是踩坑后能总结经验,成为更厉害的侠客!
下次遇到Integer比较,你还会掉坑里吗?评论区留言说说你遇到过的奇葩Bug吧!