目录
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 编译器会对基本类型的值进行自动装箱:
- 把基本类型的值作为参数传递给需要相应包装类的对象的方法。
- 基本类型的值赋给相应包装类的变量。
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 编译器会对包装类型的值进行自动拆箱:
- 把包装类型的值作为参数传递给需要相应基本类型的值的方法。
- 把包装类型的值赋值给相应基本类型的变量。
下边的例子展示了拆箱是如何工作的:
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();
通过上述方法,可以有效地避免自动装箱和拆箱对程序性能的影响。