自动拆装箱
基本数据类型
基本类型,又可以称为内置类型,在Java
中,基本类型是不同于类(Class)的特殊类型,也是我们在编程过程中使用最频繁的类型。
关于基本数据类型的详细介绍:基础(二):基本数据类型
基本类型的优点
在Java
中,我们可以使用new
关键字创建新对象,新创建的对象存储在堆(heap)中,我们通过栈(stack)中的引用来使用对应的对象。所以,就对象本身来说是非常消耗资源的。基本类型是我们在编程过程中使用最为频繁的类型,比如int
、boolean
等,如果我们每次在使用的时候都通过new关键字去创建新的对象,就会显得格外的笨重。所以基本数据类型也就应运而生,这种数据的变量不需要通过new
关键字进行创建,它们不会在堆(heap)上进行创建,而是直接在栈
(stack)内存中存储,所以会更加的高效
。
包装类型
Java
中的基本类型并不是面向对象的,所以在实际使用的过程中非常的不方便,为了解决这个问题,Java
为每个基本数据类型都提供了对应的类进行表示,这样与基本数据类型相对应的类称为包装类
(Wrapper Class)。
所有的包装类都在java.lang
包中,基本类型与包装类的对应关系如下:
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
基本类型所对应的包装类不需要死记硬背,从表中我们可以看到,除了int、char之外的其他基本数据类型的包装类名都是其首字母大写,所以我们只需要记住char和int这两个特殊的即可,即Character,Integer
为什么需要包装类
前面的基本类型的优点中提到,Java中为了提高效率提供了八种基本数据类型,那为什么还需要包装类呢?上文中也提到由于基本数据类型不是面向对象的,所以在实际的使用过程中非常的不方便,比如我们在使用集合的时候,我们无法将基本数据类型的数据存放进集合的,因为集合容器要求元素必须是Object类型的。
为了让基本数据类型具有对象的特征,就出现了包装类,顾名思义,包装类即是把基本数据类型"包装"起来,使其具有对象的性质及特征,并且为其添加相应的属性和方法,便利了基本类型的操作。
拆箱与装箱
既然基本数据类型与包装类之间相互对应,那么它们之间也是可以相互转换的。
把基本数据类型转换为包装类的过程就是打包装,英语对应为boxing
,翻译成中文为装箱
把包装类转换为基本数据类型的过程就是拆包装,英语对应为unboxing
,翻译成中文为拆箱
在Java1.5
之前我们可以使用以下代码进行装箱操作:
Integer i = new Integer(10);
自动拆箱与自动装箱
自Java1.5
版本中,Java
提供了自动拆箱和自动装箱的功能。这里的自动即是需要我们手动进行操作的步骤可以省略掉,Java
程序会自动完成这些操作。
Integer i = 10; // 自动装箱
int a = i; // 自动拆箱
实现原理
/**
* 自动装箱都是通过包装类的valueOf()方法来实现的
* 自动拆箱都是通过包装类对象的xxxValue()方法实现的
*/
public class AutoBoxing {
public static void main(String[] args) {
Integer integer = 10; // 自动装箱, Integer.valueOf(10)
int i = integer; // 自动拆箱, integer.intValue()
}
}
使用场景
场景一:将基本类型放入集合
public class AutoBoxingTest {
public static void main(String[] args) {
/*
* 不会报错,这里会进行自动装箱,
* 即list.add(Integer.valueOf(i))
*/
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
}
}
场景二:基本类型与包装类比较
/**
* 包装类与基本类型进行比较时,都是先将包装类进行拆箱成基本数据类型,然后才进行比较的
*/
public class AutoBoxingTest {
public static void main(String[] args) {
Integer i = 1;
// i.intValue() == 1
System.out.println(i == 1 ? "equal" : "not equal");
Boolean bool = Boolean.TRUE;
// bool.booleanValue()
System.out.println(bool ? "true" : "false");
}
}
场景三:包装类型的运算
public class AutoBoxingTest {
public static void main(String[] args) {
/*
* 两个包装类之间的运算都会被自动拆箱为基本类型进行
* Integer i = Integer.valueOf(10);
* Integer j = Integer.valueOf(20);
*
* i + j => i.intValue() + j.intValue();
*/
Integer i = 10, j = 20;
System.out.println(i + j);
}
}
场景四:三目运算符的使用
public class AutoBoxingTest {
public static void main(String[] args) {
/*
* Integer i = Integer.valueOf(0);
* 当三目运算符的操作数为包装类对象和基本数据类型时,
* 其中的对象就会进行自动拆箱成基本数据类型进行操作
*
* 如果i的值为null时,就会发生NPE
*/
boolean flag = true;
Integer i = 0;
int j = 1;
int k = flag ? i : j; // flag ? i.intValue() : j
}
}
场景五:函数参数与返回值
public class AutoBoxingTest {
/**
* 自动拆箱
*
* @param num 整型包装类对象
* @return 整型基本类型字面量
*/
public int getNum1(Integer num) {
return num;
}
/**
* 自动装箱
*
* @param num 整型基本类型字面量
* @return 整型包装类对象
*/
public Integer getNum2(int num) {
return num;
}
}
自动拆装箱与缓存
public class AutoBoxingCache {
public static void main(String[] args) {
/*
* 在Java中 == 比较的是对象的引用,而equals比较的才是值
* 按照常规套路两个比较的输出都应该是 false,但是由于Java 5
* 在Integer的操作上引入了一个新功能来节省内存和提高性能
* 整型对象通过使用相同的对象引用实现了缓存和重用
*
* 注意:
* 1、适用于整数值范围:-128 ~ 127
* 2、只适用于自动装箱,使用构造函数创建的对象不适用
*/
Integer integer1 = 10, integer2 = 10;
System.out.println(integer1 == integer2 ? "true" : "false"); // true
Integer integer3 = 200, integer4 = 200;
System.out.println(integer3 == integer4 ? "true" : "false"); // false
}
}
自动拆装箱带来的问题
- 包装对象进行数值比较时,不能简单的使用
==
,虽然-128到127之间的数字可以,但是这个范围之外的数值还是需要使用equals
比较 - 有些场景中都会进行自动拆装箱,当包装类对象为
null
时,那么自动拆箱时会抛出NPE(空指针异常)
- 如果在循环中有大量的拆装箱操作,会浪费很多的资源