Java中的包装类
1. 包装类的基本概念
在Java中,包装类(Wrapper Class)是用于将基本数据类型封装为对象的类。Java的基本数据类型包括`byte`、`short`、`int`、`long`、`float`、`double`、`char`和`boolean`。这些基本数据类型不是对象,因此不能直接调用方法或用于需要对象的地方。为了弥补这一不足,Java提供了相应的包装类,每个基本数据类型都有一个对应的包装类:
-
`byte` 对应 `Byte`
-
`short` 对应 `Short`
-
`int` 对应 `Integer`
-
`long` 对应 `Long`
-
`float` 对应 `Float`
-
`double` 对应 `Double`
-
`char` 对应 `Character`
-
`boolean` 对应 `Boolean`
这些包装类位于`java.lang`包中,提供了许多实用的方法,使得基本数据类型可以作为对象使用。
2. 包装类的作用和用途
2.1 集合框架
Java的集合框架(如`ArrayList`、`HashMap`等)只能存储对象,不能存储基本数据类型。包装类允许将基本数据类型转换为对象,从而可以将它们存储在集合中。
```java
import java.util.ArrayList;
public class WrapperClassExample {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(10); // 自动装箱
list.add(20);
list.add(30);
for (Integer value : list) {
System.out.println(value); // 自动拆箱
}
}
}
```
2.2 提供实用方法
包装类提供了许多有用的方法,例如将字符串转换为基本数据类型、将基本数据类型转换为字符串、比较值等。
```java
public class WrapperMethodsExample {
public static void main(String[] args) {
int intValue = Integer.parseInt("123");
String strValue = Integer.toString(123);
int comparison = Integer.compare(10, 20);
System.out.println("Parsed int value: " + intValue);
System.out.println("String value: " + strValue);
System.out.println("Comparison result: " + comparison);
}
}
```
3. 自动装箱和拆箱
从Java 5开始,Java引入了自动装箱(Autoboxing)和自动拆箱(Unboxing)功能,使得基本数据类型和对应的包装类之间的转换更加方便。
3.1 自动装箱
自动装箱是指编译器自动将基本数据类型转换为对应的包装类对象。
```java
public class AutoboxingExample {
public static void main(String[] args) {
int intValue = 100;
Integer integerObject = intValue; // 自动装箱
System.out.println("Integer object: " + integerObject);
}
}
```
3.2 自动拆箱
自动拆箱是指编译器自动将包装类对象转换为对应的基本数据类型。
```java
public class UnboxingExample {
public static void main(String[] args) {
Integer integerObject = 100;
int intValue = integerObject; // 自动拆箱
System.out.println("int value: " + intValue);
}
}
```
4. 包装类的构造方法和常用方法
每个包装类都有多个构造方法和常用方法,以下是一些示例。
4.1 Integer类
`Integer`类用于封装`int`类型。
```java
public class IntegerExample {
public static void main(String[] args) {
Integer integer1 = new Integer(100);
Integer integer2 = Integer.valueOf(100);
Integer integer3 = Integer.parseInt("100");
System.out.println("integer1: " + integer1);
System.out.println("integer2: " + integer2);
System.out.println("integer3: " + integer3);
int intValue = integer1.intValue();
System.out.println("intValue: " + intValue);
String strValue = integer1.toString();
System.out.println("strValue: " + strValue);
int comparison = Integer.compare(10, 20);
System.out.println("Comparison result: " + comparison);
}
}
```
4.2 Double类
`Double`类用于封装`double`类型。
```java
public class DoubleExample {
public static void main(String[] args) {
Double double1 = new Double(10.5);
Double double2 = Double.valueOf(10.5);
Double double3 = Double.parseDouble("10.5");
System.out.println("double1: " + double1);
System.out.println("double2: " + double2);
System.out.println("double3: " + double3);
double doubleValue = double1.doubleValue();
System.out.println("doubleValue: " + doubleValue);
String strValue = double1.toString();
System.out.println("strValue: " + strValue);
int comparison = Double.compare(10.5, 20.5);
System.out.println("Comparison result: " + comparison);
}
}
```
5. 包装类的性能影响
5.1 装箱和拆箱的开销
虽然自动装箱和拆箱使得代码更加简洁,但它们也带来了性能开销。每次装箱都会创建一个新的对象,而拆箱则涉及对象的解引用。在性能敏感的代码中,频繁的装箱和拆箱操作可能导致性能下降。
```java
public class PerformanceExample {
public static void main(String[] args) {
long start = System.nanoTime();
Integer sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i; // 自动装箱和拆箱
}
long end = System.nanoTime();
System.out.println("Sum: " + sum);
System.out.println("Time taken: " + (end - start) + " ns");
}
}
```
5.2 缓存机制
为了减轻装箱的性能影响,Java对某些包装类实现了缓存机制。例如,对于`Integer`类,值在`-128`到`127`之间的对象会被缓存,以减少重复创建对象的开销。
```java
public class IntegerCacheExample {
public static void main(String[] args) {
Integer int1 = Integer.valueOf(100);
Integer int2 = Integer.valueOf(100);
Integer int3 = Integer.valueOf(200);
Integer int4 = Integer.valueOf(200);
System.out.println("int1 == int2: " + (int1 == int2)); // 输出:true
System.out.println("int3 == int4: " + (int3 == int4)); // 输出:false
}
}
```
在上述代码中,对于值`100`,`int1`和`int2`指向同一个缓存对象,而对于值`200`,`int3`和`int4`是不同的对象。
6. 包装类的不可变性
包装类是不可变类,一旦创建,其值就不能改变。不可变性使得包装类对象是线程安全的,适用于多线程环境。
```java
public class ImmutabilityExample {
public static void main(String[] args) {
Integer int1 = 100;
Integer int2 = int1;
int1++;
System.out.println("int1: " + int1); // 输出:101
System.out.println("int2: " + int2); // 输出:100
}
}
```
在上述代码中,`int1`的值改变后,`int2`的值仍然保持不变,证明了`Integer`对象的不可变性。
7. 使用包装类的注意事项
7.1 NullPointerException
使用包装类时要小心`NullPointerException`。由于包装类可以为`null`,在进行拆箱操作时,如果包装类对象为`null`,会抛出`NullPointerException`。
```java
public class NullPointerExample {
public static void main(String[] args) {
Integer integer = null;
try {
int intValue = integer; // 自动拆箱,抛出NullPointerException
} catch (NullPointerException e) {
System.out.println("Caught NullPointerException");
}
}
}
```
7.2 性能优化
在性能敏感的代码中,应尽量减少不必要的装箱和拆箱操作,或者考虑使用基本数据类型。
```java
public class PerformanceOptimizationExample {
public static void main(String[] args) {
long start = System.nanoTime();
int sum = 0; // 使用基本数据类型
for (int i = 0; i < 1000000; i++) {
sum += i;
}
long end = System.nanoTime();
System.out.println("Sum: " + sum);
System.out.println("Time taken: " + (end - start) + " ns");
}