Java自动装箱和自动拆箱

来源:互联网 发布:淘宝店售假违规 编辑:程序博客网 时间:2024/06/11 09:56

定义:

自动装箱:Java自动将原始数据类型转化为相应的包装类对象,例如,把int转化为Integer。
拆箱:自动装箱的反过程,相应的包装类对象转化为原始数据类型,例如,把Integer转为int。

内置类型和包装器内置类型包装器类型booleanBooleancharCharacterbyteByteshortShortintIntegerlongLongfloatFloatdoubleDoublevoidVoid

实现机制:

     当发生自动装箱时,编译器自动调用了valueOf()方法,得到相应的包装器对象,当发生拆装时,编译器自动调用了xxxValue()方法(例如IntValue,LongValue等)。
  • 如若想确认是否调用了相应的方法,可以在相应的位置设置断点,debug一下就能看到进入相应的方法。

  • 使用java自带的javap反编译工具也能看得出来。

性能的比较:

自动装箱和拆箱是编译器分别通过自动调用valueOf()和xxxValue()来实现的,使用包装器类型和直接使用基本数据类型上就会有一性能的差距。
[java] view plain copy
 print?
  1. Long sum = new Long(0);//sum是包装器Long  
  2. for(long i=0;i<Integer.MAX_VALUE;i++){  
  3.      sum += i;  
  4. }  
  5. windows7, I5的处理器,4G的内存,时间:11S  
  6.   
  7. long sum = new Long(0);//sum是基本类型long  
  8. for(long i=0;i<Integer.MAX_VALUE;i++){  
  9.      sum += i;  
  10. }  
  11. windows7, I5的处理器,4G的内存,时间:1S  

从结果可以看出,时间上相差10秒,差距还是不小的。

常见面试题目:

题目1:

[java] view plain copy
 print?
  1. public class App {  
  2.         public static void main(String[] args) {  
  3.               Integer i1 = 127;  
  4.               Integer i2 = 127;  
  5.               Integer i3 = 128;  
  6.               Integer i4 = 128;  
  7.                 
  8.               System. out.println(i1 == i2);  
  9.               System. out.println(i3 == i4);  
  10.        }  
  11.   
  12. }  
程序的输出是什么呢,如果你的答案是true,true的话,以下就值得这你留步了。程序的输出是true,false。
我们先看源代码:
[java] view plain copy
 print?
  1. public static Integer valueOf(int i) {  
  2.     assert IntegerCache.high >= 127;  
  3.     if (i >= IntegerCache.low && i <= IntegerCache. high)  
  4.         return IntegerCache.cache[i + (-IntegerCache.low)];  
  5.     return new Integer(i);  
  6. }  
在Integer里,valueOf是以上这样实现的,为了降低内存占用和垃圾回收的成本,类库里增加了一个cache,保存常用的int包装器对象(-128 ~ 127),当数值在这个范围时,就从cache里取出对象,所以i1和i2指同一个内在块,输出就为true。而i3和i4的值大于127,就不会在这个cache里取值,重新new一个对象,因此不是同一个内存块,输出就为false。

Integer和Long都是使用了cache机制,所以在 -128 ~ 127 之间,比较都为true,
但是Integer的cache大小可以通过参数来改变,但是Long却不行。


  1. -Djava.lang.Integer.IntegerCache.high=300  
  2. -Djava.lang.Integer.IntegerCache.low=-300  
  3.   
  4. Boolean也使用了cache的机制,源代码如下:  
  5. public static final Boolean TRUE = new Boolean(true);  
  6. public static final Boolean FALSE = new Boolean(false);  
  7.   
  8. public static Boolean valueOf(boolean b) {  
  9.     return (b ? TRUE : FALSE);  
  10. }  

题目2:


  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         Integer a = 1;  
  4.         Integer b = 2;  
  5.         Integer c = 3;  
  6.         Long g = 3L;  
  7.         Long h = 2L;  
  8.   
  9.         System.out.println(c==(a+b));//1  
  10.         System.out.println(c.equals(a+b));//2  
  11.         System.out.println(g==(a+b));//3  
  12.         System.out.println(g.equals(a+b));//4  
  13.         System.out.println(g.equals(a+h));//5  
  14.     }  
  15. }  

输出:
true
true
true
false
true

  1. a拆箱,b拆箱,相加结果为int,c拆箱,int和int相比较.
  2. a拆箱,b拆箱,相加结果为int,方法equals的参数是Object,所以int又要装箱,引用比较,又因为数据3在cache里,所以为true.
  3. a拆箱,b拆箱,相加结果为int,转化为long型,g拆箱,数值相比较,为true.
  4. a拆箱,b拆箱,相加结果为int,方法equals的参数是Object,所以int又要装箱为Integer,Long和Integer比较,所以为false.
  5. a拆箱,b拆箱,相加结果为long,方法equals的参数是Object,所以要装箱为Long,Long和Long相比较,所以为true。

从以上测试中,可以得出以下结论:
1:当基本数据类型和包装器类型
做 ==, + 和 - 运算时,包装器类型会拆箱为基本数据类型,再做运算。

陷阱1

[java] view plain copy
  1. Integer integer100=null;  
  2. int int100=integer100;  

          这两行代码是完全合法的,完全能够通过编译的,但是在运行时,就会抛出空指针异常。其中,integer100为Integer类型的对象,它当然可以指向null。但在第二行时,就会对integer100进行拆箱,也就是对一个null对象执行intValue()方法,当然会抛出空指针异常。所以,有拆箱操作时一定要特别注意封装类对象是否为null。

陷阱2
由于JDK的8个包装类和String 都是不可变类,即被创建后,实例的实例变量不可以改。
ong t = System.currentTimeMillis();Long sum = 0L;for (long i = 0; i < Integer.MAX_VALUE; i++) {    sum += i;}System.out.println("total:" + sum);System.out.println("processing time: " + (System.currentTimeMillis() - t) + " ms");

输出结果: 
总数:2305843005992468481 
处理时间:6756 ms

仔细琢磨一下,你可能会想到下面这种执行速度更快的实现方法:

long t = System.currentTimeMillis();//Long sum = 0L;
long sum = 0L;for (long i = 0; i < Integer.MAX_VALUE; i++) {    sum += i;}System.out.println("total:" + sum);System.out.println("processing time: " + (System.currentTimeMillis() - t) + " ms") ;

输出结果: 
总数:2305843005992468481 
处理时间:1248 ms

为啥?因为Long包装类是不可变类,因此,前文出现的第一段代码段会自动转化为如下代码。所以,导致处理时间较长的原因也就水落石出了:不必要地创建了2147483647个”Long“类型实例。

long t = System.currentTimeMillis();Long sum = 0L;for (long i = 0; i < Integer.MAX_VALUE; i++) {sum += new Long(i);}System.out.println("total:" + sum);System.out.println("processing time: " + (System.currentTimeMillis() - t) + " ms") ;

由此可知,想要编写速度更快的 Java 代码,我们也需要考虑”Autoboxing”与”Unboxing”这样的基础概念。

另外像Long sum=0L; sum++; 会导致产生一个新的Long对象(即引用变量sum 在第二次指向了一个新的Long对象),
而第一次的那个Long对象就会被回收。

原创粉丝点击