当前位置: 首页 > news >正文

由装箱引发的——Integer比较的来龙去脉

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

前置知识:两个对象==比较的是栈的值,"==" 在java中比较对象时永远是比较对象的地址,这一点绝不会错。众所周之,java是保留了int,char等基本数据类型的,也就是说int类型的并不是对象,然而有些方法却需要object 类型的变量,所以java使用了装箱机制,我们可以自豪的这样声明一个整型变量:Integer a = new Integer(10); 那么整型的a也就是对象了,那这句是什么意思呢:Integer a= 10; java中可以这样声明一个对象吗?当然不是,从jdk1.5后,java实现了自动装箱,也就是自动将Integer a =10 中的int类型的10转化为了 Integer类型。

好,有了前面的只是我们且先看一个题目:
Integer a = 127;
Integer b = 127;

Integer c = 128;
Integer d = 128;

System.out.println(a==b);
System.out.println(c==d);

答案是什么呢? 如果您回答true,false,那么很遗憾的告诉你,你答对了!!!

分析一下,Integer a =127,Integer a=128。127,128应该不会造成什么差异吧,难道是自动装箱的过程有猫腻?找下源码看看:
private static class IntegerCache {
    private IntegerCache(){
    }
    static final Integer cache[] = new Integer[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Integer(i - 128);
        }
    }
    public static Integer valueOf(int i) {
        final int offset = 128;
        if (i >= -128 && i <= 127) { // must cache
            return IntegerCache.cache[i + offset];
        }
        return new Integer(i);
    }
}

源码解释:当我们装箱时,jvm实际上是自动调用的valueOf()这个方法,也就是Integer a= 10相当于Integer.valueOf(10)。好,我们看下Integer a = 127 的执行过程,首先调用Integer.valueOf(127) ,由于127在-128到127之间(看上面的源码),所以返回一个缓存值 return IntegerCache.cache[127+128];也就是数组中下标是255的值,那这个到底是什么值呢? 我们继续看:


static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Integer(i - 128);
    }

这是用一个for循环对数组cache赋值,cache[255] = new Integer(255-128),也就是new一个Integer(127) ,并把引用赋值给cache[255],好了,然后是Integer b= 127,流程基本一样,最后又到了cache[255] = new Integer(255-128),这一句,有点迷糊了,这不是又new了一个对象127吗,然后把引用赋值给cache[255],我们比较这两个引用(前面声明a的时候也有一个),由于是不同的地址,所以肯定不会相等,应该返回false啊!这么想你就错了,请注意看for语句给cache[i]初始化的时候外面有一个{},{}前面一个大大的static关键字大咧咧的杵在哪呢,对静态的,那么我们就可以回想下static有什么特性:
只能初始化一次,在对象间共享,也就是不同的对象共享同一个static数据,那么当我们Integer b = 127的时候,并没有new出一个新对象来,而是共享了a这个对象的引用,记住,他们共享了同一个引用!!!,那么我们进行比较a==b时,由于是同一个对象的引用(它们在堆中的地址相同),那当然返回 true了!!!

然后我们在看Integer c = 128;Integer d = 128;,当数据不再-128到127之间时,是不执行return IntegerCache.cache[i + offset];这句的,也就是不会返回一个static的引用,而是执行了return new Integer(i); 于是当 Integer d = 128 时,又会重新返回一个引用,两个不同的引用
在做c==d 的比较时当然返回false了!!

/*===========================================================*/

public static void main(String[] args) {
    Integer a = new Integer(1);
    Integer b = new Integer(1);
    int c=1;
    Integer e = 1;
    System.out.println("a==b:"+(a==b));
    System.out.println("a==c:"+(a==c));
    System.out.println("a==e:"+(a==e));
    System.out.println("c==e:"+(c==e));
}
结果:
a==b:false
a==c:true
a==e:false
c==e:true

Integer是int的封装对象,两个对象==比较的是栈的值。
Integer a = new Integer(1);
Integer b = new Integer(1);
a与b在栈中,存的是Integer对象在堆中的地址,而不是值。
a、b指向堆中的地址显然不同所以 a==b 为false.


int c = 1; int为值类型,引用类型Integer与值类型int比较显然比较的是值。
因为int在堆中是不开辟内存的,他在栈中的值则为他本身的值
所以a==c比较的是他们各自的value, a==c为true

Integer e=1; 这个比较特殊,直接赋值,它有独立的内存,每次赋值时将检查内存中是否有值跟他匹配的,若有则把此内存地址付给e,若没有,开辟新的内存。

你可以尝试下面的例子:
Integer t1 = 1;
Integer t2 = 1;
t1==t2 为true,如上所说,此时t1与t2指向的是同一块内存。

new 一定是开辟新的内存,直接赋值则不一定开辟新的内存。
因为a的引用指向堆,而e指向专门存放他的内存,所以他们的内存地址不一样。
所以a==e为false.

c==e等同于 a==c,一个引用类型一个值类型
integer与integer比较的是引用的地址,integer与int,integer先拆箱,比较的是值。

/*==========================================================*/

Integer 在自动装箱时调用了valueOf() 方法,其中IntegerCache 用来缓存Integer值的。默认缓存的Integer 值范围是  -128 ~ 127 。
 
我们来分析一下valueOf(int i)的执行过程:
如果 i 大于缓存中的 最小值(-127) 并且 小于 缓存中的最大值(127),直接返回IntegerCache 中缓存的Integer对象。否则就新建一个Integer对象并返回。

在-128~127之间时,装箱操作后,a 和 b 都是指向缓存中的同一个对象,结果返回true。其他情况下就不一样了,装箱操作时都是返回新的Integer对象,== 操作时地址必然不相等,所以返回false。
 
以后遇到数字比较时,要么先将值赋给对应的基本类型在比较,要么比较包装类型中的包装值(例如 a.intValue() == b.intValue()),要么直接调用equals方法。

转载于:https://my.oschina.net/u/129971/blog/131794

相关文章:

  • CIO管理札记
  • Centos6.0系统drbd+heartbeat+nfs实现高可用文件存储
  • NO.82 为需求分解任务
  • 插入容器STL学习笔记(八) 序列式容器 共性
  • 数据提交Ajax处理浏览器缓存的问题
  • ThinkPHP实例化模型的四种方法
  • error: Setup script exited with error: Unable to find vcvarsall.bat - 转
  • NSString与int和float的相互转换
  • php搜索
  • 时隔6年,再次鼓捣wordpress
  • Win7系统关闭开机动画操作步骤
  • 初见Ubuntu12.04 LTS
  • 加中实训第一天(笔记+代码) - 2013.5.28
  • oracle Streams基础之传播进程 (5)
  • 推荐最新的搜狗输入法--可以设置在中文状态下输入英文标点
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • ES2017异步函数现已正式可用
  • java第三方包学习之lombok
  • laravel5.5 视图共享数据
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • SpringBoot 实战 (三) | 配置文件详解
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 前端面试题总结
  • 微信小程序设置上一页数据
  • 新手搭建网站的主要流程
  • 正则学习笔记
  • ​HTTP与HTTPS:网络通信的安全卫士
  • ​渐进式Web应用PWA的未来
  • ​如何在iOS手机上查看应用日志
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (9)目标检测_SSD的原理
  • (C语言)二分查找 超详细
  • (day6) 319. 灯泡开关
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (九)信息融合方式简介
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (四) 虚拟摄像头vivi体验
  • (转)母版页和相对路径
  • (转)视频码率,帧率和分辨率的联系与区别
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET Core中Emit的使用
  • .net反编译工具
  • [BZOJ] 2006: [NOI2010]超级钢琴
  • [C#]扩展方法
  • [C++] new和delete
  • [C++]运行时,如何确保一个对象是只读的
  • [halcon案例2] 足球场的提取和射影变换
  • [HNOI2008]玩具装箱toy
  • [JavaWeb]——过滤器filter与拦截器Interceptor的使用、执行过程、区别
  • [JAVA设计模式]第二部分:创建模式
  • [jQuery]10 Things I Learned from the jQuery Source
  • [LeetCode] 197. 上升的温度
  • [LeetCode] Ransom Note 赎金条