Integer自动装箱细节分析


装箱 就是将基本的数据类型(比如int)封装成对象类型(比如Integer)。拆箱 就是将对象类型拆包装为基本类型。

装箱和拆箱操作为我们提供了很多便利。

  • 包装类,如Integer给我们提供的友好的API方法,使我们可以快速完成数据类型之间的转换。
  • Java是面向对象的,我们在Java中处理的绝大多数都是对象,在类似Map集合类等很多场合中,我们需要放入Integer这种对象类型。

很多情况下,编译器为我们自动完成了基本数据类型的拆箱和装箱操作。

public class TestInteger{
    public static void main(String[] args){
        int a1 = 127;
    //auto-boxing
        Integer i = a1;
    //auto-unboxing
        int b1 = i;
    }
}

下面,我们来看一段代码:

public class TestInteger{
         public static void main(String[] args){
            int a1 = 12;
            //auto-boxing
            Integer number1 = a1;
            Integer number2 = a1;
            System.out.println("number1==number2:"+ (number1==number2));
            //out:number1==number2:true

            int b1=128;
            Integer number3 = b1;
            Integer number4 = b1;
            System.out.println("number3==number4:"+ (number3==number4));
            //out:number3==number4:false
        }
}

通过编译,我们发现Integer类型在auto-boxing操作时,-128到127之间的int类型整数封装之后的对象是同一个,这个范围之外的int类型整数封装之后对象是不同的。通过后面的分析,我们可以知道:

-128到127这个范围,-128是固定的,上限127是我们可以通过Jvm参数配置的。

为什么会这样呢?我们通过这段程序的字节码分析这段程序编译的细节:

Microsoft Windows [版本 10.0.15063]
(c) 2017 Microsoft Corporation。保留所有权利。

D:\JavaProject\SuperMarket\bin\com\javaexc\service>javap -c Test.class
Compiled from "Test.java"
public class com.javaexc\service.Test {
  public com.javaexc\service.Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        12
       2: istore_1
       3: iload_1
       4: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       7: astore_2
       8: iload_1
       9: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      12: astore_3
      13: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      16: new           #28                 // class java/lang/StringBuilder
      19: dup
      20: ldc           #30                 // String number1==number2:
      22: invokespecial #32                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      25: aload_2
      26: aload_3
      27: if_acmpne     34
      30: iconst_1
      31: goto          35
      34: iconst_0
      35: invokevirtual #35                 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
      38: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      41: invokevirtual #43                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      44: sipush        128
      47: istore        4
      49: iload         4
      51: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      54: astore        5
      56: iload         4
      58: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      61: astore        6
      63: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      66: new           #28                 // class java/lang/StringBuilder
      69: dup
      70: ldc           #48                 // String number3==number4:
      72: invokespecial #32                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      75: aload         5
      77: aload         6
      79: if_acmpne     86
      82: iconst_1
      83: goto          87
      86: iconst_0
      87: invokevirtual #35                 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
      90: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      93: invokevirtual #43                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      96: return
}

通过观察4、9、51、58这四行字节码我们发现,编译器调用Integer.valueOf()帮助我们完成自动装箱操作。

我们进一步阅读Integer.class文件,分析Integer.valueOf()实际上完成的工作:

   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);
    }

当输入参数i满足i >= IntegerCache.low && i <= IntegerCache.high这个范围时,return的是IntegerCache.cache[]这个数组里的元素,否则return一个新的Integer对象。
那么IntegerCache.lowIntegerCache.high以及IntegerCache.cache[]分别是什么呢?我们继续看代码:

 /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    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) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low));
            }
            high = h;

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

        private IntegerCache() {}
    }

我们看到IntegerCache.lowIntegerCache.high以及IntegerCache.cache[]都是static final修饰的,IntegerCache.low固定是-128,IntegerCache.high可以通过设置属性来修改,默认是127。

结论1:
int类型整数自动装箱时,当值在上述范围内时,返回的是IntegerCache.cache[]这个数组里的同一个元素。

这也就是-128到127之间的int类型整数自动封装成同一个对象的原因。这里的IntegerCache就是用来处理Integer自动装箱操作的。

在自动装箱时,当值在 [128,127] 之间时,引用指向的都是内存中的同一个对象。如果超过这个范围,每次装箱都会new()一个新的对象。这样便加大了对简单数字的重复使用,在某些场景下可以优化我们代码的性能。

结论2:
值得说明的是,上述情况只有在我们使用自动装箱时,编译器自动调用Integer.valueOf()才会出现,如果我们采用new()来创建对象,每一次new()的对象都是一个新的对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值