# 自动装箱与拆箱

作者: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 中的 IntegerShortByteCharacterBoolean 等包装类,使用了 对象缓存池机制 来提高性能。

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

# 包装类缓存范围

包装类 缓存范围
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(),默认比较引用。但 IntegerString 等类都已重写,比较的是值。

# 缓存范围内

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

# 基本类型与包装类型比较(自动拆箱)

Integer a = 100;
int b = 100;
System.out.println(a == b); // true(a 拆箱为 int 后比较)
1
2
3

# null 拆箱风险

Integer a = null;
int b = a;  // ⚠️ NullPointerException
1
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

Map 判断 key 是否相等用 .hashCode().equals(),只要值相等即可。 即使 c == d 为 false,只要 c.equals(d) 为 true,仍然能取出。


# 四、缓存池调优

可通过 JVM 参数扩展缓存范围:

-XX:AutoBoxCacheMax=500
1

配合 Integer.valueOf() 使用,可以提高缓存命中率与内存复用。