有了基本类型为什么还需要包装类

Posted by 汤键|兔子队列 on November 18, 2021 禁止转载
本文总共 2029 字 · 阅读全文大约需要 6 分钟

缘由

  • Java中有8种基本数据类型,这些基本类型又都有对应的包装类
  • 因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型
  • 比如,在集合类中,是无法将int、double等类型放进去的
  • 因为集合的容器要求元素是Object类型
  • 为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作

基本类型和包装类型的区别

  • 1-默认值不同,基本类型的默认值为0,false或\u0000等,包装类默认为null
  • 2-初始化方式不同,一个需要new,一个不需要
  • 3-存储方式不同,基本类型保存在栈上,包装类对象保存在堆上(通常情况下,在没有JIT优化栈上分配时)

如何理解自动拆装箱

  • 拆箱与装箱
  • 包装类是对基本类型的包装,所以,把基本数据类型转换成包装类的过程就是装箱
  • 反之,把包装类转换成基本数据类型的过程就是拆箱
  • 自动拆装箱
  • 在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能
  • 自动装箱:就是将基本数据类型自动转换成对应的包装类
  • 自动拆箱:就是将包装类自动转换成对应的基本数据类型
  • 自动拆装箱原理
  • 自动装箱都是通过包装类的valueOf()方法来实现的
  • 自动拆箱都是通过包装类对象的xxxValue()来实现的
  • 如:
  • int的自动装箱都是通过 Integer.valueOf() 方法来实现的
  • Integer的自动拆箱都是通过 integer.intValue() 来实现的

哪些地方会自动拆装箱

  • 了解过原理之后,在来看一下,什么情况下,Java会帮我们进行自动拆装箱
  • 前面提到的变量的初始化和赋值的场景就不介绍了,那是最简单的也最容易理解的
  • 这里主要来看一下,那些可能被忽略的场景
  • 场景一、将基本数据类型放入集合类
  • Java中的集合类只能接收对象类型,那么以下代码为什么会不报错呢?
  • 将上面代码进行反编译,可以得到以下代码:
  • 通过以上可以得出结论,当把基本数据类型放入集合类中的时候,会进行自动装箱
  • 场景二、包装类型和基本类型的大小比较
  • 当对Integer对象与基本类型进行大小比较的时候,实际上比较的是什么内容呢?看以下代码:
  • 对以上代码进行反编译,得到以下代码:
  • 可以看到,包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的
  • 场景三、包装类型的运算
  • 当对Integer对象进行四则运算的时候,是如何进行的呢?看以下代码:
  • 反编译后代码如下:
  • 可以发现,两个包装类型之间的运算,会被自动拆箱成基本类型进行
  • 场景四、三目运算符的使用
  • 看一个简单的三目运算符的代码:
  • 很多人不知道,其实在 int k = flag ? i : j;
  • 这一行,会发生自动拆箱
  • 反编译后代码如下:
  • 这其实是三目运算符的语法规范
  • 当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作
  • 因为例子中,flag ? i : j;
  • 片段中,第二段的i是一个包装类型的对象,而第三段的j是一个基本类型,所以会对包装类进行自动拆箱
  • 如果这个时候i的值为 null ,那么就会发生NPE([自动拆箱导致空指针异常])
  • 场景五、函数参数与返回值
  • 这个比较容易理解,直接上代码了:

自动拆装箱与缓存

  • Java SE的自动拆装箱还提供了一个和缓存有关的功能,先来看一下代码,猜测一下输出结果:
  • 普遍认为上面的两个判断的结果都是false
  • 虽然比较的值是相等的,但是由于比较的是对象,而对象的引用不一样,所以会认为两个if判断都是false的
  • 在Java中,==比较的是对象引用,而equals比较的是值
  • 所以,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都将返回false
  • 奇怪的是,这里两个类似的if条件判断返回不同的布尔值
  • 上面这段代码真正的输出结果:
  • 原因就和Integer中的缓存机制有关
  • 在Java 5中,在Integer的操作上引入了一个新功能来节省内存和提高性能
  • 整型对象通过使用相同的对象引用实现了缓存和重用
  • 我们只需要知道,当需要进行自动装箱时,如果数字在-128至127之间时,会直接使用缓存中的对象,而不是重新创建一个对象
  • 其中的javadoc详细的说明了缓存支持-128到127之间的自动装箱过程
  • 最大值127可以通过 -XX:AutoBoxCacheMax=size 修改
  • 实际上这个功能在Java 5中引入的时候,范围是固定的-128 至 +127
  • 后来在Java 6中,可以通过 java.lang.Integer.IntegerCache.high 设置最大值
  • 这使我们可以根据应用程序的实际情况灵活地调整来提高性能
  • 到底是什么原因选择这个-128到127范围呢?
  • 因为这个范围的数字是最被广泛使用的
  • 在程序中,第一次使用Integer的时候也需要一定的额外时间来初始化这个缓存
  • 在Boxing Conversion部分的Java语言规范(JLS)规定如下:
  • 如果一个变量p的值是:
  • 范围内时,将p包装成a和b两个对象时,可以直接使用a==b判断a和b的值是否相等