Java自动装箱/拆箱
什么是装箱/拆箱
在Java中一共有四类八种基本数据类型,在JDK1.5中,给这四类八种基本数据类型加入了包装类,对应如下:
| 基本类型 | 包装类型 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| boolean | Boolean |
| char | Character |
在很多时候我们需要在基本数据类型和其对应的包装类之间相互转换,所以从JavaSE5开始引入了自动装箱/拆箱。将一个int变量转换成Integer对象,这个过程叫做装箱;反之将Integer对象转换成int类型值,这个过程叫拆箱。以上装箱/拆箱是在编译class文件的时候自动完成的,因此叫做自动装箱/拆箱。下面是一个小例子:
1 | public class Main { |
反编译之后的class文件如下:
1 | public class Main { |
从上面的例子可以看到,当我们把一个值为100的int型变量赋给Integer对象时,Java编译器自动对int进行装箱,也就是加上了valueOf方法。而当我们把Integer对象赋给int变量时,Java编译器自动对Integer对象进行拆箱,也就是intValue方法。八个包装类装箱所用到的方法都是valueOf方法,拆箱方法都是形如“变量名+Value”,如intValue,longValue。
什么时候触发装箱/拆箱
- 赋值。将包装类对象赋值给基本数据类型或者反之,都会触发自动装箱拆箱。
- 运算符。在表达式中含有运算符时会触发装箱拆箱操作。
- 方法参数传递。将基本数据类型作为形参,而方法需要相应的包装类型,这时会触发装箱,反之亦然。
下面是一个小例子:
1 | public class Main { |
反编译之后的代码:
1 | public class Main { |
上面的例子很清晰的展示了Java编译器会在什么情况下进行自动装箱拆箱。在此就不赘述了。
需要注意的点
在使用==操作符判断两个包装类对象的值是否相等的时候实际上是在比较两个对象是否是同一个对象,而不是两个对象的值是否相等。只有==操作符两边有一个基本类型变量时才会触发拆箱。
装箱/拆箱方法实现
拆箱方法很简单,以Integer为例,下面是Integer类的intValue方法实现:
1 | public int intValue() { |
其他七个包装类同理。
装箱方法同样以Integer为例:
1 | public static Integer valueOf(int i) { |
上面的IntegerCache类是Integer的私有静态内部类,里面维护了一个Integer对象数组来实现缓存支持。其中最小值low为-128,最大值high默认为127,可以通过JVM启动参数修改。也就是说int值在-128到127之间的会返回缓存数组里的Integer对象引用,超出缓存范围的新建Integer对象。
在Java中,Integer、Short、Byte、Long、Character这几个类的valueOf方法的实现是类似的,只有Integer类可以通过参数改变缓存范围,Byte、Short、Long的固定范围是[-128, 127], Character的范围是[0, 127]。Double和Float的valueOf方法的实现是类似的,浮点型包装类没有缓存机制。而对于Boolean类,它的实现中定义了2个静态成员属性,所以不会新建Boolean对象。