一文带你深入了解Java的自动拆装箱

 更新时间:2023年11月23日 14:01:01   作者:荼锦AI编程  
Java推出了对于基本数据类型的对应的对象,将基本数据类型转换为对象就称为装箱,反之则是拆箱,本文主要为大家介绍了Java自动拆装箱的原理与应用,需要的可以参考下

什么是自动拆装箱

JDK 1.5开始增加了自动拆装箱机制,Java保留了一些原始的基本数据类型,但由于Java是强面向对象语言,数据类型当然也应该设置成对象才是,所以Java也推出了对于基本数据类型的对应的对象,将基本数据类型转换为对象就称为装箱,反之则是拆箱

八种基本数据类型

基本数据类型对象类型
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Java的大部分字节码指令都没有支持类型byte、char和short,甚至没有任何指令支持boolean类型。

那么Java底层是如何实现这些数据类型的?

事实上编译器会在编译期或运行期搞事

  • 将byte和short类型的数据带符号扩展(Sign-Extend)为相应的int类型数据。
  • 将boolean和char类型数据零位扩展(Zero-Extend)为相应的int类型数据。

在处理boolean、byte、short和char类型的数组时,也会转换为使用对应的int类型的字节码指令来处理。因此,大多数对于boolean、byte、short和char类型数据的操作,实际上都是使用相应的对int类型作为运算类型来进行的

例如,下面代码每一行代码上面的注释为其部分字节码指令

public class Test {
    /*
     *   以下均为整型入栈指令
     *   iconst_1 (-1到5的数据使用iconst指令,-1使用iconst_m1指令)
     *   bipush 100 (-128到127[一个字节]的数据使用bipush指令)
     *   sipush 1000 (-32768到32767[二个字节]的数据使用sipush指令)
     *   ldc 40000 (-2147483648到2147483647[四个字节]的数据使用ldc指令)
     */
    public static void main(String[] args) {
        // bipush 100【将整型数据100压入栈】
        byte b1 = 100;
        // bipush 101【将整型数据101压入栈】
        short s1 = 101;
        // bipush 49【将整型数据49压入栈】
        char c1 = '1';
        // iconst_1【将整型数据1压入栈】
        boolean bl1 = false;
    }
}

自动拆装箱原理

byte与Byte

public class Test {
    public static void main(String[] args) {
        Byte num = 1;
        byte num3 = num;
    }
}

编译后代码

public class Test {
    public static void main(String[] args) {
        Byte num = Byte.valueOf(1);
        byte num3 = num.byteValue();
    }
}

从编译后代码可以看出,这两者的转换用了Byte类中的方法,看一下这两个方法

    // 静态内部类
	private static class ByteCache {
        private ByteCache(){}

        static final Byte cache[] = new Byte[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }
	
	private final byte value;

    public static Byte valueOf(byte b) {
        // 数组是不存在负的下标的,所以加上偏移量
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
    }
	
	public Byte(byte value) {
        this.value = value;
    }

	public byte byteValue() {
        return value;
    }

Byte类在使用的时候就会将[-128,127]范围的Byte对象缓存进类中,这是用了享元模式的思想存取常用的热点Byte对象,重复利用该范围类的Byte对象,也就是说在这个数值范围的Byte对象一直是同一个对象

short与Short、long与Long、char与Character的转换原理和这个相同

(char保存的是ASCII码,所以只缓存[0,127]的对象)

int与Integer

public class Test {
    public static void main(String[] args) {
        Integer num = 1;
        Integer num2 = 200;
        int num3 = num;
    }
}

编译后代码

public class Test {
    public static void main(String[] args) {
        Integer num = Integer.valueOf(1);
        Integer num2 = Integer.valueOf(200);
        int num3 = num.intValue();
    }
}

我们可以看出自动拆装箱事实上是调用了Integer类中的方法,来看一下这两个方法,事实上和byte类似,默认[-128,127]的Integer对象被缓存了,但是IntegerCache.high是可能被人为配置过的,如果配置的缓存上限值大于127就会缓存[-128,配置的上限值],在此范围内直接返回缓存的对象,否则就new一个新的Integer对象

	public static Integer valueOf(int i) {
        // IntegerCache.low = -128,IntegerCache.high = 127(默认)
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

boolean与Boolean

同样是这两个方法,由于布尔型只有两个值,所以直接缓存两个对象

    public static final Boolean TRUE = new Boolean(true);

    public static final Boolean FALSE = new Boolean(false);

    private final boolean value;

    public Boolean(boolean value) {
        this.value = value;
    }

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

    public boolean booleanValue() {
        return value;
    }

float与Float以及double与Double

由于浮点数整数位相同时小数位可以有多种情况,比如整数位为1的有1.321332,1.543635,..................,数量太多所以没有某一个浮点数会被反复使用,所以没必要缓存,直接返回一个Float新对象

    public static Float valueOf(float f) {
        return new Float(f);
    }

    public static Double valueOf(double d) {
        return new Double(d);
    }

测试

我们测试一下是否返回的是同一个对象

public class Test {
    public static void main(String[] args) {
        Integer b1 = 10;
        Integer b2 = 10;
        Integer b3 = new Integer(10);
        Integer b4 = new Integer(10);
        System.out.println(b1 == b2); // true
        System.out.println(b2 == b3); // false
        System.out.println(b3 == b4); // false
    }
}

引用类型使用等于比较,比较的是对象是否相同(基本数据类型使用 = 进行比较比较的是数值)

那么b1等于b2说明两个装箱类型返回的b1和b2两个Integer对象是同一个对象,10这个数值在缓存范围内

而使用构造方法返回的对象是两个不同的对象,所以不相同结果为false

public class Test {
    public static void main(String[] args) {
        Integer b1 = 1000;
        Integer b2 = 1000;
        System.out.println(b1 == b2);// false
    }
}

而1000这个数值已经没有缓存了,所以返回的是两个对象

public class Test {
    public static void main(String[] args) {
        Double b1 = 10.0;
        Double b2 = 10.0;
        System.out.println(b1 == b2);// false
    }
}

由于Double是没有用缓存的,所以两个对象一定是不同的

到此这篇关于一文带你深入了解Java的自动拆装箱的文章就介绍到这了,更多相关Java自动拆装箱内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java Convert Kotlin空指针异常的解决方法

    Java Convert Kotlin空指针异常的解决方法

    本文主要介绍了Java Convert Kotlin空指针异常的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • 养成良好java代码编码规范

    养成良好java代码编码规范

    这篇文章主要介绍了如何养成良好java代码编码规范,规范需要平时编码过程中注意,是一个慢慢养成的好习惯,下面小编就带大家来一起详细了解一下吧
    2019-06-06
  • 浅谈利用Spring的AbstractRoutingDataSource解决多数据源的问题

    浅谈利用Spring的AbstractRoutingDataSource解决多数据源的问题

    本篇文章主要介绍了浅谈利用Spring的AbstractRoutingDataSource解决多数据源的问题,具有一定的参考价值,有需要的可以了解一下
    2017-08-08
  • Spring学习之开发环境搭建的详细步骤

    Spring学习之开发环境搭建的详细步骤

    本篇文章主要介绍了Spring学习之开发环境搭建的详细步骤,具有一定的参考价值,有兴趣的可以了解一下
    2017-07-07
  • springcloud整合seata的实现代码

    springcloud整合seata的实现代码

    这篇文章主要介绍了springcloud整合seata的实现方法,整合步骤通过引入spring-cloud-starter-alibaba-seata jar包,文中结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • Kafka中的producer拦截器与consumer拦截器详解

    Kafka中的producer拦截器与consumer拦截器详解

    这篇文章主要介绍了Kafka中的producer拦截器与consumer拦截器详解,Producer 的Interceptor使得用户在消息发送前以及Producer回调逻辑前有机会对消息做 一些定制化需求,比如修改消息等,需要的朋友可以参考下
    2023-12-12
  • SpringBoot中引入MyBatisPlus的常规操作

    SpringBoot中引入MyBatisPlus的常规操作

    这篇文章主要介绍了SpringBoot中引入MyBatisPlus的常规操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • SpringBoot 分模块开发的操作方法

    SpringBoot 分模块开发的操作方法

    这篇文章主要介绍了SpringBoot 分模块开发的操作方法,通过在原项目新增一个maven模块,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • Java详解HashMap实现原理和源码分析

    Java详解HashMap实现原理和源码分析

    这篇文章主要介绍了Java关于HashMap的实现原理并进行源码分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • Java8实现对List<Integer>的求和

    Java8实现对List<Integer>的求和

    这篇文章主要介绍了Java8实现对List<Integer>的求和方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05

最新评论