Java 到底偷偷做了什么之自动拆装箱

1.作用
是一种java提供的语法糖,实际简化了基本数据类型与对应的对象包装类之间的转化.

本身简化了基本数据类型在对象使用时的代码.

通过自动拆装箱实现了泛型对基本数据类型的兼容.

实际自动拆装箱的实现,有效提高了效率,减少了对象的创建.(对象缓存)

2.概念定义
首先得明白什么是拆装箱.

装箱:指将基本类型转化为对应的包装类对象,比如int到Integer,float到Float.一般将基本类型作为参数传入包装类构造方法.

拆箱:与装箱相反,由对应包装类对象转为基本数据类型.使用包装类的方法获取内部封装的基本类型值.

而自动拆装箱大大简化了这一过程,通过简单的赋值操作,便可以实现两者对应直接的转换.
Integer integer = 10; int i = integer;

基本数据类型与对应的包装类如下:

Primitive typeWrapper class
booleanBoolean
byteByte
charCharacter
floatFloat
intInteger
longLong
shortShort
doubleDouble

3.如何实现
装箱:实际调用包装器的valueOf()方法;

拆箱:实际使用包装器的xxxValue()方法.

Integer integer = 10;
int i = integer;

编译结果如下:

Integer integer = Integer.valueOf((int)10);
int i = integer.intValue();

valueOf()的方法实现解释了为什么自动装箱比手动通过构造器装箱的效率更高了.

public static Integer valueOf(int i) {
//但基本数据类型值 在指定范围之内时,会返回原先缓存的对象,默认范围为-128到127

    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

由于Integer比较特殊,可以设置缓存范围,因此以Integer为例,看一下缓存的实现

    static final int low = -128;//不可被修改
    static final int high;
 
    static {
        // 默认缓存范围是从-128 到 127
   //但是最大值允许配置
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
//设置的缓存值与默认值比较,取较大的一个
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE:不能超过数组的最大长度
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;
 
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
 
        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }
    

设置缓存最大值:
运行时可通过-XX:AutoBoxCacheMax=1000 指定最大缓存

除去Double与Float类型外(存在浮点数,没有经常使用的热点值),Character/Byte/Short/Long/Boolean都有各自的缓存实现.不过相对于Integer,只能使用默认的缓存范围.
数值型的范围为-128到127:Byte/Short/Long
Character字符型:0到127
Boolean:只有true和false 的实现


public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

4.何时何地会自动拆装箱
一般情况下,在进行数值赋值,与数值比较,运算符计算或涉及到基本包装类的泛型时,会触发自动拆装箱.


public static void main(String[] args) {
    int i1 = 1;
    Integer integer1 = 1;
    Integer integer2 = 2;
    System.out.println(i1 == integer1);
    System.out.println(integer1 == integer2);
    System.out.println(integer1.equals(integer1));
    System.out.println(integer1.equals(1));
    System.out.println(integer1 + i1);
    System.out.println(integer1 + integer2);
}
//结果
true
false
true
true
2
3
//反编译的结果如下
public static void main(String[] args) {
        int i1 = 1;
        Integer integer1 = Integer.valueOf((int)1);//赋值时的装箱
        Integer integer2 = Integer.valueOf((int)2);
        System.out.println((boolean)(i1 == integer1.intValue()));//与数值比较时的拆箱
        System.out.println((boolean)(integer1 == integer2));//包装类之间依旧是比较地址
        System.out.println((boolean)integer1.equals((Object)integer1));
        System.out.println((boolean)integer1.equals((Object)Integer.valueOf((int)1)));//equals接受的是对象,因此只有可能触发自动装箱
        System.out.println((int)(integer1.intValue() + i1));//进行数值计算,会转换为基本数据类型计算
        System.out.println((int)(integer1.intValue() + integer2.intValue()));
    }
 
public static void main(String[] args) {
    List<Integer> list = new ArrayList<>(6);
    list.add(1);
//通过自动拆装箱,实际上使得泛型机制能够支持基本数据类型的操作.极大简化了代码
//list.add((Integer)Integer.valueOf((int)1));
}

实际上在项目中 的应用,基于我有限经验的理解,在接收外部数据或者数据库数据,映射到类的属性中或者复制到变量中时,推荐使用包装类.因为数据返回有可能会是null,如果直接转换成基本类型,会导致报错.

5.可能带来的问题
空指针:
就像上面说的,自动拆箱很容易导致空指针异常,尤其是在允许存入null的集合中取得数据时.因此,在使用包装类进行可能触发自动拆箱的时候,务必要注意处理null值的情况,防止空指针异常.

内存问题:
注意自动装箱会创建对象的过程.很有可能在你以为只是简单使用基本类型时,导致自动装箱在大量创建对象.
例如不知道赋值变量的类型,使用超过缓存的数值进行大量赋值,导致自动装箱创建对象.你所预想使用基本类型在退出方法后,就被清除了.但实际上大量创建的对象被保存在堆上,导致内存空间被大量占用,降低了系统的性能.

参考资料:
链接:Autoboxing and Unboxing (The Java Tutorials > Lea…
链接:深入剖析Java中的装箱和拆箱 - 海 子 - 博客园
链接:Java 自动装箱与拆箱的实现原理 - 简书
https://blog.csdn.net/qq_38071004/article/details/81911165

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值