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 初始化的时候就会读取该系统属性来决定上界。

总结

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

相关文章

  • 解决在IDEA下使用JUnit的问题(解决过程)

    解决在IDEA下使用JUnit的问题(解决过程)

    很多朋友跟小编反馈在IDEA下使用JUnit进行实例测试的时候出现很多奇葩问题,今天小编通过本文给大家分享idea使用JUnit出现问题及解决过程,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • 基于Java堆内存的10个要点的总结分析

    基于Java堆内存的10个要点的总结分析

    本篇文章是对Java堆内存的10个要点进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 一个牛人给Java初学者的建议(必看篇)

    一个牛人给Java初学者的建议(必看篇)

    下面小编就为大家带来一篇一个牛人给Java初学者的建议(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • IDEA中Web项目控制台乱码的问题及解决方法

    IDEA中Web项目控制台乱码的问题及解决方法

    这篇文章主要介绍了IDEA中Web项目控制台乱码的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Java调用本地库的JNA快速入门教程

    Java调用本地库的JNA快速入门教程

    JNA(Java Native Access)是Java库,简化调用操作系统API,本文通过一个示例项目展示了如何使用JNA与C/C++编写的DLL交互,包括定义原生接口、数据类型映射、指针和引用的处理、结构体的使用以及异常处理等,感兴趣的朋友一起看看吧
    2025-09-09
  • hibernate-validator改进校验框架validator v0.4使用

    hibernate-validator改进校验框架validator v0.4使用

    这篇文章主要为大家介绍了改进 hibernate-validator,新一代校验框架 validator 使用介绍 v0.4,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪<BR>
    2023-03-03
  • java三个环境变量配置简单教程

    java三个环境变量配置简单教程

    这篇文章主要为大家详细介绍了java三个环境变量配置简单教程,配置path变量、配置classpath变量、最后是配置JAVA_HOME变量,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • java设计模式之代理模式(Porxy)详解

    java设计模式之代理模式(Porxy)详解

    这篇文章主要为大家详细介绍了java设计模式之代理模式Porxy的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • java实现文件上传下载至ftp服务器

    java实现文件上传下载至ftp服务器

    这篇文章主要为大家详细介绍了java实现文件上传下载至ftp服务器的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • 详解SpringMVC拦截器配置及使用方法

    详解SpringMVC拦截器配置及使用方法

    本篇文章主要介绍了SpringMVC拦截器配置及使用方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09

最新评论