【Java】Java 中的自动装箱和拆箱

目录

1、Java 中的自动装箱

2、Java 中的自动拆箱

3、自动装箱和拆箱总结  

4、在 Java 中怎样避免自动装箱和拆箱?


        为什么Java需要自动装箱和拆箱这一机制呢?

        简单来说,就是有些历史原因的。

        比如,泛型(Java5引入),Java中的泛型只能使用引用类型,不能使用基本数据类型。还有集合框架(Java2引入),Java 的集合框架(如 List、Set、Map 等)只能存储对象(引用类型),不能存储基本数据类型。因此为了API的兼容性,代码的灵活性和便利性,就有了自动装箱和拆箱机制。

        下边一起来体会下这个机制。

1、Java 中的自动装箱

        自动装箱是 Java 编译器在基本类型和它们对应的包装类之间进行的自动转换。例如,将 int 类型转换为 Integer 类型,将 double 类型转换为 Double 型等等。

        如果转换以相反的方式进行,则称为拆箱// 包装类和基本数据类型的相互转换

        下面是一个最简单的自动装箱例子:

Character ch = 'a';

        例如,下面的代码:

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2){
    li.add(i);
}

        尽管将 int 值作为基本类型而不是 Integer 对象添加到 li 列表,但是代码仍然可以编译。这里 li 是一个 Integer 对象的列表,而不是 int 值的列表,为什么 Java 编译器不会发出编译时错误呢?

        编译器之所以不会产生错误,是因为它从 i 创建了一个 Integer 对象,并将该对象添加到 li 列表。因此,编译器在运行时实际是将前面的代码转换为以下代码:

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2){
    li.add(Integer.valueOf(i)); //装箱
}  

        Integer.valueOf(i) 这段代码里有什么呢?//自动装箱

//源码版本:java17
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

        Java 在拆箱的过程中,创建了一个新的对象(new Integer),既然创建了额外的对象,就增加内存开销和垃圾回收的负担,这也是为什么要避免频繁进行大量的自动装箱和拆箱操作的原因。//自动装箱和拆箱涉及到创建和销毁额外的对象,即包装类对象

        在以下情况下,Java 编译器会对基本类型的值进行自动装箱:

  1. 把基本类型的值作为参数传递给需要相应包装类的对象的方法。
  2. 把基本类型的值赋给相应包装类的变量。

2、Java 中的自动拆箱

        接下来,看一下Java中的自动拆箱,例如下边的代码:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li){
       if (i % 2 == 0){
           sum += i;
       }    
       return sum; 
    }
}

        求余运算符 (%) 和一元运算符 (+=) 并不适用于 Integer 对象,但 Java 编译器在编译该方法时却不会产生任何错误,因为在调用 intValue() 方法时,Java 会将 Integer 转换为 int:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i : li)
        if (i.intValue() % 2 == 0) //拆箱操作
            sum += i.intValue();
        return sum;
}

        将包装类型(Integer)的对象转换为其对应的基本类型(int)称为拆箱。在以下情况下,Java 编译器会对包装类型的值进行自动拆箱:

  1. 把包装类型的值作为参数传递给需要相应基本类型的值的方法。
  2. 把包装类型的值赋值给相应基本类型的变量。

        下边的例子展示了拆箱是如何工作的:

import java.util.ArrayList;
import java.util.List;

public class Unboxing {

    public static void main(String[] args) {
        Integer i = new Integer(-8);

        // 1. 通过方法调用进行拆箱
        int absVal = absoluteValue(i);
        System.out.println("absolute value of " + i + " = " + absVal);

        List<Double> ld = new ArrayList<>();
        ld.add(3.1416);    // Π 通过方法调用自动装箱。

        // 2. 通过赋值进行拆箱
        double pi = ld.get(0);
        System.out.println("pi = " + pi);
    }

    public static int absoluteValue(int i) {
        return (i < 0) ? -i : i;
    }
}

        该程序会打印以下内容:

absolute value of -8 = 8
pi = 3.1416

3、自动装箱和拆箱总结  

        自动装箱和拆箱可以让开发人员编写更加清晰的代码,使代码更加易于阅读下表列出了基本类型及其对应的包装类,Java 编译器会使用它们进行自动的装箱和拆箱:

基本类型包装类型
booleanBoolean
byteByte
charCharacter
float    Float
int    Integer
long    Long
short    Short
double    Double

        自动装箱和拆箱的实现是通过 Java 编译器在编译时进行的。具体来说,Java 编译器会将自动装箱和拆箱操作转换为对应的方法调用,以实现基本数据类型和包装类型之间的转换。

        但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值//所以,应该尽量避免不必要的装箱

4、在 Java 中怎样避免自动装箱和拆箱?

        就一条原则:尽量使用基本数据类型

        尽可能地使用基本数据类型,而不是对应的包装类型。基本数据类型的数据存储在栈中,而包装类型的对象存储在堆中,因此基本数据类型的操作比包装类型的操作更加高效。

        至此,全文结束。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swadian2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值