前言
中国有句老话叫“事不过三”,指一个人犯了同样的错误,一次两次三次还可以原谅,超过三次就不可原谅了。有人指出这个“三”是虚数,用来泛指多次,所以“事不过三”不包括“三”。至于“事不过三”包不包括“三”,可能跟每个人的底线有关系,属于哲学范畴,不在本文的讨论范围之内。
写代码也是如此,同一个代码“坑”,踩第一次叫“长了经验”,踩第二次叫“加深印象”,踩第三次叫“不长心眼”,踩三次以上就叫“无可救药”。在本文中,笔者总结了一些Java坑,描述了问题现象,进行了问题分析,给出了避坑方法。希望大家在日常工作中,遇到了这类Java坑,能够提前避让开来。
一、 对象比较方法
JDK1.7提供的Objects.equals方法,非常方便地实现了对象的比较,有效地避免了繁琐的空指针检查。
1. 问题现象
在JDK1.7之前,在判断一个短整型、整型、长整型包装数据类型与常量是否相等时,我们一般这样写:
从JDK1.7之后,提供了Objects.equals方法,并推荐使用函数式编程,更改代码如下:
为什么直接把==替换为Objects.equals方法就会导致输出结果不一样?
2. 问题分析
通过反编译第一段代码,我们得到语句“System.out.println(shortValue == 12345);”的字节码指令如下:
原来,编译器会判断包装数据类型对应的基本数据类型,并采用这个基本数据类型的指令进行比较(比如上面字节码指令中的sipush和if_icmpne等),相当于编译器自动对常量进行了数据类型的强制转化。
为什么采用Objects.equals方法后,编译器不自动对常量进行数据类型的强制转化?通过反编译第二段代码,我们得到语句“System.out.println(Objects.equals(shortValue,12345));”的字节码指令如下:
原来,编译器根据字面意思,认为常量12345默认基本数据类型是int,所以会自动转化为包装数据类型Integer。
在Java语言中,整数的默认数据类型是int,小数的默认数据类型是double。
通过分析Objects.equals方法的源代码可知:语句“System.out.println(Objects.equals(shortValue,12345))”,因为Objects.equals的两个参数对象类型不一致,一个是包装数据类型Short,另一个是包装数据类型Integer,所以最终的比较结果必然是false;而语句“System.out.println(Objects.equals(intValue,12345))”,因为Objects.equals的两个参数对象类型一致,都是包装数据类型Integer且取值相同,所以最终的比较结果必然是true。
3. 避坑方法
1) 保持良好的编码习惯,避免数据类型的自动转化
为了避免数据类型自动转化,更科学的写法是直接声明常量为对应的基本数据类型。
第一段代码可以这样写:
第二段代码可以这样写:
2) 借助开发工具或插件,及早地发现数据类型不匹配问题
在Eclipse的问题窗口中,我们会看到这样的提示:
3) 进行常规性单元测试,尽量把问题发现在研发阶段
“勿以善小而不为”,不要因为改动很小就不需要进行单元测试了,往往Bug都出现在自己过度自信的代码中。像这种问题,只要进行一次单元测试,是完全可以发现问题的。
注意:
进行必要单元测试,适用于以下所有案例,所以下文不再累述。
接下篇:https://developer.aliyun.com/article/1228289?spm=a2c6h.13148508.setting.20.7be64f0ebemzoR