Java基础:自动装箱和自动拆箱
感谢,原文链接:
https://www.cnblogs.com/dolphin0520/p/3780005.html
Java的自动包装类型
首先基本数据类型:4类8种
基本数据类型 | 对应包装器类 |
---|---|
byte(1字节) | Byte |
short(2字节) | Short |
int(4字节) | Integer |
long(8字节) | Long |
float(4字节) | Float |
double(8字节) | Double |
char(2字节) | Character |
boolean(未定) | Boolean |
自动装箱就是自动将基本数据类型转换为包装器类型;
自动拆箱就是自动将包装器类型转换为基本数据类型。
简单来说就是:
Integer i = 1; //装箱
int j = i; //拆箱
装箱和拆箱怎么实现的
public static void main(String[] args) {
Integer i = 1;
int j = i;
}
123456
上面的代码反编译后如下图
从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。其他的也类似,比如Double、Character。
总结:装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。
面试题解析
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1 == i2); // true
System.out.println(i3 == i4); // false
}
1234567891011
看下Integer的部分源码就知道了
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
12345
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) {
try {
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);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
12345678910111213141516171819202122232425262728293031323334
从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。
上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。
private static void testDouble() {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2); //false
System.out.println(i3==i4); //false
}
1234567891011
在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。
注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
Double、Float的valueOf方法的实现是类似的。
谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别
当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:
1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;
2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。
private static void testAll() {
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); //true
System.out.println(e==f); //false
System.out.println(c==(a+b)); // true
System.out.println(c.equals(a+b)); //true
System.out.println(g==(a+b)); // true
System.out.println(g.equals(a+b)); //false
System.out.println(g.equals(a+h));// true
}
123456789101112131415161718192021
首先第一个没有什么疑问,c和d都来自缓存,所以相等输出true.
第二个e,f的输出也没什么疑问,都会创建新的对象,所以不等,输出false.
第三个可能刚刚看的时候有点懵逼,但是仔细想想就知道答案,首先会执行的是a+b,首先a和b是包装类型,是对象,他们是不可能相加的,所以先要执行拆箱的操作将a,b拆成基本数据类型再做相加的操作,完了之后与c进行比较,c是包装类型,所以c也会自动拆箱进行比较
第四个的其实就是equals()方法和 == 的区别,equals()和 == 其实一样,判断的是两个对象是否相等,但是Integer对equals()方法进行了重写,他比较的就是值,他不会进行类型转换。所以执行a+b的时候依然会有一个拆箱的过程,但是指向equals的时候她会有一个装箱的过程。所以输出的是true.
第五个和第三个有点类似,都是转化为基本的数据类型,然后比较数值,所以他们数值是相等的,返回的是true.
第六个也是先拆箱,然后相加运算,但是请注意这个和上一个直接==不同,上面可以拆箱比较数值,而这个equals()方法里面是Object类型的数据,也就是说他要传一个引用类型的参数,所以a+b会装箱成Integerr类型的数据,但是g是Long类型。类型不同就比较不了了。
第七个,对于a+h,先自动触发拆箱,就变成了int类型和long类型相加,这个会触发类型自动提升,结果是long类型的,然后会触发装箱过程,就变成Long了。因此比较结果是true。