Java自动装箱与拆箱

来源:互联网 发布:神优化单机游戏低配 编辑:程序博客网 时间:2024/06/06 01:31

自动装箱与拆箱机制在实际使用中非常常见,不过也特别容易出错,博主在面对下面一道题的时候自信满满,可还是没有能够全对,所以写下这篇博文,给自己对自动装箱与拆箱机制做一下知识巩固,也给各位朋友做一下参考。 
  首先有这样一道题,给出下面代码的输出结果:

public class AutoBoxing{    public static void main(String[] args)    {        Integer a = 1;        Integer b = 2;        Integer c = 3;        Integer d = 3;        Integer e = 321;        Integer f = 321;        Long g = 3L;        System.out.println(c==d);        System.out.println(e==f);        System.out.println(c==(a+b));        System.out.println(c.equals(a+b));        System.out.println(g==(a+b));        System.out.println(g.equals(a+b));    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

  运行结果:

truefalsetruetruetruefalse
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  如果你看到这边,答案都正确,而且没有丝毫的疑问,那么对于你来说这篇博文就此结束了,如果没有,请继续翻阅。


  首先从最基础的开始讲起,首先通过反编译来看一看自动装箱和拆箱的过程: 
  首先看如下一段程序:

public class AutoBoxing2{    public static void main(String[] args)    {        Integer a = 1;        Integer b = 2;        Integer c = a+b;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  反编译结果为:(如果对于java反编译不太了解的朋友可以先看一下《通过Java反编译揭开一些问题的真相》)

public jvm.optimize.AutoBoxing2();    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0                1: invokespecial #1                  // Method java/lang/Object."<init>":()V         4: return              LineNumberTable:        line 3: 0  public static void main(java.lang.String[]);    flags: ACC_PUBLIC, ACC_STATIC    Code:      stack=2, locals=4, args_size=1         0: iconst_1               1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;         4: astore_1               5: iconst_2               6: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;         9: astore_2              10: aload_1               11: invokevirtual #3                  // Method java/lang/Integer.intValue:()I        14: aload_2               15: invokevirtual #3                  // Method java/lang/Integer.intValue:()I        18: iadd                  19: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;        22: astore_3              23: return              LineNumberTable:        line 8: 0        line 9: 5        line 10: 10        line 11: 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

  可以看到Integer a=1实际上自动装箱为Integer a = Integer.valueOf(1),而在进行a+b的时候可以看到进行了自动拆箱,将a拆箱为Integer.intValue();然后将a和b的int值相加,相加之后有进行了自动装箱:Integer c=Integer.valueOf(3).


  接下来我们就可以上面题目中给出的 System.out.println(c==d);和System.out.println(e==f);他们分别的结果为true和false。 
  知道Integer会缓存-128至127的朋友估计这两条语句的输出结果都能答对。 
  如果没有答对,请看解析: 
  Integer c=3;会自动装箱为Integer c = Integer.valueOf(3),那么看一下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);    }    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) -1);            }            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() {}    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

  可以看到实际上Integer会缓存-128值127的内容,如果值在这个区间之内(比如c和d),那么就会返回IntegerCache中的引用,所以Integer c= Integer d = IntegerCache.cache[3+(–128)] = IntegerCache.cache[131], c和d是相等的。 
  但是如果超过这个区间,比如e和f,则Integer e = new Integer(321); Integer f = new Integer(321);new出来的自然是在堆中新开辟的内存,两份地址不同,自然e和f不同,也就是如果遇到这样的情况:

Integer m = new Integer(2);Integer n = new Integer(2);System.out.println(m==n);
  • 1
  • 2
  • 3

  那么输出的结果是false(如果Integer m=2; Intger n=2则m和n相同)


  接着再说System.out.println(c==(a+b)); 
  我们看如下代码:

Integer a = 1;Integer b = 2;Integer c = 3;System.out.println(c==(a+b));
  • 1
  • 2
  • 3
  • 4

  反编译之后:

          0: iconst_1               1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;         4: astore_1               5: iconst_2               6: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;         9: astore_2              10: iconst_3              11: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;        14: astore_3              15: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;        18: aload_3               19: invokevirtual #4                  // Method java/lang/Integer.intValue:()I        22: aload_1               23: invokevirtual #4                  // Method java/lang/Integer.intValue:()I        26: aload_2               27: invokevirtual #4                  // Method java/lang/Integer.intValue:()I        30: iadd                  31: if_icmpne     38        34: iconst_1              35: goto          39        38: iconst_0              39: invokevirtual #5                  // Method java/io/PrintStream.println:(Z)V        42: return    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

  可以看到实际在c==(a+b)的时候是执行拆箱机制,实际上就是在运算3==2+1,当然就是true咯。


  继续说明: System.out.println(c.equals(a+b)); 
  同样看一下c.equals(a+b)反编译的结果(篇幅限制,只截取部分相关的结果):

        19: aload_1               20: invokevirtual #4                  // Method java/lang/Integer.intValue:()I        23: aload_2               24: invokevirtual #4                  // Method java/lang/Integer.intValue:()I        27: iadd                  28: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;        31: invokevirtual #5                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  可以看到a+b先拆箱为int再相加之后再装箱为Integer型与c进行equals比较,那么我们再看一下equals()方法的源码:

    public boolean equals(Object obj) {        if (obj instanceof Integer) {            return value == ((Integer)obj).intValue();        }        return false;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  通过查看源码可知此条语句的输出结果为true。


  最后来看一下System.out.println(g==(a+b));和System.out.println(g.equals(a+b));两条语句。 
  System.out.println(g==(a+b));由前面的推论可知最后g拆箱为long型,a+b为int型,基础类型int可以自动升级为long,所以输出为true。 
  对于System.out.println(g.equals(a+b));可以看一下Long的equals()方法。

    public boolean equals(Object obj) {        if (obj instanceof Long) {            return value == ((Long)obj).longValue();        }        return false;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  对于(a+b)来说是Integer类型,所以返回false. 
  鉴于包装类的“==”运算在不遇到算术运算的情况下不会自动拆箱,以及它们equals()方法不处理数据转型的关系,博主建议在实际编码中要尽量避免这样使用自动装箱与拆箱机制。

0 0
原创粉丝点击