java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java自动拆装箱、基本类型的转换、包装类的缓存

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

作者:骑个小蜗牛

文章详细介绍了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

面板上的三个选项:

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

基本数据类型的转换

1. 容量大小排序

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

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位)

总结

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

您可能感兴趣的文章:
阅读全文