一文带你深入了解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自动拆装箱内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Fastjson反序列化随机性失败示例详解

    Fastjson反序列化随机性失败示例详解

    这篇文章主要为大家介绍了Fastjson反序列化随机性失败示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • SpringBoot接值实现方法详解

    SpringBoot接值实现方法详解

    这篇文章主要介绍了SpringBoot接值实现方法,SpringBoot接值是指在SpringBoot应用程序中接收请求参数,从HTTP请求中获取参数,并将其绑定到Java对象中进行处理的过程,感兴趣想要详细了解可以参考下文
    2023-05-05
  • js-tab选项卡

    js-tab选项卡

    本文主要介绍了js-tab选项卡的示例代码。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • java 多线程-线程通信实例讲解

    java 多线程-线程通信实例讲解

    本文主要介绍java 多线程-线程通信 这里整理了相关资料及示例代码,有兴趣的小伙伴可以参考下
    2016-09-09
  • Java使用RedisTemplate模糊删除key操作

    Java使用RedisTemplate模糊删除key操作

    这篇文章主要介绍了Java使用RedisTemplate模糊删除key操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • 关于HttpServletRequest获取POST请求Body参数的3种方式

    关于HttpServletRequest获取POST请求Body参数的3种方式

    这篇文章主要介绍了关于HttpServletRequest获取POST请求Body参数的3种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Spring Security用户定义 

    Spring Security用户定义 

    这篇文章主要介绍了Spring Security用户定义,大家都知道 Spring Security的用户定义有很多方式,其实主要有两种,基于内存的和基于数据库的,下面我给大家简单介绍一下这两种方式,需要的朋友可以参考下
    2022-02-02
  • Java全面细致讲解Wrapper的使用

    Java全面细致讲解Wrapper的使用

    在封装中有一种特殊的类,能够把基本的数据类型进行转换来方便实际的使用。我们在之前提到的一些数据类型,最明显的特征是所有字母为小写状态,那么经过Wrapper的包装后,首字母就变成了大写。下面我们就这种特殊的封装类Wrapper的使用
    2022-05-05
  • Java中List集合去重的几种方式详细解析

    Java中List集合去重的几种方式详细解析

    这篇文章主要介绍了Java中List集合去重的几种方式详细解析,在日常的业务开发中,偶尔会遇到需要将 List 集合中的重复数据去除掉的场景,那么今天我们来看看几种LIst集合去重的方式,需要的朋友可以参考下
    2023-11-11
  • Java中的DelayQueue源码解析

    Java中的DelayQueue源码解析

    这篇文章主要介绍了Java中的DelayQueue源码解析,一个实现PriorityBlockingQueue实现延迟获取的无界队列,在创建元素时,可以指定多久才能从队列中获取当前元素,只有延时期满后才能从队列中获取元素,需要的朋友可以参考下
    2023-12-12

最新评论