目录
一、包装类概述
在Java中,基本数据类型(如int、double、boolean等)不是对象,但有时我们需要将它们当作对象来处理。为此,Java为每个基本数据类型提供了对应的包装类(Wrapper Class)。
主要包装类及其对应关系
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
为什么需要包装类?
-
泛型支持:Java泛型不支持基本类型,只能使用包装类
-
集合框架:集合如ArrayList、HashMap等只能存储对象
-
提供实用方法:包装类提供了许多有用的方法(如parseInt()、toString()等)
-
允许null值:包装类可以表示缺失值(null),而基本类型不能
二、装箱与拆箱
装箱(Autoboxing)
装箱是指将基本数据类型自动转换为对应的包装类对象。
java
// 手动装箱(Java 5之前)
Integer intObj1 = Integer.valueOf(10);
// 自动装箱(Java 5之后)
Integer intObj2 = 10; // 编译器自动转换为Integer.valueOf(10)
拆箱(Unboxing)
拆箱是指将包装类对象自动转换为对应的基本数据类型。
java
Integer intObj = Integer.valueOf(20);
// 手动拆箱
int i1 = intObj.intValue();
// 自动拆箱
int i2 = intObj; // 编译器自动转换为intObj.intValue()
案例解析
public class BoxingExample { public static void main(String[] args) { // 自动装箱 List<Integer> list = new ArrayList<>(); list.add(1); // 自动装箱为Integer list.add(2); // 自动装箱为Integer // 自动拆箱 int first = list.get(0); // 自动拆箱为int // 混合运算 Integer a = 5; Integer b = 10; int sum = a + b; // 先拆箱再相加 System.out.println("First element: " + first); System.out.println("Sum: " + sum); } }
三、Integer缓存机制
什么是Integer缓存?
Java为了优化性能,对Integer对象实现了缓存机制。默认情况下,Integer会缓存-128到127之间的值。
Integer a = 127; Integer b = 127; System.out.println(a == b); // true,因为使用了缓存中的同一对象 Integer c = 128; Integer d = 128; System.out.println(c == d); // false,超出缓存范围,创建了新对象
缓存机制实现原理
Integer类的valueOf()方法实现了缓存:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
缓存范围的可配置性
缓存的上限可以通过JVM参数进行调整:
-XX:AutoBoxCacheMax=<size>
这将把缓存上限从127扩展到指定的size。
深入案例
java
public class IntegerCacheDemo {
public static void main(String[] args) {
// 测试缓存范围内的值
Integer i1 = 100;
Integer i2 = 100;
System.out.println("i1 == i2: " + (i1 == i2)); // true
// 测试缓存范围外的值
Integer i3 = 200;
Integer i4 = 200;
System.out.println("i3 == i4: " + (i3 == i4)); // false
// 使用new创建对象,不适用缓存
Integer i5 = new Integer(100);
Integer i6 = new Integer(100);
System.out.println("i5 == i6: " + (i5 == i6)); // false
// 比较应该使用equals
System.out.println("i1.equals(i2): " + i1.equals(i2)); // true
System.out.println("i3.equals(i4): " + i3.equals(i4)); // true
System.out.println("i5.equals(i6): " + i5.equals(i6)); // true
}
}
四、其他包装类的缓存机制
除了Integer,其他包装类也有类似的缓存机制:
-
Byte:缓存所有可能值(-128到127)
-
Short:缓存-128到127
-
Long:缓存-128到127
-
Character:缓存0到127
-
Boolean:缓存TRUE和FALSE
// Boolean缓存示例 Boolean b1 = true; Boolean b2 = true; System.out.println(b1 == b2); // true // Character缓存示例 Character c1 = 'A'; Character c2 = 'A'; System.out.println(c1 == c2); // true Character c3 = '啊'; Character c4 = '啊'; System.out.println(c3 == c4); // false
五、常见陷阱与最佳实践
陷阱1:空指针异常
Integer num = null; int n = num; // 运行时抛出NullPointerException
解决方案:确保包装类对象不为null后再拆箱
陷阱2:比较运算符的误用
Integer a = 100; Integer b = 100; Integer c = 200; Integer d = 200; System.out.println(a == b); // true System.out.println(c == d); // false
最佳实践:比较包装类对象时总是使用equals()方法
陷阱3:性能问题
// 性能低下的代码 Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; // 发生了大量的自动装箱操作 }
优化方案:使用基本类型long
long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; }
六、综合应用案例
案例1:包装类在集合中的应用
java
public class CollectionWithWrappers {
public static void main(String[] args) {
// 1. List中使用Integer
List<Integer> numbers = new ArrayList<>();
numbers.add(1); // 自动装箱
numbers.add(2);
numbers.add(3);
int sum = 0;
for (Integer num : numbers) {
sum += num; // 自动拆箱
}
System.out.println("Sum: " + sum);
// 2. Map中使用包装类
Map<String, Integer> wordCount = new HashMap<>();
wordCount.put("apple", 5);
wordCount.put("orange", 3);
// 更新值
wordCount.put("apple", wordCount.get("apple") + 1); // 自动拆箱和装箱
System.out.println("Updated counts: " + wordCount);
}
}
案例2:方法重载与自动装箱
java
public class OverloadingBoxing {
public static void print(int num) {
System.out.println("Primitive int: " + num);
}
public static void print(Integer num) {
System.out.println("Wrapper Integer: " + num);
}
public static void main(String[] args) {
print(10); // 调用print(int)
print(Integer.valueOf(10)); // 调用print(Integer)
// 有趣的情况
print(null); // 编译错误,歧义调用
}
}
七、总结
-
包装类是基本数据类型的对象表示,提供了更多功能和灵活性
-
自动装箱和拆箱是Java的语法糖,简化了基本类型和包装类之间的转换
-
Integer缓存机制优化了常用数值的性能,但要注意比较时的行为差异
-
最佳实践:
-
比较包装类对象时使用equals()而非==
-
注意可能的NullPointerException
-
性能敏感场景优先使用基本类型
-
了解各包装类的缓存范围
-