自動裝箱(基本類型 → 包裝類)與拆箱(包裝類 → 基本類型)是 JDK 5 引入的語法糖。本文從字節碼層面揭示實現原理,深度剖析 IntegerCache 緩存機制,並通過 5 道經典面試題驗證掌握程度。
| 基本類型 | 包裝類 | 裝箱方法 | 拆箱方法 |
|---|---|---|---|
| int | Integer | Integer.valueOf(int) | Integer.intValue() |
| byte | Byte | Byte.valueOf(byte) | Byte.byteValue() |
| short | Short | Short.valueOf(short) | Short.shortValue() |
| long | Long | Long.valueOf(long) | Long.longValue() |
| float | Float | Float.valueOf(float) | Float.floatValue() |
| double | Double | Double.valueOf(double) | Double.doubleValue() |
| char | Character | Character.valueOf(char) | Character.charValue() |
| boolean | Boolean | Boolean.valueOf(boolean) | Boolean.booleanValue() |
// 源碼 Integer i = 10; // 裝箱 int n = i; // 拆箱 // 反編譯字節碼 ICONST_10 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; // 裝箱調用 valueOf ASTORE 1 ALOAD 1 INVOKEVIRTUAL java/lang/Integer.intValue ()I // 拆箱調用 intValue ISTORE 2
結論:裝箱本質調用 Wrapper.valueOf(),拆箱本質調用 Wrapper.xxxValue()。
public static Integer valueOf(int i) {
if (i >= -128 && i <= IntegerCache.high) // 默認高位 127
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high = 127; // 可通過 -XX:AutoBoxCacheMax=N 調大
static final Integer[] cache = new Integer[(high - low) + 1];
static {
for (int k = 0; k < cache.length; k++)
cache[k] = new Integer(low + k);
}
}
關鍵點:
new Integer()Byte/Short/Long/Character 同理緩存對應範圍TRUE/FALSEInteger i1 = 100, i2 = 100, i3 = 200, i4 = 200; System.out.println(i1 == i2); // true (緩存命中) System.out.println(i3 == i4); // false (新對象)
Double d1 = 100.0, d2 = 100.0, d3 = 200.0, d4 = 200.0; System.out.println(d1 == d2); // false System.out.println(d3 == d4); // false
Boolean b1 = false, b2 = false, b3 = true, b4 = true; System.out.println(b1 == b2); // true System.out.println(b3 == b4); // true
Integer a = new Integer(100); // 每次 new,不走緩存 Integer b = 100; // 走 valueOf,可能命中緩存 System.out.println(a == b); // false (引用不同) System.out.println(a.equals(b)); // true (值相等)
差異總結:① 不觸發裝箱 ② 效能上自動裝箱通常優於 new(緩存複用)
Integer a=1, b=2, c=3, d=3, e=321, f=321; Long g=3L, h=2L; c == d // true (緩存) e == f // false (超出緩存) c == (a + b) // true (a+b 觸發拆箱→int 運算→裝箱比較值) c.equals(a + b) // true (拆箱運算後裝箱再 equals) g == (a + b) // true (Long 拆箱為 long 比較數值) g.equals(a + b) // false (Integer 1+2=3 裝箱為 Integer,Long.equals(Integer) 返回 false) g.equals(a + h) // true (a+h=3L 裝箱為 Long,Long.equals(Long) 返回 true)
核心規則:
== 兩側均為包裝類引用 → 比較引用地址== 任一側為表達式(含算術運算)→ 觸發拆箱比較數值equals() 不進行類型轉換,Integer.equals(Long) 恆為 falseintValue()Trove/FastUtil 原始類型集合-XX:AutoBoxCacheMax=2000