装箱拆箱详解
今天面试,遇到了这个问题,不注意,答错了。回顾一下相关java知识。
装箱:将基本数据类型包装成引用数据类型,使其具有对象的性质。
拆箱:将引用型数据简化成基本数据类型。
举个例子:
Integer a=4;自动装箱
int b=new Integer(4);自动拆箱
为了进一步说明问题,写段代码。
public class DataType {
public static void main(String args[]) {
DataType dt = new DataType();
dt.m1();
dt.m2();
dt.m3();
dt.m4();
dt.m5();
dt.m6();
dt.m7();
dt.m8();
}
public void m1() {
Integer a = new Integer(4);
Integer b = 4;
System.out.println("m1 result " + (a == b));
}
public void m2() {
Integer a = new Integer(128);
Integer b = 128;
System.out.println("m2 result " + (a == b));
}
public void m3() {
Integer a = new Integer(4);
Integer b = new Integer(4);
System.out.println("m3 result " + (a == b));
}
public void m4() {
Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println("m4 result " + (a == b));
}
public void m5() {
Integer a = 4;
Integer b = 4;
System.out.println("m5 result " + (a == b));
}
public void m6() {
Integer a =128;
Integer b =128;
System.out.println("m6 result " + (a == b));
}
public void m7() {
Integer a = Integer.valueOf(4);
Integer b = 4;
System.out.println("m7 result " + (a == b));
}
public void m8() {
Integer a = Integer.valueOf(128);
Integer b = 128;
System.out.println("m8 result " + (a == b));
}
}
分析一下执行结果吧。
下面给出打印结果。你是否都对了呢?
我们来看一下。
第一组:
观察 m1,m2。
a和b是两个不同的对象,不难看出,上面两组都是false。
第二组:
观察m3,m4。
a和b 依旧是两个不同的对象。都为false。
第三组
观察m5,m6。
为什么一个true,一个false呢?
第四组
观察m7,m8。
为什么一个true,一个false呢?
true说明a和b是同一个对象。a通过Integer.valueOf()生成,b通过自动装箱生成。为什么会是同一个对象呢?下面为false又是怎么回事?
为说明问题,回顾一下装箱、拆箱的两个方法:
Integer.valueOf()返回一个Integer类型对象。
Integer.parseInt()返回一个int类型对象。
查看valueOf方法实现,代码如下:
/**
* Returns a <tt>Integer</tt> instance representing the specified
* <tt>int</tt> value.
* If a new <tt>Integer</tt> instance is not required, this method
* should generally be used in preference to the constructor
* {@link #Integer(int)}, as this method is likely to yield
* significantly better space and time performance by caching
* frequently requested values.
*
* @param i an <code>int</code> value.
* @return a <tt>Integer</tt> instance representing <tt>i</tt>.
* @since 1.5
*/
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
发现,[-128,127]区间的数据返回的是缓冲区中的对象,其他则是新创建的装箱对象。这就解释得通了。为进一步说明问题,这里看一下IntegerCache的源码:
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() {}
}
对于这个例子,也可以使用javap解析字节码查看:
f:\>javap -c DataType
Compiled from "DataType.java"
public class DataType {
public DataType();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class DataType
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method m1:()V
12: aload_1
13: invokevirtual #5 // Method m2:()V
16: aload_1
17: invokevirtual #6 // Method m3:()V
20: aload_1
21: invokevirtual #7 // Method m4:()V
24: aload_1
25: invokevirtual #8 // Method m5:()V
28: aload_1
29: invokevirtual #9 // Method m6:()V
32: aload_1
33: invokevirtual #10 // Method m7:()V
36: aload_1
37: invokevirtual #11 // Method m8:()V
40: return
public void m1();
Code:
0: new #12 // class java/lang/Integer
3: dup
4: iconst_4
5: invokespecial #13 // Method java/lang/Integer."<init>":(I)V
8: astore_1
9: iconst_4
10: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: astore_2
14: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
17: new #16 // class java/lang/StringBuilder
20: dup
21: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
24: ldc #18 // String m1 result
26: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: aload_1
30: aload_2
31: if_acmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
42: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
45: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: return
public void m2();
Code:
0: new #12 // class java/lang/Integer
3: dup
4: sipush 128
7: invokespecial #13 // Method java/lang/Integer."<init>":(I)V
10: astore_1
11: sipush 128
14: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
17: astore_2
18: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
21: new #16 // class java/lang/StringBuilder
24: dup
25: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
28: ldc #23 // String m2 result
30: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: aload_1
34: aload_2
35: if_acmpne 42
38: iconst_1
39: goto 43
42: iconst_0
43: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
46: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
49: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
52: return
public void m3();
Code:
0: new #12 // class java/lang/Integer
3: dup
4: iconst_4
5: invokespecial #13 // Method java/lang/Integer."<init>":(I)V
8: astore_1
9: new #12 // class java/lang/Integer
12: dup
13: iconst_4
14: invokespecial #13 // Method java/lang/Integer."<init>":(I)V
17: astore_2
18: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
21: new #16 // class java/lang/StringBuilder
24: dup
25: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
28: ldc #24 // String m3 result
30: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: aload_1
34: aload_2
35: if_acmpne 42
38: iconst_1
39: goto 43
42: iconst_0
43: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
46: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
49: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
52: return
public void m4();
Code:
0: new #12 // class java/lang/Integer
3: dup
4: sipush 128
7: invokespecial #13 // Method java/lang/Integer."<init>":(I)V
10: astore_1
11: new #12 // class java/lang/Integer
14: dup
15: sipush 128
18: invokespecial #13 // Method java/lang/Integer."<init>":(I)V
21: astore_2
22: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
25: new #16 // class java/lang/StringBuilder
28: dup
29: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
32: ldc #25 // String m4 result
34: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
37: aload_1
38: aload_2
39: if_acmpne 46
42: iconst_1
43: goto 47
46: iconst_0
47: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
50: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
53: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
56: return
public void m5();
Code:
0: iconst_4
1: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: iconst_4
6: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: astore_2
10: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
13: new #16 // class java/lang/StringBuilder
16: dup
17: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
20: ldc #26 // String m5 result
22: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: aload_1
26: aload_2
27: if_acmpne 34
30: iconst_1
31: goto 35
34: iconst_0
35: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
38: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
41: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: return
public void m6();
Code:
0: sipush 128
3: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_1
7: sipush 128
10: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: astore_2
14: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
17: new #16 // class java/lang/StringBuilder
20: dup
21: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
24: ldc #27 // String m6 result
26: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: aload_1
30: aload_2
31: if_acmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
42: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
45: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: return
public void m7();
Code:
0: iconst_4
1: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: iconst_4
6: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: astore_2
10: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
13: new #16 // class java/lang/StringBuilder
16: dup
17: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
20: ldc #28 // String m7 result
22: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: aload_1
26: aload_2
27: if_acmpne 34
30: iconst_1
31: goto 35
34: iconst_0
35: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
38: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
41: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: return
public void m8();
Code:
0: sipush 128
3: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_1
7: sipush 128
10: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: astore_2
14: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
17: new #16 // class java/lang/StringBuilder
20: dup
21: invokespecial #17 // Method java/lang/StringBuilder."<init>":()V
24: ldc #29 // String m8 result
26: invokevirtual #19 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: aload_1
30: aload_2
31: if_acmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #20 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
42: invokevirtual #21 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
45: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: return
}
总结:
以Integer为例,说明。
1.使用new Integer()方式装箱每次创建的对象都不同。对于任何数值,即使数值相同,它们也是不同的对象。
2.对于自动装箱,其实是调用了Integer.valueOf()。对于使用这种方式装箱的对象,在[-128,127]之间的数据会优先使用缓冲区中的对象,如果数值相同,它们是同一个对象。在此区间外,即使是同一数值也是不同的对象。
3.使用new Integer()方式装箱,两者无论数值相同与否,都是不同对象。