概述
Java中有8种基本数据类型,这些基本类型都有对应的包装类。
为什么
因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类种,我们是无法将int、double等类型放进去的。因为集合的容器要求元素是Object类型。
为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
区别
- 默认值不同,基本类型的默认值为0,false或\u0000等,包装类默认为null
- 初始化方式不同,一个需要new,一个不需要
- 存储方式不同,基本类型保存在栈上,包装类对象保存在堆上,成员变量的话,在不考虑jit优化的栈上分配时,都是随着对象一起保存在堆上的
自动拆装箱
包装类是对基本类型的包装,所以把基本数据类型转换成包装类的过程就是装箱;反之,把包装类转换成基本数据类型的过程就是拆箱。
在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。
如:int的自动装箱都是通过Integer.valueOf()方法来实现的,Integer的自动拆箱都是通过Integer.intValue()来实现的。
自动拆装箱的场景
场景一:将基本数据类型放入集合类
Java集合类只能接收对象类型,那么以下代码为什么会不报错呢?
List<Integer>li new ArrayList<>();
for (int i=1;i<50;i++{
1i.add(i);
}
将上面代码反编译,得到以下代码:
List<Integer>li new ArrayList<>();
for (int i=1;i<50;i++{
1i.add(Integer.valueOf(i));
}
所以当我们把基恩数据类型放入集合类重,会进行自动装箱
场景二:包装类型和基本数据类型的大小比较
有没有人想过,当我们怼Integer对象与基本类型进行大小比较的时候,实际上比较的是什么内容呢?看以下代码:
Integer a=1;
System.out.println(a==1?"等于":"不等于");// 等于
Boolean bool=false;
System.out.print1n(bool?"真":"假");// 真
反编译之后:
Integer a=1;
System.out.println(a.intValue()==1?"等于":"不等于");// 等于
Boolean bool=false;
System.out.print1n(bool.booleanValue?"真":"假");// 真
可以看到,包装类和基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型再进行比较的
场景三:包装类型的运算
对Integer进行四则运算
Integer i 10;
Integer j=20;
System.out.println(i+j);
反编译之后
Integer i=Integer.valueOf(10);
Integer j=.valueOf(20);
System.out.println(i.intValue()+j.intValue());
可以发现,两个包装类型之间运算,会被自动拆箱成基本类型进行四则运算
场景四:三目运算符的使用
这是很多人不知道的一个场景,严重的可以造成线上生产事故,看一个简单的三目运算符的代码:
boolean flag=true; //设置成true,保证条件表达式的表达式二一定可以执行
boolean simpleBoolean=false; //定义一个基本数据类型的boolean变量
Boolean nullBoolean=null;//定义一个包装类对象类型的Boolean?变量,值为nul1
boolean x =f1ag?nullBoolean:simpleBoolean;//使用三目运算符并给x变量赋值
在最后一行,右边会发生自动拆箱,反编译后:
boolean flag=true; //设置成true,
boolean simpleBoolean=false;
Boolean nullBoolean=null;
boolean x =f1ag?nullBoolean.booleanValue():simpleBoolean;
这其实是三目运算符的语法规范。当第二、第三位操作数分别为基本类型和对象时,编译器会将其中的包装类对象就自动拆箱为基本类型进行操作。因此 null.booleanValue()会造成npe异常。
场景五:函数参数与返回值
上代码:
//自动拆箱
public int getNum1(Integer num){
return num;
}
//自动装箱
public Integer getNum2(int num){
return num;
}
自动拆装箱和缓存
Java SE的自动拆装箱还提供了一个和缓存有关的功能,我们先来看以下代码
public static void main(String...strings){
Integer integer1 = 3;
Integer integer2 = 3;
if (integer1 == integer2)
System.out.println("integer1 == integer2"); // 执行这段
else
System.out.println("integer1 != integer2);
Integer integer3 = 300;
Integer integer4 = 300;
if (integer3 == integer4)
System.out.println("integer3 == integer4");
else
System.out.println("integer3 != integer4"); // 执行这段
}
原因是Integer中的暖存机制导致,在Java5中,Integer引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。
Integer缓存区间 -128~+127, 在此区间会直接使用缓存中的对象,而不会重新创建对象
只适用于自动装箱,使用构造函数创建对象不适用,比如new
其中javadoc详细的说明了缓存支持-128到127之间的自动装箱支持,最大值127可以通过 -XX:AutoBoxCacheMax=size 修改。 在Java6中也可以通过java.lang.Integer.IntegerCache.high 设置最大值。