目录

1、Java 中的自动装箱

2、Java 中的自动拆箱

3、自动装箱和拆箱总结  

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

1、Java 中的自动装箱

// 包装类和基本数据类型的相互转换

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

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

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

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

2、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 编译器会使用它们进行自动的装箱和拆箱:

基本类型

包装类型

boolean

Boolean

byte

Byte

char

Character

float    

Float

int    

Integer

long    

Long

short    

Short

double    

Double

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

        在自动装箱时,编译器会自动调用对应的包装类型的 valueOf() 方法来将基本数据类型转换为对应的包装类型。例如,将 int 类型的变量 i 转换为 Integer 类型的变量 obj,编译器会自动调用 Integer.valueOf(i) 方法。

        在自动拆箱时,编译器会自动调用对应的包装类型的 xxxValue() 方法将包装类型转换为基本数据类型。例如,将 Integer 类型的变量 obj 转换为 int 类型的变量 i,编译器会自动调用 obj.intValue() 方法。

        需要注意的是,在进行自动装箱和拆箱的过程中,编译器会生成一些额外的代码,因此可能会对性能产生一定的影响。因此,在对性能要求较高的场景下,应尽量避免使用自动装箱和拆箱。

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

        (1)使用基本数据类型

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

        (2)使用静态常量

        对于常用的数值,可以使用静态常量来避免自动装箱的开销。例如,使用如下代码定义一个静态常量:

public static final int MAX_VALUE = 100;

        这样就可以在代码中直接使用 MAX_VALUE,而不需要使用 Integer.valueOf(MAX_VALUE)。

        (3)使用 StringBuilder 或 StringBuffer

        在字符串拼接时,应该使用 StringBuilder 或 StringBuffer,而不是字符串连接符 “+”。因为字符串连接符会导致自动装箱的开销,而 StringBuilder 或 StringBuffer 可以避免这个问题。

        例如,将两个字符串连接起来,可以使用如下代码:

String str1 = "hello";
String str2 = "world";
StringBuilder sb = new StringBuilder();
sb.append(str1).append(str2);
String result = sb.toString();

        通过上述方法,可以有效地避免自动装箱和拆箱对程序性能的影响。