Java 包装类型详解
包装类型是基本类型的对象表示,理解其原理能避免很多陷阱。
一、包装类型概述
1.1 八种包装类型
基本类型 包装类型
boolean → Boolean
byte → Byte
char → Character
short → Short
int → Integer
long → Long
float → Float
double → Double
1.2 为什么需要包装类型
// 泛型需要对象
List<Integer> list = new ArrayList<>();
// ❌ List<int> list = new ArrayList<>();
// 集合存储对象
Map<String, Integer> map = new HashMap<>();
// 方法返回多个值
class Result {
Integer code;
String message;
}
二、自动拆装箱
2.1 自动装箱
// 之前:手动装箱
Integer value = Integer.valueOf(10);
// Java 5+:自动装箱
Integer value = 10;
// 编译器转换为:Integer.valueOf(10)
2.2 自动拆箱
// 之前:手动拆箱
Integer value = Integer.valueOf(10);
int primitive = value.intValue();
// Java 5+:自动拆箱
Integer value = 10;
int primitive = value;
// 编译器转换为:value.intValue()
2.3 运算时的拆装箱
Integer a = 10;
Integer b = 20;
// 自动拆箱 → 运算 → 自动装箱
Integer sum = a + b;
// 等价于:Integer.valueOf(a.intValue() + b.intValue())
三、Integer 缓存池
3.1 IntegerCache
// Integer 内部类
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 缓存范围:-128 到 127(默认)
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
}
// valueOf 方法
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
3.2 缓存陷阱
// ✅ 使用缓存
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true(同一对象)
// ❌ 不使用缓存
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false(不同对象)
// ✅ 正确比较
Integer e = 200;
Integer f = 200;
System.out.println(e.equals(f)); // true(值相等)
3.3 缓存范围配置
// JVM 参数可调整缓存范围
-XX:AutoBoxCacheMax=200 // 上限(下限固定 -128)
// 示例
// -XX:AutoBoxCacheMax=200
Integer a = 200;
Integer b = 200;
System.out.println(a == b); // true
四、其他包装类型缓存
4.1 Long 缓存
// Long 也有缓存(-128 到 127)
Long a = 100L;
Long b = 100L;
System.out.println(a == b); // true
Long c = 200L;
Long d = 200L;
System.out.println(c == d); // false
4.2 Byte 缓存
// Byte 全部缓存(-128 到 127,覆盖全部范围)
Byte a = 100;
Byte b = 100;
System.out.println(a == b); // true(总是 true)
4.3 Character 缓存
// Character 缓存 0-127
Character a = 'a';
Character b = 'a';
System.out.println(a == b); // true
Character c = '\u4e2d'; // 中文
Character d = '\u4e2d';
System.out.println(c == d); // false
4.4 Short 缓存
// Short 缓存 -128 到 127
Short a = 100;
Short b = 100;
System.out.println(a == b); // true
Short c = 200;
Short d = 200;
System.out.println(c == d); // false
五、常见陷阱
5.1 == vs equals
// ❌ 错误:使用 == 比较
Integer a = 200;
Integer b = 200;
System.out.println(a == b); // false
// ✅ 正确:使用 equals
System.out.println(a.equals(b)); // true
// ✅ 或者:拆箱比较
System.out.println(a.intValue() == b.intValue()); // true
5.2 空指针异常
// ❌ 可能 NPE
Integer value = null;
int primitive = value; // NullPointerException
// ✅ 检查 null
Integer value = null;
int primitive = value != null ? value : 0;
// ✅ 使用 Optional
Optional.ofNullable(value).orElse(0);
5.3 循环中的拆装箱
// ❌ 性能差
Long sum = 0L;
for (long i = 0; i < 1000000; i++) {
sum += i; // 每次循环:拆箱 → 运算 → 装箱
}
// ✅ 性能好
long sum = 0L;
for (long i = 0; i < 1000000; i++) {
sum += i; // 无拆装箱
}
5.4 泛型中的陷阱
// ❌ 意外行为
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 删除元素
list.remove(1); // 删除索引 1,不是值 1
// 结果:[1, 3]
// ✅ 删除值
list.remove(Integer.valueOf(1));
// 结果:[2, 3]
5.5 switch 中的装箱
// ❌ 不支持
Integer value = 1;
switch (value) { // 编译错误
case 1: break;
}
// ✅ 支持(自动拆箱)
int primitive = value;
switch (primitive) {
case 1: break;
}
六、最佳实践
6.1 选择建议
// ✅ 计算用基本类型
int sum = 0;
for (int i = 0; i < 10000; i++) {
sum += i;
}
// ✅ 集合用包装类型
List<Integer> list = new ArrayList<>();
// ✅ 数据库用包装类型(允许 null)
public class User {
private Integer age; // 可为 null
}
6.2 比较建议
// ✅ 始终使用 equals
Integer a = 200;
Integer b = 200;
boolean equal = a.equals(b);
// ✅ 或者拆箱比较
boolean equal = a.intValue() == b.intValue();
// ❌ 避免使用 ==
boolean equal = a == b; // 可能 false
6.3 性能优化
// ✅ 循环内用基本类型
long sum = 0;
for (long i = 0; i < 1000000; i++) {
sum += i;
}
// ✅ 预估容量,减少扩容
List<Integer> list = new ArrayList<>(10000);
// ✅ 避免不必要的装箱
int getValue() { return 10; }
// 优于:Integer getValue() { return 10; }
6.4 数据库映射
// ✅ 使用包装类型(允许 null)
public class User {
private Integer age;
private Long id;
private Boolean active;
}
// ❌ 基本类型(不能为 null)
public class User {
private int age; // 不能为 null
private long id; // 不能为 null
private boolean active; // 不能为 null
}
七、总结
包装类型核心要点:
| 类型 | 缓存范围 | 注意事项 |
|---|---|---|
| Integer | -128 到 127 | 比较用 equals |
| Long | -128 到 127 | 比较用 equals |
| Byte | -128 到 127(全部) | 总是相等 |
| Character | 0 到 127 | 中文不缓存 |
| Short | -128 到 127 | 比较用 equals |
包装类型使用要点:计算用基本类型,集合用包装类型,比较用 equals,注意 NPE。