Java中的装箱、拆箱

  • 装箱:将基本数据类型转换为包装器类型
  • 拆箱:将包装器类型转换为基本数据类型

装箱与拆箱的过程时自动进行的,因此称为“自动装箱”、“自动拆箱”,属于编译期的语法糖。

以基本类型int与包装类型Integer为例讨论。既然是编译期的语法糖,那么直接分析编译出来的字节码即可,可以使用jad工具(估计内部封装了javap一类的工具)。

装箱的实现

1
2
3
public static void main(String[] args) {
Integer boxing = 1;
}

jad解析class文件,发现根据字节码反编译为Integer.valueOf(int)方法:

1
2
3
4
5
6
7
public static void main(String args[]) {
Integer boxing = Integer.valueOf(1);
// 0 0:iconst_1
// 1 1:invokestatic #2 <Method Integer Integer.valueOf(int)>
// 2 4:astore_1
// 3 5:return
}

这是一个静态工厂方法,内部最终调用了Integer类的构造器Integer.<init>(init)

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);
}

特别的,Integer类默认缓存-128~127的包装对象(上界可通过java.lang.Integer.IntegerCache.high参数配置,最小127)。因此,缓存范围内的包装对象来自缓存的常量池,相同value包装对象的引用相等;缓存范围外的包装对象的引用均不相等。

拆箱的实现

1
2
3
public static void main(String[] args) {
int unboxing = new Integer("2");
}

拆箱的时候编译为Integer.intValue()方法:

1
2
3
4
5
6
7
8
9
10
public static void main(String args[]) {
int unboxing = (new Integer("2")).intValue();
// 0 0:new #2 <Class Integer>
// 1 3:dup
// 2 4:ldc1 #3 <String "2">
// 3 6:invokespecial #4 <Method void Integer(String)>
// 4 9:invokevirtual #5 <Method int Integer.intValue()>
// 5 12:istore_1
// 6 13:return
}

该方法直接返回内部值Integer#value:

1
2
3
public int intValue() {
return value;
}

自动装箱与自动拆箱的陷阱

陷阱主要出现在包装类型与基本类型混用的场景中。

以“==”为例:

  • 如果两个操作数都是基本类型,则使用基本类型的比较。
  • 如果一个操作数是基本类型,另一个是包装类型,则拆箱为基本类型再比较:如果包装对象为null,抛出NullPointerException。(想想看,如果装箱为包装类型,显然无法发现null)
  • 如果两个操作数都是包装类型,则比较二者的引用是否相等:如果二者内部值相等,但在缓存范围外,则必然不等。

因此,如果要比较包装类型是否相等,最好显示的使用equals方法。

另外,如果是算术运算、除“==”外的逻辑运算、位运算等,则统一拆箱为基本类型再运算;有抛出NullPointerException的危险。

扫描微信关注我
微信公众号二维码
本文链接:Java中的装箱、拆箱
作者:猴子007
出处:https://monkeysayhi.github.io
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名及链接。
我是猴子007,<br>一只非常特殊的动物,<br>可以从事程序的开发、维护,<br>经常因寻找香蕉或母猿而无心工作。