# 自动装箱与拆箱
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
Java 为了让基本类型能像对象一样操作,引入了 包装类(Wrapper Classes)。
自动装箱(Autoboxing)和自动拆箱(Unboxing)是 Java 编译器在基本类型与包装类型之间 自动转换 的特性。
# 一、触发时机
- 自动装箱:将基本类型转换为包装类型,例如:
Integer i = 10; // 等价于 Integer i = Integer.valueOf(10);
1
- 自动拆箱:将包装类型转换为基本类型,例如:
int j = i; // 等价于 int j = i.intValue();
1
# 二、缓存机制
Java 中的 Integer、Short、Byte、Character、Boolean 等包装类,使用了 对象缓存池机制 来提高性能。
Integer.valueOf(int) 内部缓存示例:
public static Integer valueOf(int i) {
// 其中 low = -128 high = 127
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
1
2
3
4
5
6
2
3
4
5
6
# 包装类缓存范围
| 包装类 | 缓存范围 |
|---|---|
| Boolean | true / false |
| Byte | -128 ~ 127 |
| Character | 0 ~ 127 |
| Short | -128 ~ 127 |
| Integer | -128 ~ 127(默认) |
| Long | -128 ~ 127 |
| Float/Double | ❌ 无缓存 |
Integer缓存最大值可通过-XX:AutoBoxCacheMax设置
# 三、比较陷阱
❗ == 比较的是引用,.equals() 比较的是值。
如果超出缓存范围,会产生新对象,== 结果可能为 false。
注意:如果类没有重写
equals(),默认比较引用。但Integer、String等类都已重写,比较的是值。
# 缓存范围内
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true(缓存池)
System.out.println(a.equals(b)); // true(值相等)
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false(不同对象)
System.out.println(c.equals(d)); // true(值相等)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 基本类型与包装类型比较(自动拆箱)
Integer a = 100;
int b = 100;
System.out.println(a == b); // true(a 拆箱为 int 后比较)
1
2
3
2
3
# null 拆箱风险
Integer a = null;
int b = a; // ⚠️ NullPointerException
1
2
2
# Map 中的陷阱
Map<Integer, String> map = new HashMap<>();
Integer a = 100, b = 100;
Integer c = 200, d = 200;
map.put(a, "Value100");
map.put(c, "Value200");
System.out.println(map.get(b)); // Value100
System.out.println(map.get(d)); // Value200
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Map 判断 key 是否相等用 .hashCode() 和 .equals(),只要值相等即可。
即使 c == d 为 false,只要 c.equals(d) 为 true,仍然能取出。
# 四、缓存池调优
可通过 JVM 参数扩展缓存范围:
-XX:AutoBoxCacheMax=500
1
配合 Integer.valueOf() 使用,可以提高缓存命中率与内存复用。