Java 自动装箱/拆箱要注意的坑
·
基本类型与包装类
要明白装箱拆箱,首先要分清 Java 中的两种数据类型:
表格
| 基本数据类型 | 对应的包装类(引用类型) |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
- 基本类型:是简单值类型,存储在栈中,不能调用方法
- 包装类:是类(引用类型),存储在堆中,可以调用方法
以 int 与 Integer 为例子,在64位系统下,int 只占用 4 字节的空间,而 Integer 在开启指针压缩的情况下还要占用16字节的空间。无论是读写效率还是存储效率,基本类型的效率都比包装类的效率高。
自动装箱与拆箱
自动装箱(Autoboxing)
自动将基本数据类型转换为对应的包装类对象的过程,由 Java 编译器自动完成,无需手动调用构造方法。
// 手动装箱(传统方式)
Integer num1 = new Integer(10);
// 自动装箱(编译器自动转换,等价于 Integer.valueOf(10))
Integer num2 = 10;
自动拆箱(Unboxing)
自动将包装类对象转换为对应的基本数据类型的过程,同样由编译器自动完成。
Integer num3 = 20;
// 手动拆箱(传统方式)
int num4 = num3.intValue();
// 自动拆箱(编译器自动转换,等价于 num3.intValue())
int num5 = num3;
实际应用场景
自动装箱 / 拆箱最常用在以下场景,也是新手最容易接触到的场景:
集合操作
集合只能存储对象,不能直接存储基本数据类型,自动装箱 / 拆箱让操作更简洁:
import java.util.ArrayList;
import java.util.List;
public class BoxDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// 自动装箱:int → Integer
list.add(10);
list.add(20);
// 自动拆箱:Integer → int
int firstNum = list.get(0);
System.out.println(firstNum); // 输出:10
}
}
方法参数 / 返回值
当方法的参数 / 返回值是包装类,但传入 / 接收的是基本类型时,自动转换:
public class BoxDemo2 {
// 方法参数是包装类Integer
public static void printNum(Integer num) {
System.out.println(num);
}
// 方法返回值是包装类Integer
public static Integer getNum() {
// 自动装箱:int → Integer
return 30;
}
public static void main(String[] args) {
// 自动装箱:int → Integer(传入基本类型10,自动转为Integer)
printNum(10);
// 自动拆箱:Integer → int(接收包装类返回值,自动转为int)
int result = getNum();
System.out.println(result); // 输出:30
}
}
运算场景
包装类对象参与算术运算时,会先自动拆箱为基本类型,再计算:
public class BoxDemo3 {
public static void main(String[] args) {
Integer a = 5;
Integer b = 10;
// 自动拆箱:a和b先转为int,再相加;结果自动装箱为Integer
Integer sum = a + b;
// 自动拆箱:sum转为int,再输出
System.out.println(sum); // 输出:15
}
}
注意事项
空指针异常
Map<Character, Character> map = new HashMap<>();
char a = 'A';
if(a == map.get('A')){
System.out.println("true");
}else{
System.out.println("false");
}
上述代码会报空指针异常,但如果把 a 的类型改为 Character包装类,则能正确运行。
本质是因为map中没有存储 ‘A’ 的键值对,因此返回的是空对象。又因为我们是用基础类型来和其比较,系统会尝试将空对象进行拆箱,因此报出空指针异常。所以要把 a 转为包装类型,避免空指针异常。
缓存池问题
Integer有缓存池(默认 - 128~127),这个范围内的自动装箱会复用对象,超出范围则新建对象:
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true(复用缓存对象)
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false(新建对象,==比较地址)
System.out.println(c.equals(d)); // true(equals比较值,推荐用equals)
性能影响
频繁的装箱 / 拆箱会产生额外的对象,增加 GC 压力,高性能场景(如循环运算)建议直接用基本类型。
更多推荐

所有评论(0)