Java 装箱 拆箱

Posted shiyuan310

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 装箱 拆箱相关的知识,希望对你有一定的参考价值。

以前都没有听说过,这次看海子的博客,真是长见识了!

https://www.cnblogs.com/dolphin0520/p/3780005.html

简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

1         //自动装箱
2         Integer num = 3;
3         //自动拆箱
4         int count = num;

在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

因此可以用一句话总结装箱和拆箱的实现过程:

装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。

下面这段代码是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);
    }

在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

IntegerCache.cache[]是一个静态的Integer数组对象,也就是说最终valueOf返回的都是一个Integer对象。

上面我们看到在Integer的构造函数中,它分两种情况: 

1、i >= 128 || i < -128 =====> new Integer(i) 
2、i < 128 && i >= -128 =====> SMALL_VALUES[i + 128]

1 static final Integer cache[] = new Integer[256];

cache本来已经被创建好,也就是说在i >= 128 || i < -128是会创建不同的对象,在i < 128 && i >= -128会根据i的值返回已经创建好的指定的对象。

下面是几个例子:

 1 package lesson1213;
 2 
 3 public class TestZhuangXiang {
 4 
 5     public static void main(String[] args) {
 6         //自动装箱
 7         Integer num1 = 3;        
 8         Integer num2 = 3;
 9         
10         Integer num3 = 200;
11         Integer num4 = 200;
12         
13         System.out.println(num1==num2);  //true
14         System.out.println(num3==num4);  //false
15         
16     }
17 
18 }

如果是Double类呢?

1         Double d1 = 1.0;
2         Double d2 = 1.0;
3         Double d3 = 200.0;
4         Double d4 = 200.0;
5         
6         System.out.println(d1==d2);  //false
7         System.out.println(d3==d4);  //false

看看上面的执行结果,跟Integer不一样,这样也不必奇怪,因为它们的valueOf实现不一样,结果肯定不一样,那为什么它们不统一一下呢? 
这个很好理解,因为对于Integer,在(-128,128]之间只有固定的256个值,所以为了避免多次创建对象,我们事先就创建好一个大小为256的Integer数组SMALL_VALUES,所以如果值在这个范围内,就可以直接返回我们事先创建好的对象就可以了。

但是对于Double类型来说,我们就不能这样做,因为它在这个范围内个数是无限的。 
总结一句就是:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

所以在Double里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。

1     public static Double valueOf(double d) {
2         return new Double(d);
3     }

下面我们进行一个归类: 
Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。 
Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。

下面对Integer派别进行一个总结,如下图: 
这里写图片描述

下面我们来看看另外一种情况:Boolean类

1         Boolean b1 = true;
2         Boolean b2 = true;
3         Boolean b3 = false;
4         Boolean b4 = false;
5         System.out.println(b1==b2); //true
6         System.out.println(b3==b4); //true

可以看到返回的都是true,也就是它们执行valueOf返回的都是相同的对象。

1     public static Boolean valueOf(boolean b) {
2         return (b ? TRUE : FALSE);
3     }

可以看到并没有创建对象,因为已经在内部提前创建好了两个对象,因为只有两种情况,这样也是为了避免重复创建太多对象。

1     public static final Boolean TRUE = new Boolean(true);
2     public static final Boolean FALSE = new Boolean(false);

下面讲述下equal  和 == 在这几个类中的应用

 1 package lesson1213;
 2 
 3 public class Test {
 4     
 5     /*1:== 比较两个值是否相等。如果作用于基本数据类型,比较其值是否相等。
 6          * 如果作用于引用类型变量,则比较的是所指向对象的地址。
 7       2:在object中equal,equal方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。
 8       但是有一些类例如String,Double, Date,Interger等,都对equal方法进行了重写用来比较应用的对象所存储的值是否相等。
 9       注意equal不能用于基本数据类型。
10     */    
11     
12     public static void main(String[] args) {
13         Integer num1 = 200;
14         int num2 = 200;
15         System.out.println(num1==num2);         //true
16         System.out.println(num2==num1);            //true    
17         System.out.println(num1.equals(num2));  //true
18         
19         Integer a = 1;
20         Integer b = 2;
21         Integer c = 3;
22         Integer d = 3;
23         Integer e = 300;
24         Integer f = 300;
25         Long g = 3L;
26         Long h = 2L;
27         
28         Integer e1 = 100;
29         Integer e2 = 200;
30         int e3=100;
31         int e4=200;
32         
33         System.out.println(c==d);              //true,  没有疑问
34         System.out.println(e==f);              //false, 超过范围-128~127,每次都重新new
35         System.out.println(e==f+0);            //true, 如果有操作运算符,就是比较的值
36         System.out.println(c==(a+b));          //true
37         System.out.println(c.equals((a+b)));   //true
38         System.out.println(g==(a+b));          //true   //有操作运算符
39         System.out.println(g.equals((a+b)));   //false  //类型不一样
40         System.out.println(g.equals((a+h)));   //true   //自动提升类型
41         System.out.println(g==(a+h));          //true    
42         System.out.println(e==(e1+e2));        //true
43         System.out.println(e.equals((e1+e2))); //true
44         System.out.println(e==(e1+e4));        //true
45         System.out.println(e.equals((e2+e3))); //true
46     }
47 }

总结:

1:当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动  拆箱的过程)。

补充:"=="只有两个都是包装类型,并且没有操作运算,才是比较是否指向同一个对象。如果有一个是包装类型,另一个不是包装类型,则比较的就是值。

          当两种不同类型用==比较时,包装器类的需要拆箱, 当同种类型用==比较时,会自动拆箱或者装箱

          当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。

2:equal, 首先判断类型是否一样,如果一样,就看值是否相等。对于包装器类型,equals方法并不会进行类型转换。

下面是Integer中对equal进行重载:

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

line36由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。

line37对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。

下面这个例子有点奇怪,自己也不是很了解,但是程序运行结果就是这样:会自动提升类型,所以相等

 1         int e5 = 3;
 2         long e6 = 3;        
 3         //Long e66 = 3;                        //编译失败Type mismatch: cannot convert from int to Long
 4         Long e666=3L; 
 5         Double e7 = 3.0;
 6         Integer e8 = 3;
 7         
 8         System.out.println(e5==e6);            //true
 9         System.out.println(e5==e666);          //true
10         System.out.println(e5==e7);            //true
11         System.out.println(e6==e7);            //true, 一边是基础类型,一般是包装类,比较值
12         System.out.println(e6==e8);            //true
13         //System.out.println(e7==e8);          //编译失败Incompatible operand types Double and Integer

到此处,应该对包装类和装箱,拆箱比较了解了,在遇见这样的题目应该没有问题了。

 

以上是关于Java 装箱 拆箱的主要内容,如果未能解决你的问题,请参考以下文章

java拆箱和装箱

java 自动装箱拆箱

Java 自动装箱与拆箱

java 自动装箱和拆箱

Java中装箱和拆箱的代码

了解Java的自动装箱与拆箱