博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
由装箱引发的——Integer比较的来龙去脉
阅读量:5927 次
发布时间:2019-06-19

本文共 3470 字,大约阅读时间需要 11 分钟。

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

你可能感兴趣的文章
让Java和JavaScript进行交互
查看>>
linux逻辑卷管理
查看>>
LINQ之路12:LINQ Operators之数据转换(Projecting)
查看>>
SQL Server:数据库角色
查看>>
分享8个超棒的基于HTML5和jQuery的开发教程
查看>>
JFreeChart开发_用JFreeChart增强JSP报表的用户体验
查看>>
SpringMVC+Swagger详细整合
查看>>
计算机视觉领域最全汇总(第2部分)
查看>>
[译] 所有你需要知道的关于完全理解 Node.js 事件循环及其度量
查看>>
(六十九)复合语句
查看>>
我的友情链接
查看>>
设计模式:装饰者
查看>>
Java Web中实现Servlet的方式
查看>>
第三方库之 - SVProgressHUD
查看>>
11个让你吃惊的 Linux 终端命令
查看>>
Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut
查看>>
MySQL与MongoDB的操作对比
查看>>
# 180111php编译错误
查看>>
EIGRP 查看邻居命令详解
查看>>
js闭包
查看>>