128陷阱深度详解——Integer自动拆箱的艺术

本文详细介绍了Java中的Integer自动拆箱现象,特别是所谓的"128陷阱"。当Integer对象的值在[-128, 127]范围内时,由于Integer缓存机制,比较操作会返回true,否则返回false。文章深入分析了Integer的valueOf方法、IntegerCache的内部工作原理,包括下界-128、上界的动态配置以及缓存的创建过程,帮助读者理解这一特性。
摘要由CSDN通过智能技术生成

128陷阱深度详解——Integer自动拆箱的艺术

1.概述

      
127陷阱指 Integer类封装的数字在[-128,127]范围内比较可以相等,超过此范围不能相等的现象。示例代码如下

Integer a= 128;
Integer b= 128;
System.out.println(a==b);

Integer e= 100;
Integer f= 100;
System.out.println(e==f);

Integer c= -129;
Integer d= -129;
System.out.println(c==d);

    打印效果

false
true
false

2.原理

      要了解其中原理,首先理解几个概念。

2.1 对象值相等,对象地址相等

      对于两个一般的引用对象,例如

对象A :Student{age=15,name="小明"}  
对象B :Student{age=15,name="小明"}

    地址相等意味着两个引用对象指向堆中同一块地址,修改A的属性,B也会相应变化。
    值相等意味着两个对象内容是一致的。A的各个属性都与B的属性相等。示例中A和B的age都是15,name都是小明,无论AB是否地址相等,我们可以直观看到AB的各个属性一致。。
    此外,从程序运行角度来看,如果打印A==B结果为true,则认为两个对象的地址是相等的。如果打印A.equals(B)结果为true,则认为两个对象的值是相等的。

2.2 自动拆箱

     和上例中的Student类不同,Integer类包含一个静态方法valueOf(int i),当两个Integer类的实例

Integer a= 128;
Integer b= 128;

     用两个等号==相比较时,比较的是valueOf的返回值,这个过程称为自动拆箱。 进入Integer类源码,找到valueOf()方法如下:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

     不难发现,IntegerCache.low 和IntegerCache.high是“128陷阱”的关键。当int i超过某个范围时,返回一个新的对象,即

return new Integer(i);

     否则,如果i在约束的范围内(范围很可能时我们上面提到的[-128,127]),返回某个固定地址的值。即

return IntegerCache.cache[i + (-IntegerCache.low)];

3."128陷阱"实现机制

      由此,我们可以推出,两个超过[-128,127]范围的Integer类对象比较时,比较了两个new Integer(i)的地址,他们是不可能相等的。如果不超过范围[-128, 127],则是比较两个“常量”对象的地址,即IntegerCache.cache[n],如果值相等,地址必然相等,A==B的返回值也就一定是true了。

      下面我们进入Integer源码,观察IntegerCache.low 和IntegerCache.high是如何工作的。

4.IntegerCache工作机制

4.1 源码概述

      IntegerCache类的完整源码如下,之后逐一分析:
      这段源码表示,IntegerCache是Integer的静态内部类。该类加载时,需要依次执行“static”修饰的语句或块,称为静态初始化语句、静态初始化块。

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
4.2 定义下界-128

      容易发现,IntegerCache.low是-128,即128陷阱[-128,127]的下界。

static final int low = -128;

      这行语句由static修饰,叫做静态初始化语句。final修饰表示low这个变量赋值为-128后,不能再被其他语句改变了。

4.3 定义上界127
static final int high;
static final Integer cache[];

      之后两行语句意味着high和cache被声明,并且之后仅可以赋值一次。

      执行完毕三行初始化语句,开始执行初始化块,如下。

String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

      读取某个配置文件中的内容。
      如果读取到内容,则把该值和127比较取较大值,作为“128陷阱”的上界,即上文提到的IntegerCache.high

 if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
4.4 定义缓存

      定义好上界(根据配置文件,可能大于127)和下界(指定为-128)后,新建缓存区,即cache。缓冲区的大小由上界IntegerCache.high、下届IntegerCache.low共同决定。

cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

      至此,IntegerCache的成员变量全部定义完毕。由于不存在任何非静态变量,不用创建IntegerCache类的对象即可引用到所需内容(上界、下界、缓存区)。

5.总结

      综上,我们可以看到,128陷阱的实现主要利用自动拆箱。Integer的自动拆箱,使用了上文2.2提到的valueOf方法,Integer类的实例A、实例B使用双等号比较,比较的实际上是经过Integer类处理过的地址,而这个处理过程主要依托于IntegerCache类。IntegerCache类初始化128陷阱的上界、下界和缓存,最后才能达到“128陷阱”的效果。
      建议读者翻到博客开头,通过第一节示例代码思考、回忆Integer自动拆箱流程。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值