[Java]浅谈Java中的包装器类型以及自动装箱与拆箱
什么是对象包装器
这得从Java的八大基础数据类型讲起,Java作为一种强类型的语言,它为每个变量声明了一种类型,其中一共有8种基本类型,它们分别是:
基本类型 | 存储所需大小 | 对象包装器 |
---|---|---|
int | 4字节 | Integer |
short | 2字节 | Short |
long | 8字节 | Long |
byte | 1字节 | Byte |
float | 4字节 | Float |
double | 8字节 | Double |
char | 2字节 | Character |
boolean | 根据JVM的编译行为会有不同的结果(1/4) | Boolean |
注意:使用float和double进行运算时,可能会产生误差。金融相关的数据推荐使用BigDecimal。这是由于二进制系统中无法精确地表示分数所导致的
基础类型的内存分配策略
这里我们从阿里巴巴的开发手册中来切入:
对于Integer var = ? 在-128至127 范围内的赋值,Integer 对象是在 IntegerCache.cache 产 生,会复用已有对象。
这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数 据,都会在堆上产生,并不会复用已有对象,
这是一个大坑,推荐使用 equals方法进行判断。
由此可见,常用的一些常量,会在栈中进行存储,超出一定的界限后,这些数值存储在堆中。
Java使用了基本类型来让一些常用的数值可以存储在栈中,从而让整体的内存消耗更加小巧,如果有多个参数的值都是100,那么统一指向100这个数字在JVM栈中的地址即可。但是编程中很多时候,我们需要这些数字具备对象的能力,比如说我从序列化的JSON中得到了一个数值,它的类型是String->"100",那么有没有办法让它变成int呢?
为了解决这些问题,Java推出了包装器类型。
使用对象包装器时,你需要注意的几个点
- 对象包装器是不可变的。对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化。
如果需要理解这个思想,你可以执行这段代码
public class IntegerTest {
public static void main(String[] args) {
Integer two = Integer.valueOf(2);
change(two);
System.out.println(two);
}
public static void change(Integer integer) {
integer = integer + 1;
}
}
- 对象包装器声明为final,因此不可以被继承
- 在定义集合的时候,不支持基本类型,可以使用包装器
- 相对于基础类型来说,包装器往往意味着更多的空间
- 用equals去比较对象包装器的值,而不是==
- 对于不可预期值的包装器值,最好加上空值判断
自动装箱与拆箱
-
自动装箱: 编译器自动将基本类型转化成包装器类型的行为
box.png
-
自动拆箱: 编译器自动将包装器类型转化成基本类型的行为
unweave.png
自动拆箱引发的NPE问题
包装器类可以允许引用为null
,所以自动拆箱有可能会抛出一个NullPointerException
。
- 拆箱时引发的空指针异常
Integer integer = null;
System.out.println(2*integer);
integer.intValue(); 这个方法不允许integer为null
包装器类型的升级
如果在一个计算中混用了Integer和Double类型,Integer值就会拆箱,提示为Double,再进行装箱。
Integer integer = 1;
Double douNum = 2.0;
System.out.println(integer*douNum);