Java自动装箱/拆箱


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
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
Integer i3 = 100;
int i4 = i3;
System.out.println(i3 + " " + i4);
}
}

反编译之后的class文件如下:

1
2
3
4
5
6
7
8
9
10
public class Main {
public Main() {
}

public static void main(String[] args) {
Integer i3 = Integer.valueOf(100);
int i4 = i3.intValue();
System.out.println(i3 + " " + i4);
}
}

从上面的例子可以看到,当我们把一个值为100的int型变量赋给Integer对象时,Java编译器自动对int进行装箱,也就是加上了valueOf方法。而当我们把Integer对象赋给int变量时,Java编译器自动对Integer对象进行拆箱,也就是intValue方法。八个包装类装箱所用到的方法都是valueOf方法,拆箱方法都是形如“变量名+Value”,如intValue,longValue。

什么时候触发装箱/拆箱

  • 赋值。将包装类对象赋值给基本数据类型或者反之,都会触发自动装箱拆箱。
  • 运算符。在表达式中含有运算符时会触发装箱拆箱操作。
  • 方法参数传递。将基本数据类型作为形参,而方法需要相应的包装类型,这时会触发装箱,反之亦然。

下面是一个小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Main {
public static void main(String[] args) {
//赋值
Integer i1 = 100;
Integer ii = 1;
//算数运算符
i1 += ii;
i1 += 2;
//自增自减运算符
i1++;
i1--;
//关系运算符
boolean b = i1 > 10;
b = i1 == 10;
b = i1 > ii;
//位运算符
int i = i1 | ii;
i = i1 |10;
i = i1 << ii;
i = i1 << 2;
//方法参数传递
func(10, i1);
}
public static void func(Integer i1, int i2) {}
}

反编译之后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public Main() {
}

public static void main(String[] args) {
Integer i1 = Integer.valueOf(100);
Integer ii = Integer.valueOf(1);
i1 = Integer.valueOf(i1.intValue() + ii.intValue());
i1 = Integer.valueOf(i1.intValue() + 2);
i1 = Integer.valueOf(i1.intValue() + 1);
i1 = Integer.valueOf(i1.intValue() - 1);
boolean b = i1.intValue() > 10;
b = i1.intValue() == 10;
b = i1.intValue() > ii.intValue();
int i = i1.intValue() | ii.intValue();
i = i1.intValue() | 10;
i = i1.intValue() << ii.intValue();
i = i1.intValue() << 2;
func(Integer.valueOf(10), i1.intValue());
}
public static void func(Integer i1, int i2) {}
}

上面的例子很清晰的展示了Java编译器会在什么情况下进行自动装箱拆箱。在此就不赘述了。

需要注意的点

​ 在使用==操作符判断两个包装类对象的值是否相等的时候实际上是在比较两个对象是否是同一个对象,而不是两个对象的值是否相等。只有==操作符两边有一个基本类型变量时才会触发拆箱。

装箱/拆箱方法实现

​ 拆箱方法很简单,以Integer为例,下面是Integer类的intValue方法实现:

1
2
3
public int intValue() {
return value;//value是Integer类的一个私有常量,存放Integer对象的值
}

其他七个包装类同理。

​ 装箱方法同样以Integer为例:

1
2
3
4
5
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(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对象。


文章作者: Amos Liu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Amos Liu !
 上一篇
Java数组 Java数组
Java数组​ 在Java中,有大量的方式可以持有对象。而数组是一种效率最高的存储和随机访问对象引用序列的方式。数组是一个简单的线性序列,这使得元素访问非常快速。但在其生命周期中数组对象的大小被固定且不可改变。 ​ 数组有三种初
2017-04-09
下一篇 
算法练习1 算法练习1
算法练习1Add TwoNumbersYou are given two non-empty linked lists representing two non-negative integers. The digits are store
2017-04-01
  目录