Java中的自动拆装箱、基本类型的转换、包装类的缓存详解

 更新时间:2024年12月16日 08:41:30   作者:骑个小蜗牛  
文章详细介绍了Java中数据类型的拆装箱、自动拆箱和装箱,以及包装类的缓存机制,包括基本数据类型的容量大小、转换规则和自动类型转换等

数据类型的拆装箱

1. 拆箱、装箱

拆箱

把包装类转换为基本数据类型就是拆箱。拆箱通过包装类的xxValue方法实现。

装箱

把基本类型转换为包装类的过程就是装箱。装箱通过包装类的valueOf方法实现。

2. 自动拆箱

将包装类自动转化成对应的基本数据类型(通过包装类的xxValue方法)。

    public static void main(String[] args) {
        int int1 = new Integer(100);
        long long1 = new Long(100);
        double double1 = new Double(100.0);
    }

实际等于

    public static void main(String[] args) {
        int int1 = new Integer(100).intValue();
        long long1 = new Long(100).longValue();
        double double1 = new Double(100.0).doubleValue();
    }

3. 自动装箱

将基本数据类型自动转化为对应的包装类(通过包装类的valueOf方法)。

    public static void main(String[] args) {
        Integer int2 = 100;
        Long long2 = 100L;
        Double double2 = 100.0;
    }

实际等于

    public static void main(String[] args) {
        Integer int2 = Integer.valueOf(100);
        Long long2 = Long.valueOf(100L);
        Double double2 = Double.valueOf(100.0);
    }

4. 自动拆装箱使用场景

将基本类型放入集合类(自动装箱)

集合类只支持包装类型(不支持基本数据类型),但是我们add(基本数据类型)也不会报错,是因为Java给我们做了自动装箱。

实际等于

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(Integer.valueOf(1));
        list.add(Integer.valueOf(2));
    }

包装类型和基本类型比较大小(自动拆箱)

包装类与基本数据类型进行比较运算,其实是先将包装类进行拆箱成基本数据类型,然后比较。

    public static void main(String[] args) {
        boolean b = new Integer(2) > 1;
    }

实际等于

    public static void main(String[] args) {
        boolean b = new Integer(2).intValue() > 1;
    }

包装类型的运算(自动拆箱)

运算中包含包装类型时,会将包装类型自动拆箱为基本类型再进行运算

    public static void main(String[] args) {
        int a = new Integer(1) + 1;
    }

实际等于

    public static void main(String[] args) {
        int a = new Integer(1).intValue() + 1;
    }

三目运算(自动拆箱)

value = flag ? value1 : value2

三目运算中,如果value1和value2中一个是基本数据类型、另一个是包装类型。那么无论value是基本数据类型还是包装类型,只要根据判断结果从value1和value2选择的那个值是包装类型时,都会先把包装类型拆箱为基本数据类型,再根据value的类型来决定是否再对值进行拆封箱处理。

    public static void main(String[] args) {
        Integer a = null;
        int v1 = true ? 0 : a;// 正常
        int v2 = false ? 0 : a;// 报错
        Integer v3 = true ? 0 : a;// 正常
        Integer v4 = false ? 0 : a;// 报错
    }

实际等于

    public static void main(String[] args) {
        Integer a = null;
        int v1 = true ? 0 : a;// 正常
        int v2 = false ? 0 : a.intValue();// 报错
        Integer v3 = true ? 0 : a;// 正常
        Integer v4 = false ? 0 : Integer.valueOf(a.intValue());// 报错
    }

方法的返回值(自动拆装箱)

方法的返回值类型与实际返回值类型不同时,会自动拆装箱,变成方法返回值相同的类型。

    //自动拆箱
    public int getNum1() {
        return new Integer(1);
    }

    //自动装箱
    public Integer getNum2() {
        return 1;
    }

实际等于

    //自动拆箱
    public int getNum1() {
        return new Integer(1).byteValue();
    }

    //自动装箱
    public Integer getNum2() {
        return Integer.valueOf(1);
    }

5. 自动拆装箱的触发时机

在编译期,Java文件编译称Class(字节码)文件的过程中触发自动拆装箱的动作。

通过查看字节码文件可以予以证明。两种查看字节码信息的方式:

1.通过javac和javap查看

先通过javac将.java代码编译成.class字节码,然后通过javap分析字节码。

javac:编译成class(字节码)

javac DemoTest.java

javap:分析字节码

javap -verbose DemoTest.class

若出现编码问题,可指定编码

javac -encoding utf-8 DemoTest.java
javap -encoding utf-8 -verbose DemoTest.class

2.IDEA使用ASM Bytecode Outline插件查看字节码

类文件上右键->选择: Show Bytecode outline

面板上的三个选项:

  • Bytecode 表示对应的class字节码文件
  • ASMified 表示利用ASM框架生成字节码对应的代码
  • Groovified 对应的是class字节码指令

6. 自动拆装箱带来的问题

  • 包装对象之间的数值比较不能简单的使用==,除了特殊有缓存的情况(如Integer的-128~127),其他比较都需要使用equals比较。
  • 如果包装类对象为NULL,那么自动拆箱就可能会抛出空指针异常
  • 如果一个for循环中有大量拆装箱操作,会浪费很多资源

基本数据类型的转换

1. 容量大小排序

基本数据类型容量大小排序(不含布尔类型、容量由小到大):

  • byte<short/char<int<long<float<double
  • byte<<int<long<float<double

2. 转换规则

  1. 8种基本数据类型中,除了布尔类型之外,其他的8中数据类型之间都可以相互转换
  2. 任何浮点类型不管占用多少个字符,都比整数型容量大
  3. char和short可表示的种类数量相同,但是char可以取更大的整数
  4. 整数的默认类型为int,小数的默认类型为double
  5. 小容量向大容量转换,称为自动类型转换(又称隐式类型转换),不会丢失精度
  6. 大容量转换成小容量,叫做强制类型转换(又称显式类型转换),可能会会丢失精度,需要加强制类型转换符。
  7. 当整数字面值没有超出byte,short,char的取值范围,可以直接赋值给byte,short,char类型的变量
  8. 多种数据类型混合运算,先转换成容量大的那种类型再做运算(byte,short,char混合运算的时候,各自先转换成int类型再做运算)

3. 自动类型转换

小容量向大容量转换,称为自动类型转换(又称隐式类型转换)。

规则

  • 小容量的类型转化为大容量的类型自动使用自动类型转换
  • 整数类型可以自动转化为浮点类型,可能会产生舍入误差
  • 字符可以自动提升为整数

4. 强制类型转换

大容量转换成小容量,叫做强制类型转换(又称显式类型转换)。

强制类型转换需要在要强制类型转换的前面加上括号,然后在括号里面加上你要转换的类型。

规则

  • 大容量的类型转化为大容量的类型必须使用强制类型转换
  • 强制类型转换可能导致溢出或损失精度
  • 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入
  • 不能把对象类型转换为不相干的类型

包装类的缓存

Java包装类的缓存机制,是在Java 5中引入的一个有助于节省内存、提高性能的功能。

包装类的缓存只在装箱(通过包装类的valueOf方法来实现)时有效。

想必大家一定遇到过这样的问题:两个数值相等的包装类型对象通过==进行比较时,得到的结果有时是true,有时又是false。

这可能就是包装类的缓存在捣乱:当包装类对象经历过装箱操作,得到的对象可能是缓存好的对象,也可能是新创建的对象。

1. 包装类型的缓存值范围

基本类型大小(bit)默认值取值范围包装类包装类缓存范围
byte(字节)80[-2^7,2^7-1]
[-128,127]
Byte[-128,127]
char(字符)16空值(\u0000)
(unicode编码)
[0,2^16-1]
[0,65535]
Character[0,127]
short(短整数)160[-2^15,2^15-1]
[-32768,32767]
Short[-128,127]
int(整数)320[-2^31,2^31-1]
[-2147483648,2147483647]
Integer[-128,127]
long(长整数)640L[-2^63,2^63-1]
[-9223372036854774808,9223372036854774807]
Long[-128,127]
float(单精度小数)320.0F[-2^31,2^31-1]
[3.402823e+38,1.401298e-45]
Float
double(双精度小数)640.0[-2^63,2^63-1]
[1.797693e+308,4.9000000e-324]
Double
boolean(布尔值)8falsetrue,falseBooleantrue,false

boolean类型的底层是转换为1,0存储的,所以大小只有一个字节(8位)

  • valueOf方法的逻辑是先从判断值是否在缓存值范围中。如果在,直接返回缓存中的对象;如果不在,创建新的对象。
  • 在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的。
  • 在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax=来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后IntegerCache 初始化的时候就会读取该系统属性来决定上界。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • mybatis中的扩展实现源码解析

    mybatis中的扩展实现源码解析

    这篇文章主要介给大家绍了关于mybatis中扩展实现的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • Java中List转Array的两种主要方式

    Java中List转Array的两种主要方式

    在Java中经常遇到需要List与数组互相转换的场景,这篇文章主要给大家介绍了关于Java中List转Array的两种主要方式,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06
  • String与XML互转以及从XML取节点值并修改的方法

    String与XML互转以及从XML取节点值并修改的方法

    今天小编就为大家分享一篇String与XML互转以及从XML取节点值并修改的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • 多用多学之Java中的Set,List,Map详解

    多用多学之Java中的Set,List,Map详解

    下面小编就为大家带来一篇多用多学之Java中的Set,List,Map详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • SpringCloud通过Feign传递List类型参数方式

    SpringCloud通过Feign传递List类型参数方式

    这篇文章主要介绍了SpringCloud通过Feign传递List类型参数方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • 关于TransmittableThreadLocal线程池中线程复用问题的解决方案

    关于TransmittableThreadLocal线程池中线程复用问题的解决方案

    这篇文章主要介绍了关于TransmittableThreadLocal线程池中线程复用问题的解决方案,线程池复用线程,如果子线程执行完未移除上下文,则会导致后续线程可以取到之前线程设置的属性,需要的朋友可以参考下
    2023-11-11
  • Java表单重复提交的避免方法

    Java表单重复提交的避免方法

    如何避免表单重复提交,这篇文章就为大家详细介绍了Java表单重复提交的避免方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • SpringBoot 工程中的异常处理方式

    SpringBoot 工程中的异常处理方式

    这篇文章主要介绍了SpringBoot 工程中的异常处理方式,帮助大家更好的理解和学习使用springboot框架,感兴趣的朋友可以了解下
    2021-02-02
  • SpringMVC请求/响应乱码问题解决方案解析

    SpringMVC请求/响应乱码问题解决方案解析

    这篇文章主要介绍了SpringMVC请求/响应乱码问题解决方案解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 关于Mybatis-Plus Update更新策略问题

    关于Mybatis-Plus Update更新策略问题

    这篇文章主要介绍了关于Mybatis-Plus Update更新策略问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11

最新评论