学过 Java 的同学或多或少都听过自动装箱拆箱, 下边通过代码和字节码文件加深下对自动拆箱装箱的理解.
1 为什么要有包装类型?
作为和基本数据类型对应的类类型存在,方便涉及到对象的操作, 比如泛型必须要求我们是对象数据类型.
2 自动装箱拆箱发生在什么时候?
自动拆箱装箱发生在代码编译期间.
通过例子来看下自动拆箱装箱是怎么做的:
public static void main(String[] args) {
Long a = 100L;
Long b = 100L;
long c = 100L;
Long d = new Long(100);
Long e = 1000L;
Long f = 1000L;
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a == d);
System.out.println(c == d);
System.out.println(e == f);
}
下面先公布答案:
true
true
false
true
false
蒙 a== b 为 true; 为什么 e== f 就是 false? a 和 c 是怎么比较的? a 和 d 又是什么情况?
接下来我们通过字节码文件看看到底有什么奥秘:
public static void main(java.lang.String[]);
Code:
0: ldc2_w #2 // long 100l
3: invokestatic #4 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
6: astore_1
7: ldc2_w #2 // long 100l
10: invokestatic #4 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
13: astore_2
14: ldc2_w #2 // long 100l
17: lstore_3
18: new #5 // class java/lang/Long
21: dup
22: ldc2_w #2 // long 100l
25: invokespecial #6 // Method java/lang/Long."<init>":(J)V
28: astore 5
30: ldc2_w #7 // long 1000l
33: invokestatic #4 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
36: astore 6
38: ldc2_w #7 // long 1000l
41: invokestatic #4 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
44: astore 7
46: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
49: aload_1
50: aload_2
51: if_acmpne 58
54: iconst_1
55: goto 59
58: iconst_0
59: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V
62: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
65: aload_1
66: invokevirtual #11 // Method java/lang/Long.longValue:()J
69: lload_3
70: lcmp
71: ifne 78
74: iconst_1
75: goto 79
78: iconst_0
79: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V
82: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
85: aload_1
86: aload 5
88: if_acmpne 95
91: iconst_1
92: goto 96
95: iconst_0
96: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V
99: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
102: lload_3
103: aload 5
105: invokevirtual #11 // Method java/lang/Long.longValue:()J
108: lcmp
109: ifne 116
112: iconst_1
113: goto 117
116: iconst_0
117: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V
120: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
123: aload 6
125: aload 7
127: if_acmpne 134
130: iconst_1
131: goto 135
134: iconst_0
135: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V
138: return
}
鬼画符? 我们看下这些鬼到底什么意思.
3: invokestatic #4 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
意思是执行 Long 的 valueof()方法 参数为基本类型 返回值为 Long 类型
看看 Long 的 valueof 方法
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
如果传入的 基本类型在 -128-127 之内就, 就从 LongCache 中取数据返回给我们. 看下 LongCache 干了啥
private static class LongCache {private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
}
}
原来如此,-128-127 就直接返回给我们 chche 中的数据, 超出这个范围直接 new 一个给我们.a== b 为 true,e== f 为 false 就说得通了.
65: aload_1
66: invokevirtual #11 // Method java/lang/Long.longValue:()J
69: lload_3
70: lcmp
拿出变量 a,执行 Long.longValue()返回一个基本数据类型, 在和 c 比较. 看下 longValue 方法
/**
* Returns the value of this {@code Long} as a
* {@code long} value.
*/
public long longValue() {return value;}
这部就是两个基本数据类型比较吗.
85: aload_1
86: aload 5
88: if_acmpne 95
取出 a 和 d 直接比较内存地址是否一样. 铁定不一样呀.
剩下的大家可以自己看下, 有不明白的可以评论问.
3 总结
自动拆箱装箱没有什么神秘的. 字节码可以告诉我们很东西. 如果某些概念理解不了, 试着看看字节码文件. 说不定会豁然开朗.
如果发现文章中有不妥之处, 希望大家指出, 共同进步.