- 装箱:将基本数据类型转换为包装器类型
- 拆箱:将包装器类型转换为基本数据类型
装箱与拆箱的过程时自动进行的,因此称为“自动装箱”、“自动拆箱”,属于编译期的语法糖。
以基本类型int与包装类型Integer为例讨论。既然是编译期的语法糖,那么直接分析编译出来的字节码即可,可以使用jad工具(估计内部封装了javap一类的工具)。
装箱的实现
|
|
jad解析class文件,发现根据字节码反编译为Integer.valueOf(int)
方法:
|
|
这是一个静态工厂方法,内部最终调用了Integer类的构造器Integer.<init>(init)
:
|
|
特别的,Integer类默认缓存-128~127
的包装对象(上界可通过java.lang.Integer.IntegerCache.high
参数配置,最小127)。因此,缓存范围内的包装对象来自缓存的常量池,相同value包装对象的引用相等;缓存范围外的包装对象的引用均不相等。
拆箱的实现
|
|
拆箱的时候编译为Integer.intValue()
方法:
|
|
该方法直接返回内部值Integer#value
:
|
|
自动装箱与自动拆箱的陷阱
陷阱主要出现在包装类型与基本类型混用的场景中。
以“==
”为例:
- 如果两个操作数都是基本类型,则使用基本类型的比较。
- 如果一个操作数是基本类型,另一个是包装类型,则拆箱为基本类型再比较:如果包装对象为null,抛出NullPointerException。(想想看,如果装箱为包装类型,显然无法发现null)
- 如果两个操作数都是包装类型,则比较二者的引用是否相等:如果二者内部值相等,但在缓存范围外,则必然不等。
因此,如果要比较包装类型是否相等,最好显示的使用equals方法。
另外,如果是算术运算、除“==
”外的逻辑运算、位运算等,则统一拆箱为基本类型再运算;有抛出NullPointerException的危险。