申明:本博客仅作为学习笔记,摘自一下两个链接!!!
参考链接:https://blog.youkuaiyun.com/weixin_37650458/article/details/86714172
https://www.cnblogs.com/dolphin0520/p/3780005.html
一、装箱 与 拆箱
java基本数据类型都有对应的包装器类。
基本数据类型 | 位数 | 包装类 |
---|---|---|
int | 4 | Integer |
byte | 1 | Byte |
short | 2 | Short |
long | 8 | Long |
float | 4 | Float |
double | 8 | Double |
char | 2 | Character |
boolean | / | Boolean |
装箱:自动将基本数据类型转换为包装器类型
拆箱:自动将包装器类型转换为基本数据类型
Integer i = 10; //装箱
int n = i; //拆箱
二、装箱、拆箱实现原理
以Integer为例,
public class Main {
public static void main (String[] args) {
Integer i = 10;
int n = i;
}
}
通过反编译Main.class文件后得到(图是copy的):
从反编译得到的字节码内容看出,在装箱(Integer i = 10;)中,自动调用的是Integer的valueOf(int)方法;在拆箱(int n = i;)中,自动调用的是Integer的intValue方法。
可以得出结论:装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。
三、一些案例
写出输出结果
案例一
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
答案:
true
false
结果分析:
输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。
Integer的valueOf的源码:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
而其中IntegerCache类的实现为:
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
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() {}
}
从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。
上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。
案例二
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
答案:
false
false
结果分析:
以下为Double的valueOf()方法,它直接new了一个Double,所以会输出false, false
public static Double valueOf(double var0) {
return new Double(var0);
}
注意:
Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
Double、Float的valueOf方法的实现是类似的。
案例三
谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。
当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:
1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;
2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。
案例四
public class Main {
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;
Long h = 2L;
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));
System.out.println(g.equals(a+h));
}
}
答案:
true
false
true
true
true
false
true
结果分析:
当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。
对于(1) c == d 和 (2) e == f,相信通过之前的讲解应该是没问题的;
对于(3) c == (a+b) :由于 a 和 b是包装类型,是对象,不能进行相加。所以,得先拆箱,将a 和 b拆成基本数据类型再相加,相加结果是个基本数据类型,然后再将这个基本数据类型与c这个包装类进行比较,而c是包装类,所以c会自动拆箱比较。
对于(4) c.equals(a+b) :先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。
对于(5) g==(a+b) :基本的数据类型,然后比较数值,所以他们数值是相等的,返回的是true
对于(6) g.equals(a+b):先拆箱,然后相加运算,但是请注意这个和上一个直接==不同,上面可以拆箱比较数值,而这个equals()方法里面是Object类型的数据,也就是说他要传一个引用类型的参数,所以a+b会装箱成Integerr类型的数据,但是g是Long类型。类型不同就比较不了了
对于(7) g.equals(a+h):对于a+h,先自动触发拆箱,就变成了int类型和long类型相加,这个会触发类型晋升,结果是long类型的,然后会触发装箱过程,就变成Long了。因此比较结果是true