问题的背景是这样的,判断数据库表中的一列是否有值以及该值是否是0,该列是数值型的,JAVA类中的类型是Long,注意这里使用的包装类。实际业务中的代码实现如下:
if(list!=null&&list.size()>0){ for(PoObjectobj:list){ if(obj.getFlowId()!=null&&!obj.getFlowId().equals(0)){ obj.setUrl(url.replace("aaa",String.valueOf(obj.getFlowId()))); } } }
从数据库表中查询出数据列表,然后遍历,判断是否flowId不为空且不为0的时候,对url属性做一个replace。url属性是显示到前端做跳转使用的。数据库表里flowId对应的列值都是0,启动项目的时候,发现页面上url地址都有值,这样就都做了跳转。本意是为0了,前端的url属性就是空的,提示暂无链接。那么问题出现在哪里了呢,就是!obj.getFlowId().equals(0)这个判断,这个判断对于flowId为0的时候返回是正确的,这就奇怪了,为啥0!=0是对的呢?其实是错用了equals方法。正确的写法应该是
!obj.getFlowId().equals(0L)。JDK中对于Long的equals的实现如下:
/*** Compares this object to the specified object. The result is* {@code true} if and only if the argument is not* {@code null} and is a {@code Long} object that* contains the same {@code long} value as this object.** @param obj the object to compare with.* @return {@code true} if the objects are the same;* {@code false} otherwise.*/publicbooleanequals(Objectobj) { if (objinstanceofLong) { returnvalue== ((Long)obj).longValue(); } returnfalse; }
0不是属于Long的,而0L是属于Long的,同样要注意的是Float的类型,0f和0的类型在Float的equals中也不是一样的概念。除了!obj.getFlowId().equals(0L)可以以外,还可以使用obj.getFlowId().compareTo(0L)==0去判断。这里面有个要注意的点就是Long的compareTo方法参数是Long类型的,所以如果你写compareTo(0)会报编译错误,强制你使用0L,而equeals方法的参数是Object类型的,所以不会报编译错误。由这点可以看出来编译器暴露出问题来的重要性了。可以用如下的测试用例测试一下几个包装类的比较:
publicstaticvoidmain(String [] args) { Longid=0L; Floata=0f; longid2=0; System.out.println(id2==0); //trueSystem.out.println(a.equals(0)); //falseSystem.out.println(a.equals(0f)); //trueSystem.out.println(id.equals(0)); //falseSystem.out.println(id.equals(0L)); //trueSystem.out.println(id.compareTo(0L)); //0 }
虽然是个小问题,但是刚入行的同学找了半天没找出来,还感觉很奇怪。也不是什么大问题,更不要怀疑JDK会出bugs或者会给你模棱两可的答案,从自己提供的参数去考虑,以及使用的方法执行的逻辑输出的结果是否是预期的结果这方面的入手。看看源码确实是个不错的成长途径。可能以后会遇到Integer的比较的时候会存在小于还是大于127的缓存的问题,希望能避坑。