基础回顾之 Java 中的自动装箱和自动拆箱

工作久了不免会对一些基础知识有些许遗忘,基础回顾是自己准备的一系列针对 Android 开发相关的一些基础知识点的记录,本文为第一篇,希望可以坚持下去吧~

原始数据类型

原始数据类型是指由语言预先定义,由保留关键字来声明,并且其值不与其他基本类型值共享状态。在 Java 中,一共 8 种原始数据类型:

  • byte:8 位带符号的二进制补码整数, 最小值为-128,最大值为 127,作为成员变量的默认初始化值为 0。

  • short:16 位带符号的二进制补码整数。 最小值为 -2^16,最大值为 2^16-1,作为成员变量的默认初始化值为 0。

  • int:32 位带符号的二进制补码整数,其最小值为-2^31,最大值为2^31-1,在 Java SE 8和更高版本中,可以用来表示无符号的 32 位整数,其最小值位 0,最大值为 2^32-1,作为成员变量的默认初始化值为 0。

  • long:64 位二进制补码整数, 带符号的 long 的最小值为 -2^63,最大值为 2^63-1,在 Java SE 8和更高版本中,可以用来表示无符号的 64 位整数,其最小值为 0,最大值为 2^64-1,作为成员变量的默认初始化值为 0L。

  • float:单精度32位浮点数,作为成员变量的默认初始化值为 0.0f。

  • double:双精度64位浮点数,作为成员变量的默认初始化值为 0.0d。

  • boolean:只有 true 和 false 两种值,作为成员变量的默认初始化值为 false。

  • char:单个16位Unicode字符,最小值为 ‘\u0000’(或0),最大值为 ‘\uffff’(或 65535),作为成员变量的默认初始化值为 ‘\u0000’。

这里需要注意一下有符号数的范围问题,以 byte 为例,在计算机中用每个字节的最高位来表示符号位,正数为 0,负数为 1,因此按理 byte 应该最小是 1111_1111,即 -127,但这里它的事实上却是 -128,原因是在计算机中,负数使用补码来表示的,关于原码、反码、补码的概念,可以看之前的博客 聊一聊位运算)。

自动装箱和拆箱

从 Java 1.5 引入,用于将包装类和原始数据类型进行自动转换,例如在使用集合时经常遇到的情况:

1
2
3
List<Integer> list = new ArrayList<>();
list.add(1); // 自动装箱,即自动将原始数据类型转换为包装类
int index1 = list.get(0); //自动拆箱,即自动将对应的包装类转换为原始数据类型

在自动装箱的时候,编译器会自动调用包装类内部的 valueOf() 方法来将原始数据类型转换为为对应的包装类对象,在每个包装类中都会有一个对应的 XXXCache 类来实现缓存的功能,通过一个数组来缓存了一定范围的包装类对象,例如 java.lang.Integer.IntegerCache 中缓存了 [-128, 127] 的 Integer 对象,这也就是为什么会有以下现象的原因:

1
2
3
4
5
6
Integer a = 127; //从缓存中返回 Integer 对象
Integer b = 127; //从缓存中返回 Integer 对象
Integer c = 128; // 调用 new Integer(i) 生成新的 Integer 对象
Integer d = 128; // 调用 new Integer(i) 生成新的 Integer 对象
System.out.println(a == b); // true
System.out.println(c == d); // false

需要注意使用包装类对象在未初始化的情况下自动拆箱导致空指针异常:

1
2
private static Integer i;
if(i == 0) return; // 空指针

参考

Java Tutorials - Primitive Data Types

Java中,为什么byte类型的取值范围为-128~127?

Why is the range of bytes -128 to 127 in Java?

Java中的自动装箱与拆箱