前言
自动拆装箱听起来挺高级的,其实就是基本数据类型和对应包装类型的相互装换的过程。说白了就是为了提高效率、简化开发;至于怎么提高效率,开发过程中有哪些点需要注意的,面试又常问哪些点;我们继续往后看。
基本数据类型和包装类型对应关系
我们知道Java有8种基本数据类型,他们对应的包装类型都是怎么样的呢?下面用一个表格总结一下:
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
在这八个类名中,除了Integer和Character类以后,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写即可。
为什么要搞一个包装类?
因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型(对象)。
所以为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
自动装箱与自动拆箱
- 装箱:基本数据类型装换为包装类型:称之为装箱(可以理解为对基本类型包装了一下,使之具有对象的性质);
- 拆箱:包装类型装为基本类型:称之为拆箱;
那么自动拆箱和自动装箱,就是为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。话不多说,直接上代码:
public static void main(String[]args){
Integer i = 200; //自动装箱 就等同于:Integer i = new Integer(10);不用我们开发人员再new一次
int b = i; //自动拆箱 又转为了基本数据类型了
}
自动装箱与自动拆箱底层如何实现的呢?
我们直接上上面代码反编译后的代码:
public static void main(String[]args){
Integer i = Integer.valueOf(10);
int b = i.intValue();
}
总结如下:
- 自动装箱:调的对应包装类型的:valueOf()方法;
- 自动拆箱:调的XXValue()方法;
自动拆装箱的实际应用场景有哪些呢?
场景一:包装类型和基本类型的大小比较时
当我们对Integer对象与基本类型进行大小比较的时候,实际上比较的是什么内容呢?看以下代码:
Integer a = 1;
System.out.println(a==1?"等于":"不等于"); //这里一比,实际上会将a自动拆箱再来比较
Boolean bool=false;
System.out.println(bool?"真":"假"); //这里一比,实际上会将bool自动拆箱再来比较
上反编译的代码:
Integer a=1;
System.out.println(a.intValue()==1?"等于":"不等于"); //和上面说的一致,调XXValue()进行拆箱
Boolean bool=false;
System.out.println(bool.booleanValue?"真":"假"); //和上面说的一致,调XXValue()进行拆箱
场景二:往集合里面赋值时
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i ++){
//这里为什么不会报错呢?不是说只能存对象吗?因为自动装箱了:反编译后就是这样的:li.add(Integer.valueOf(i));
li.add(i);
}
以上,结论就是:当我们把基本数据类型放入集合类中的时候,会进行自动装箱
场景三:作为方法返回值的时候
//自动拆箱
public int getNum1(Integer num) {
return num;
}
//自动装箱
public Integer getNum2(int num) {
return num;
}
场景四:参与运算的时候
//自动装箱
Integer i = 10;
Integer j = 20;
//自动拆箱为基本数据类型,再比较
System.out.println(i+j);
反编译后:
Integer i = Integer.valueOf(10);
Integer j = Integer.valueOf(20);
System.out.println(i.intValue() + j.intValue());
场景五:三目运算符
直接上经典案例:
boolean flag = true;
Integer i = 0;
int j = 1;
int k = flag ? i : j;
简单理解就是:如果flag为true则返回i 否则返回j;那么问题来了,假如这样写呢?
boolean flag = true;
//可以理解为调后台逻辑查询一个数值,可能为null,所以我们直接测试null的场景
//Integer i = getMoney();
Integer i = null;
int j = 1;
int k = flag ? i : j;
乍一看也没啥,最后返回不也是null吗?实际上最后一行三目运算时,i会自动拆箱为基本数据类型:反编译后
int k = flag ? i.intValue() : j;
那么就会出大问题,i为null,就会报空指针异常了;自动拆箱导致空指针异常
自动拆装箱与缓存
-
我们先看一个场景:
public static void main(String[] args) { Integer a1 = 10; Integer a2 = 10; //true System.out.println(a1==a2); Integer b1 = 200; Integer b2 = 200; //false System.out.println(b1==b2); }
-
为什么会这样呢?我们常规理解:==比地址,他们都赋值给了两个不同的对象,为什么一个相等,一个不相等呢?
-
我们引入阿里编码规范提示的一段话来解释即可:
简单的说:为了提高效率:-128-127被定义成了一个数组,被放到缓存中;因为这256个数经常使用,为了避免重复创建;
为什么double不维护这样一个数据? 。。。范围太大,无法维护
-
这里要注意一个点:我们知道自动装箱就是:调用valueOf方法;他和new Integer()是有区别的:
- valueOf()会从缓存池里取出已有对象;
- new Integer()每次都是创建一个新对象;
上代码就知道了:
public static void main(String[] args) { Integer a1 = 10; Integer a2 = 10; Integer integer1 = new Integer(10); Integer integer2 = new Integer(10); //true 底层a1 a2 都是调用的valueOf,优先从缓存池拿,所以是相等的 System.out.println(a1==a2); //false !!! new Integer每次都是新创建一个对象 System.out.println(integer1==integer2); }
开发注意事项
- 包装对象的数值比较,不能简单的使用==,虽然-128到127之间的数字可以,但是这个范围之外还是需要使用equals比较。而且阿里编码规范插件也提示:包装类型比较要使用equals;
- 三目运算的时候要注意:是否自动拆箱导致空指针异常;