对象包装器
每个基本类型都有一个对应的类,称作包装器wrapper。
/* extends Number */
// Interger <-> int
// Long <-> long
// Float <-> float
// double <-> double
// Short <-> short
// Byte <-> byte
/* not extends Number */
// Character <-> character
// Void <-> void
// Boolean <-> boolean
对象包装器是不可变的:一旦构造了包装器,就不允许更改包装在其中的值。
对象包装器是final
的:不能定义对象包装器的子类。
自动装箱(auto boxing)
声明一个Integer
对象的数组列表,并添加一个值为3的Integer
对象。
// 由于值包装在对象中,ArrayList<Integer>的效率远低于int[]数组。
ArrayList<Integer> list = new ArrayList<>();
// 等价于list.add(Integer.valueOf(3));
list.add(3);
相反地,将Integer
对象赋值给int
变量时,会自动拆箱。
// 编译器将以下语句翻译成int n = list.get(i).inValue();
int n = list.get(i);
在算术表达式中也能够自动地装箱和拆箱。
Integer n = 3;
// 编译器会自动地插入一条对象拆箱指令,在自增计算执行后,执行装箱指令。
n++;
包装器的比较
对象包装器的相等性与基本类型是不同的:
Integer a = 1000;
Integer b = 1000;
// ==检测a和b是否指向同一个存储区域,结果是false
System.out.println(a == b);
// equals方法比较a和b包装的值,结果是true
System.out.println(a.equals(b));
自动装箱对基本类型的值的范围有规范要求:boolean
、byte
、char
≤127,介于-128~127之间的short
和int
被包装在固定的对象中,即指向相同的存储区域。
Integer a = 100;
Integer b = 100;
// 此时a和b指向同一个存储区域,结果是true
System.out.println(a == b);
引用null异常
包装器引用null时,自动装箱可能会抛出NullPointerException
异常:
Integer n = null;
// 由于n引用null,执行2 * n时会将n自动拆箱取值,抛出NullPointerException异常。
System.out.println(2 * n);
自动升级
再一个条件表达式中混合使用Integer
和Double
时,Integer
值会拆箱提升为double
,再装箱为Double
:
Integer n = 1;
Double x = 2.0;
// n升级为Double,结果是1.0
System.out.println(true ? n : x);
注:装箱和拆箱是编译器认可的,而不是虚拟机。编译器再生成类的字节码时,会插入必要的方法调用。