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(1);
            list.add(2);
        }
    

    实际等于

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

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值