一、前言
提到自动装箱与自动拆箱就要先说对象包装器。
对象包装器
Java 有八种基本类型,可以简单地进行数据处理。但是由于 Java 是面向对象语言,而八种基本数据类型不支持面向对象编程,比如使用集合时,是无法将 int 这种基本类型放进去的,为了方便开发,Java 针对每个基本类型设计了对应的对象包装器(wrapper)。
基本数据类型 | 对象包装器 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
我们要注意:
- 一旦构造了包装器,不允许更改包装器中的值。
- 对象包装器类不能定义子类。
可以简单看一下 Integer 源码找一下原因:
public final class Integer extends Number implements Comparable<Integer> {
可以看到 Integer 类使用 final 修饰,所以不能被继承,也就不能有子类。
我们继续看 Integer 的构造器:
public Integer(int value) {
this.value = value;
}
看到包装器中的值使用 value 存储的,我们再看一下 value 的定义:
private final int value;
可以看到 value 是使用 final 修饰的,所以包装器类一旦创建对象,里面的值不能改变。
二、自动装箱与自动拆箱
基本类型比如 int 自动的转化为包装器类型比如 Integer 称为自动装箱(autoboxing)。
相反的,将包装器类型比如 Integer 自动转化为 int 类型,称为自动拆箱(autounboxing)。
那么什么时候会发生自动拆箱与自动装箱呢?
自动装箱与自动装箱何时发生
将一个基本类型赋值给对象包装器对象,就会发生自动装箱。比如:
List<Integer> list = new ArrayList<>();
// 此处发生自动装箱:相当于 list.add(Integer.valueOf(3))
list.add(3);
将一个包装器对象赋值给基本类型,就会发生自动拆箱。比如:
List<Integer> list = new ArrayList<>();
// 此处发生自动装箱:相当于 list.add(Integer.valueOf(3))
list.add(3);
// 此处发生自动拆箱:相当于 list.get(0).intValue();
list.get(0);
算术表达式也可以发生自动装箱与自动拆箱操作,比如自增与自减操作:
Integer n = 3;
// n 先自动拆箱,做完减法后,自动装箱
n--;
如果在一个条件表达式中混合使用 Integer 和 Double 类型,Integer 值就会自动拆箱,转化为 double,再自动装箱提升为 Double 值。
Integer a = 1;
Double b = 2.0;
// 1.0
System.out.println(true? a : b);
三、对象包装器对象之间的比较
由于自动装箱和自动拆箱,使我们觉得对象包装器对象之间的比较,好像使用 == 运算符也可以,== 运算符运用在对象之间,实际是比较对象是否指向同一个内存区域。那么我们猜测一下,下面代码运行的结果是多少:
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2);
Integer i3 = 300;
Integer i4 = 300;
System.out.println(i3 == i4);
Integer i5 = new Integer(100);
Integer i6 = new Integer(100);
System.out.println(i6 == i5);
程序的运行结果为:
true
false
false
那么为什么是这样的结果呢?
自动装箱规范要求 boolean、byte、char <= 127,介于 -128 ~ 127 之间的 short 和 int 被包装到固定的对象中。(此句话出自 Java 核心技术卷 1 的 185 页)
所以 i1 和 i2 其实指向的是一个对象,所以为 true。i3 与 i4 指向的是两个对象,所以为 false。而 i5 和 i6 不是自动装箱,属于手动装箱,所以不遵守自动装箱规范,也是两个对象,所以为 false。
所以还是推荐使用 equals() 来对对象包装器对象进行比较,equals() 在 Integer 中被重写,作用是比较包装器对象中的值,我们可以看一下源码:
public boolean equals(Object obj) {
// 当前对象与 obj 是否为同一类型,不是同一类型无法比较,返回 false
if (obj instanceof Integer) {
// obj 强转为 Integer 后进行手动拆箱转化为 int 值,与 value(前面分析过了,value 是包装器对象实际存储值的地方)进行比较
return value == ((Integer)obj).intValue();
}
return false;
}
所以只要对象包装器对象中的值相同,则 equals()
四、Integer 类常用 API
方法 | 作用 |
---|---|
int intValue() | 以 int 的形式返回 integer 对象的值 |
static String toString(int i, int radix) | 以一个新的 String 对象形式返回给定数值 i 的十进制表示 |
static int parseInt(String s) | 返回字符串表示的整型数值,十进制数 |
static int parseInt(String s, int radix) | 返回字符串表示整型数值,radix 参数类型的数 |
static Integer valueOf(String s) | 返回用 s 表是的整型数值进行初始化的一个新 Integer 对象,十进制数 |
static Integer valueOf(String s, int radix) | 返回用 s 表是的整型数值进行初始化的一个新 Integer 对象,radix 参数进制数 |