装箱和拆箱的定义
装箱就是自动将基本数据类型转换为包装器类型,拆箱就是自动将包装器类型转换为基本数据类型。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱
基本数据类型对应的包装类:
原始类型 | 包装类型 |
int(4字节) | Integer |
byte(1字节) | Byte |
short(2字节) | Short |
long(8字节) | Long |
float(4字节) | Float |
double(8字节) | Double |
char(2字节) | Character |
boolean | Boolean |
装箱和拆箱的实现
以 float 和 Float 为例,装箱就是调用 Float 的 valueOf 方法 new 一个 Float 并赋值 float;
拆箱就是调用 Float 对象的 floatValue 方法并赋值返回给 float。
何时发生自动装箱和拆箱
当表格中基础类型与对应的包装类有如下几种情况时,编译器会自动帮我们进行装箱或拆箱:
- 进行 = 赋值操作(装箱或拆箱)
- 进行 +,-,*,/ 混合运算 (拆箱)
- 进行 >,<,== 比较运算(拆箱)
- 调用 equals 进行比较(装箱)
- ArrayList,HashMap 等集合类 添加基础类型数据时(装箱)
注意事项
自动装箱和拆箱可以使代码变得简洁,但是其也存在一些问题和极端情况下的问题,以下几点需要注意:
1、对象的缓存
在Java中,出于节省内存的考虑,JVM会缓存-128到127的 Integer 对象,当创建新的Integer对象时,如果符合这个这个范围,并且已有存在的相同值的对象,则返回这个对象,否则创建新的 Integer 对象。
public void test() {
// c 和 d 实际上是同一个对象,使用 "==" 比较返回 true
Integer c = 100;
Integer d = 100;
System.out.println(c == d);
// 200 超出了范围,e 和 f 不是同一个对象,所以使用 "==" 比较返回 false
Integer e = 200;
Integer f = 200;
System.out.println(c == d);
}
2、对象相等比较
"==" 可以用于原始值进行比较,也可以用于对象进行比较,当用于对象与对象之间比较时,比较的不是对象代表的值,而是检查两个对象是否是同一对象,这个比较过程中没有自动装箱发生。进行对象值比较不应该使用 "==",而应该使用对象对应的 equals 方法。
public void test() {
// 200 超出了范围,e 和 f 不是同一个对象,所以使用 "==" 比较返回 false,使用 equals 返回 true
Integer e = 200;
Integer f = 200;
System.out.println(c == d);
System.out.println(c.equals(d));
}
还有一个需要避免的问题就是混乱使用对象和原始数据值,一个具体的例子就是当我们在一个原始数据值与一个对象进行比较时,如果这个对象没有进行初始化或者为 Null,在自动拆箱过程中 obj.xxxValue 会抛出 NullPointerException。