一场由Java中Integer引发的踩坑实战
作者:我是老姚 发布时间:2021-09-06 11:14:40
看过阿里巴巴开发手册的同学应该都会对Integer临界值127有点印象。
原文中写的是:
【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,
会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都
会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
没错,文中要求Integer对象比较的时候必须要用equals进行。
然后很多同学对于为什么要这么干没什么感觉。对于IntegerCache.cache也没什么感觉。
很多时候我们写的代码可能是这样的:
Integer num = 127;
if(num==128){
//...
}
这种情况下,我们拿Integer和一个数字比较的时候,是不会出什么问题。
然后如果我们这样写
Integer num1 = 127;
num1++;
Integer num2= 128;
if (num1 == num2) {
System.out.println(true);
}
else{
System.out.println(false);
}
神奇的事情发生了,num1和num2虽然都是128,但是他们并不相等。
这是因为Integer的值如果是-128~127之间的时候,Integer并不会创建新的对象,而是从IntegerCache.cache中取出的,所以他们随便比较都没有问题。
但是如果超出了这个范围,就不一样了。
不信你可以试试下面的代码:
Integer num1 = 127;
Integer num2= 127;
if (num1 == num2) {
System.out.println(true);
}
else{
System.out.println(false);
}
=================================
可惜的是工作中常常忘记了这一点,于是一场意外发生了。
本人前段时间写的一段代码中有下面一段:
String standardItemNameStr = sampleStandardItemList.stream()
.filter(item -> item.getSampleId()==sample.getSampleId())
.map(item -> item.getStandardItemName()).collect(Collectors.joining("、"));
系统刚刚上线的时候一切NICE,运行很正常。突然有一天出事了。客户跟反馈出BUG了。
本该显示数据的地方,成了空值。
自己在本地测试,一切OK。代码检测了十遍以上,没发现什么问题。
把生产环境的数据DOWN下来一跑发现其中第二行item.getSampleId()
的值是180,这时突然想起Integer的这个设定。二话不说,修改为下面的代码,一切恢复正常。
String standardItemNameStr = sampleStandardItemList.stream()
.filter(item -> item.getSampleId() .equals(sample.getSampleId()))
.map(item -> item.getStandardItemName()).collect(Collectors.joining("、"));
这个问题虽然很简单,但还是很容易忽略的。由此也扩展思考了一下,去测试一下Double、Float包装类,并没有catch这类的设计思路。
原因嘛应该也很简单,Integer是整数,很多时候我们用Integer的时候需要用到的值确实是比较小的,所以官方做个catch确实能起到提高执行效率的作用,而且这个缓存命中率还是比较高的,但是小数的主要用途是在小数方面,如果要做catch的话,那数量可就太多了。
那么byte、short、long的包装类会不会也有catch的设计呢?
补充:Java Integer比较中的那些坑
前几天同事偶然遇到的一个问题,在list中查询出重复的值,留下第一个,其余删除。
ArrayList<Integer> a//a中装有要操作的数据,都是数字
for(int i;i<a.size();i++){
//....遍历
for(int j=i;j<a.size();j++){
if(a.get(i)==a.get(j)){
a.remove(j);
}
}
}
然后喜闻乐见的程序出问题了:程序无法将相同的值除第一个外删除掉。说起来这也是一个比较基础性的问题。
int为基本类型,Integer类型为基本包装类型。因而可以将Integer当做一个对象来理解,所以在上面的代码示例中,用==来比较2个对象的引用无疑就是在搞笑了,地址都不一样,怎么可能返回true。
但是这里存在着一些坑,就是Integer有时候用==比较是可以得到true的(值相同),原因如下:
在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象。
所以推荐都使用equals比较。
附上int类型自动装箱为Integer时的源代码(IntegerCache.low为-128)
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这里还牵涉到了一点,就是这里为什么不直接用int类型呢?这是因为ArrayList中只接受Object对象,实际情况如下:
ArrayList al=new ArrayList();
int n=40;
Integer nI=new Integer(n);
al.add(n);//不可以
al.add(nI);//可以
来源:https://blog.csdn.net/aley/article/details/127759724


猜你喜欢
- 本文实例讲述了java继承中的构造方法。分享给大家供大家参考。具体如下:继承中的构造方法: 1、子类的构造过程中必须调用其基类的构造方法。2
- 开始学习WebSocket,准备用它来实现一个在页面实时输出log4j的日志以及控制台的日志。首先知道一些基础信息:1.java7 开始支持
- 目录一、字节码文件 与 JVM二、字节码文件示例三、字节码文件二进制结构分析1、魔数2、次版本号3、主版本号4、常量池个数总结一、字节码文件
- 一、方法(Method)概念 1、Java 中的方法就是其他编程语言中的函数(Function) 2、方法的定义格式:①
- 本文实例为大家分享了C#实现文件上传与下载的具体代码,供大家参考,具体内容如下C#实现文件上传代码: public ActionResult
- 最近单位又有一个新Java项目。涉及到扫码登录。之前项目使用的是 ajax轮询的方式。感觉太low了。所以这次用webSocket的方式进行
- 需求说明在对图像进行处理时,经常会有这类需求:想通过阈值对图像进行二值化分割,以提取自己感兴趣的区域,常见的阈值分割方法有常数分割、最大类间
- 本文实例为大家分享了java顺时针打印矩阵的具体代码,供大家参考,具体内容如下github:剑指offer编程题 import j
- 一、编码(转成字节数组)GetBytes 1、ASII码:字符串中的每个字符用一个字节表示。每个字符实际上只使用了7位,从00h
- 前言今天介绍的是关于Spring的数据库连接以及Jdbc模板的相关API方法,虽然在学习了hibernate之后,会知道实现数据库连接一般都
- 1:引入依赖<dependency> <
- 1. 是否需要整合 ?不需要 : 单独使用Springmvc. 需要将原先Spring中的内容通通迁移到Springmvc中. 例如:数据源
- 简介通过 pulsar-flink-connector 读取到 Apache pulsar 中的namespaces、topics的元数据信
- 在Windows平台上,播放PCM声音使用的API通常有如下两种。waveOut and waveIn:传统的音频MMEAPI,也是使用的最
- 实际上,HashSet 和 HashMap 之间有很多相似之处,对于 HashSet 而言,系统采用 Hash 算法决定集合元素的存储位置,
- SimpleDateFormat是处理日期格式转换的类。官方API_1.8关于SimpleDateFormat继承于DateFormate截
- 本文项目为大家分享了C#实现点餐系统,供大家参考,具体内容如下项目介绍:一家店铺使用的外卖点餐系统本项目分三大模块:登录注册模块,用户模块,
- 一、本文来由由于网络不稳定的原因,所以想着启动本地nacos来运行项目,但是nacos启动没问题了,但是依旧报错。Caused by: co
- 提示:这里可以添加本文要记录的大概内容:例如:一般系统前端调用后台相关功能接口时,需要验证此时用户的权限是否满足调用该接口的条件,因此我们需
- 首先在layout布局中设置按钮和一个ImageView<Button android:id="@+id/sel