在本文中,我们将了解 Java 中称为自动装箱/拆箱的功能。自动装箱和拆箱是将原始类型转换为对象类型的功能,反之亦然。 整个过程由Java运行时环境(JRE)自动执行。但在实现这个功能时你应该小心,因为...... 它会影响程序的性能。
表 1:带有比较运算符的基本类型和等效包装类 自动装箱和拆箱可以与比较运算符一起使用。以下代码片段说明了这是如何发生的: 清单 3:显示使用比较运算符进行自动装箱和拆箱的示例代码
介绍
int
在 JDK 1.5 以下的版本中,将、char
、float
、 等原始数据类型转换double
为它们的包装类 Integer、Character、Float、Double并不容易。从 JDK 5 开始,自动实现将原始类型转换为等效对象的功能。此属性称为自动装箱。相反的过程分别是拆箱,即 将对象转换为其相应的原始类型的过程。自动装箱和拆箱的示例代码如下:
Integer integer = 9;
拆箱
int in = 0;
in = new Integer(9);
什么时候使用自动装箱和拆箱? Java 编译器在以下条件下使用自动装箱:
- 当基本类型的值作为方法参数传递给方法时,需要相应包装类的对象。
- 当将原始类型的值分配给相应包装类的变量时。
public int sumEvenNumbers(List<Integer> intList ) {
int sum = 0;
for (Integer i: intList )
if ( i % 2 == 0 )
sum += i;
return sum;
}
在 jdk 1.5 之前,上述代码会导致编译错误,因为余数运算符 % 和一元加 += 无法应用于包装类。但在 jdk 1.5 及以上版本中,这段代码编译没有错误,将 Integer 转换为int
. Java 编译器在以下条件下使用拆箱:
- 当对象作为参数传递给需要相应基本类型的方法时。
- 当一个对象被分配给相应原始类型的变量时。
import java.util.ArrayList;
import java.util.List;
public class UnboxingCheck {
public static void main(String[] args) {
Integer in = new Integer(-8);
// 1. Распаковка через вызов метода
int absVal = absoluteValue(in);
System.out.println("absolute value of " + in + " = " + absVal);
List<Double> doubleList = new ArrayList<Double>();
// Автоупаковка через вызов метода
doubleList.add(3.1416);
// 2. Распаковка через присвоение
double phi = doubleList.get(0);
System.out.println("phi = " + phi);
}
public static int absoluteValue(int i) {
return (i < 0) ? -i : i;
}
}
自动装箱和拆箱允许开发人员编写易于阅读和理解的代码。下表显示了原始数据类型及其相应的包装对象。
原始类型 | 外壳类 |
---|---|
布尔值 | 布尔值 |
字节 | 字节 |
字符 | 特点 |
漂浮 | 漂浮 |
整数 | 整数 |
长的 | 长的 |
短的 | 短的 |
public class BoxedComparator {
public static void main(String[] args) {
Integer in = new Integer(25);
if (in < 35)
System.out.println("Value of int = " + in);
}
}
方法重载时的自动打包和解包 方法重载时的自动打包和解包按照以下规则进行:
- 当需要在扩展和封装之间进行选择时,扩展会“击败”封装;扩展是更好的选择。
public class WideBoxed {
public class WideBoxed {
static void methodWide(int i) {
System.out.println("int");
}
static void methodWide( Integer i ) {
System.out.println("Integer");
}
public static void main(String[] args) {
short shVal = 25;
methodWide(shVal);
}
}
}
程序输出类型int
- 扩展胜过可变数量的参数 在需要在扩展和可变数量的参数之间进行选择的情况下,扩展是更可取的。
public class WideVarArgs {
static void methodWideVar(int i1, int i2) {
System.out.println("int int");
}
static void methodWideVar(Integer... i) {
System.out.println("Integers");
}
public static void main( String[] args) {
short shVal1 = 25;
short shVal2 = 35;
methodWideVar( shVal1, shVal2);
}
}
- 打包优于可变数量的参数 在需要在打包和可变数量的参数之间进行选择的情况下,打包是更可取的。
public class BoxVarargs {
static void methodBoxVar(Integer in) {
System.out.println("Integer");
}
static void methodBoxVar(Integer... i) {
System.out.println("Integers");
}
public static void main(String[] args) {
int intVal1 = 25;
methodBoxVar(intVal1);
}
}
使用自动打包时应记住以下事项: 众所周知,每个好的功能都有一个缺点。汽车包装在这方面也不例外。开发人员在使用此功能时应牢记的一些重要注意事项:
- 将对象与“
==
”运算符进行比较可能会令人困惑,因为它可以应用于基本类型和对象。当此运算符应用于对象时,它实际上比较对象的引用,而不是对象本身。
public class Comparator {
public static void main(String[] args) {
Integer istInt = new Integer(1);
Integer secondInt = new Integer(1);
if (istInt == secondInt) {
System.out.println("both one are equal");
} else {
System.out.println("Both one are not equal");
}
}
}
- 将对象和原始类型与相等和关系运算符混合。如果我们将原始类型与对象进行比较,则该对象将被拆箱,
NullPointerException
如果该对象null
. - 对象缓存。该方法
valueOf()
创建一个它缓存的原始对象的容器。由于值的缓存范围为 -128 到 127(含),因此这些缓存对象的行为可能有所不同。 - 性能下降。自动装箱或拆箱会降低应用程序性能,因为它会创建不需要的对象,迫使垃圾收集器更频繁地运行。
public int sumEvenNumbers(List intList) {
int sum = 0;
for (Integer i : intList) {
if (i % 2 == 0) {
sum += i;
}
}
return sum;
}
在这一段代码中,sum +=i
它将被扩展为sum = sum + i
. 从“ +
”运算符开始,JVM 开始拆箱,因为“ +
”运算符不能应用于 Integer 对象。然后结果被自动打包回来。在JDK 1.5之前,数据类型int
和Integer是不同的。在方法重载的情况下,使用这两种类型没有问题。随着自动打包/拆包的出现,这变得更加困难。一个例子是remove()
中的重载方法ArrayList
。该类ArrayList
有两个删除方法 -remove(index)
和remove(object)
。在这种情况下,不会发生方法重载,并且会使用适当的参数调用相应的方法。
结论
自动装箱是一种将原始数据类型隐式转换为相应包装类(对象)的机制。编译器通过方法valueOf()
将原始类型转换为对象,通过方法IntValue()
等doubleValue()
获取对象的原始类型。自动装箱将布尔类型转换boolean
为布尔型、byte
字节型char
、字符型 float
、浮点型、int
整数型、long
长整型、short
短整型。拆包以相反的方向进行。 来源文章
GO TO FULL VERSION