什么是自动装箱拆箱?
很简单,下面两句代码就可以看到装箱和拆箱过程
//自动装箱
Integer total
=
99
;
//自动拆箱
int totalprim
= total
;
简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
下面我们来看看需要装箱拆箱的类型有哪些:


这个过程是自动执行的,那么我们需要看看它的执行过程:
反编译 class 文件之后得到如下内容:
1 javap -c StringTest
//java学习交流:737251827 进入可领取学习资源及对十年开发经验大佬提问,免费解答!
Integer total = 99;
public
class
Main
{
public
static
void
main
(
String
[
] args
)
{
//自动装箱
Integer total
=
99
;
//自定拆箱
int totalprim
= total
;
}
}
private final int value
;
public
Integer
(
int value
)
{
this
.value
= value
;
}
public
Integer
(String string
) throws NumberFormatException
{
this
(
parseInt
(string
)
)
;
}
private static final Integer[] SMALL_VALUES = new Integer[256];
它是一个静态的 Integer 数组对象,也就是说最终 valueOf 返回的都是一个 Integer 对象。
所以我们这里可以总结一点:装箱的过程会创建对应的对象,这个会消耗内存,所以装箱的过程会增加内存的消耗,影响性能。
2、接着看看 intValue 函数
@Override
public int
intValue
(
)
{
return value
;
}
这个很简单,直接返回value值即可。
相关问题
上面我们看到在 Integer 的构造函数中,它分两种情况:
i
>=
128
|| i
<
-
128
===
==
>
new
Integer
(i
)
i
<
128
&& i
>=
-
128
===
==
>
SMALL_VALUES
[i
+
128
]
private static final Integer[] SMALL_VALUES = new Integer[256];
SMALL_VALUES本来已经被创建好,也就是说在i >= 128 || i < -128是会创建不同的对象,在i < 128 && i >= -128会根据i的值返回已经创建好的指定的对象。
public
class
Main
{
public
static
void
main
(
String
[
] args
)
{
Integer i1
=
100
;
Integer i2
=
100
;
Integer i3
=
200
;
Integer i4
=
200
;
System
.out
.
println
(i1
==i2
)
;
//true
System
.out
.
println
(i3
==i4
)
;
//false
}
}
public
class
Main
{
public
static
void
main
(
String
[
] args
)
{
Double i1
=
100.0
;
Double i2
=
100.0
;
Double i3
=
200.0
;
Double i4
=
200.0
;
System
.out
.
println
(i1
==i2
)
;
//false
System
.out
.
println
(i3
==i4
)
;
//false
}
}
,这样也不必奇怪,因为它们的 valueOf 实现不一样,结果肯定不一样,那为什么它们不统一一下呢?最新面试题整理好了,点击 小程序在线刷题。
这个很好理解,因为对于 Integer,在(-128,128]之间只有固定的 256 个值,所以为了避免多次创建对象,我们事先就创建好一个大小为 256 的 Integer 数组SMALL_VALUES,所以如果值在这个范围内,就可以直接返回我们事先创建好的对象就可以了。
但是对于 Double 类型来说,我们就不能这样做,因为它在这个范围内个数是无限的。总结一句就是:在某个范围内的整型数值的个数是有限的,而浮点数却不是。
所以在 Double 里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。
public
static Double
valueOf
(
double d
)
{
return
new
Double
(d
)
;
}
下面我们进行一个归类:
Integer 派别:Integer、Short、Byte、Character、Long 这几个类的 valueOf 方法的实现是类似的。
Double 派别:Double、Float 的 valueOf 方法的实现是类似的。每次都返回不同的对象。
下面对 Integer 派别进行一个总结,如下图:

下面我们来看看另外一种情况:
public
class
Main
{
public
static
void
main
(
String
[
] args
)
{
Boolean i1
=
false
;
Boolean i2
=
false
;
Boolean i3
=
true
;
Boolean i4
=
true
;
System
.out
.
println
(i1
==i2
)
;
//true
System
.out
.
println
(i3
==i4
)
;
//true
}
}
public
static Boolean
valueOf
(
boolean b
)
{
return b
? Boolean
.
TRUE
: Boolean
.
FALSE
;
}
public
static final Boolean
TRUE
=
new
Boolean
(
true
)
;
public
static final Boolean
FALSE
=
new
Boolean
(
false
)
;
Integer num1
=
400
;
int num2
=
400
;
System
.out
.
println
(num1
== num2
)
;
//true
// 说明num1 == num2进行了拆箱操作
Integer num1
=
100
;
int num2
=
100
;
System
.out
.
println
(num1
.
equals
(num2
)
)
;
//true
@Override
public boolean
equals
(
Object o
)
{
return
(o
instanceof
Integer
)
&&
(
(
(Integer
) o
)
.value
== value
)
;
}
Integer num1
=
100
;
int num2
=
100
;
Long num3
=
200l
;
System
.out
.
println
(num1
+ num2
)
;
//200
System
.out
.
println
(num3
==
(num1
+ num2
)
)
;
//true
System
.out
.
println
(num3
.
equals
(num1
+ num2
)
)
;
//false
@Override
public boolean
equals
(
Object o
)
{
return
(o
instanceof
Long
)
&&
(
(
(Long
) o
)
.value
== value
)
;
}
Integer num1
=
100
;
Ingeger num2
=
200
;
Long num3
=
300l
;
System
.out
.
println
(num3
==
(num1
+ num2
)
)
;
//true
//java学习交流:737251827 进入可领取学习资源及对十年开发经验大佬提问,免费解答!
可以看到运算的时候首先对 num3 进行拆箱(执行 num3 的 longValue 得到基础类型为 long 的值 300),然后对num1 和 mum2 进行拆箱(分别执行了 num1 和 num2 的 intValue 得到基础类型为 int 的值100和200),然后进行相关的基础运算。
int num1
=
100
; int num2
=
200
;
long mum3
=
300
;
System
.out
.
println
(num3
==
(num1
+ num2
)
)
;
//true
就说明了为什么最上面会返回 true.最新面试题整理好了,点击 小程序在线刷题。
所以,当
== 运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。
陷阱1:
Integer integer100
=
null
;
int int100
=integer100
;
这两行代码是完全合法的,完全能够通过编译的,但是在运行时,就会抛出空指针异常。其中,integer100 为Integer 类型的对象,它当然可以指向 null。但在第二行时,就会对 integer100 进行拆箱,也就是对一个 null 对象执行 intValue() 方法,当然会抛出空指针异常。所以,有拆箱操作时一定要特别注意封装类对象是否为 null。
总结
1、需要知道什么时候会引发装箱和拆箱
2、装箱操作会创建对象,频繁的装箱操作会消耗许多内存,影响性能,所以可以避免装箱的时候应该尽量避免。
3、equals(Object o) 因为原equals方法中的参数类型是封装类型,所传入的参数类型(a)是原始数据类型,所以会自动对其装箱,反之,会对其进行拆箱
4、当两种不同类型用 == 比较时,包装器类的需要拆箱, 当同种类型用==比较时,会自动拆箱或者装箱