Skip to content
清晨的一缕阳光
返回

Java 包装类型详解

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(全部)总是相等
Character0 到 127中文不缓存
Short-128 到 127比较用 equals

包装类型使用要点:计算用基本类型,集合用包装类型,比较用 equals,注意 NPE。


分享这篇文章到:

上一篇文章
MySQL 锁机制详解
下一篇文章
事务隔离级别与 MVCC