以前都没有听说过,这次看海子的博客,真是长见识了!
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
到此处,应该对包装类和装箱,拆箱比较了解了,在遇见这样的题目应该没有问题了。