包装类(wrappers)

type byte short int long float double char boolean
wrapper Byte Short Integer Long Float Double Character Boolean
继承自 Number
实现了 SerializableComparable 接口
  • 所有包装类都是 immutable 的(不可修改,不可继承)。
  • 包装类应该使用 equals() 方法判断是否相等。
  • 包装类可以为 null,但自动拆箱时会抛出 NullPointerException.

语法糖

  1. Integer integer = 1; // 装箱
  2. int i = integer; // 拆箱

对以上代码进行反编译,结果如下所示:

  1. Integer integer = Integer.valueOf(1);
  2. int i = integer.intValue();

从上面反编译的代码可以看出,int 类型的自动装箱都是通过 Integer.valueOf() 方法来实现的,Integer 的自动拆箱都是通过 Integer.intValue 来实现的。

事实上,自动装箱都是通过包装类的 valueOf() 方法实现的,自动拆箱都是通过包装类的 xxxValue() 方法实现的。

缓存机制

ByteShortIntegerLongCharacter 五个包装类内,都有一个静态内部类,用于维护一个包装类对象缓存数组。当对应的基本数据类型在自动装箱时,会先判断对应的包装类是否在缓存数组内,如果在,则复用该数组缓存的对象,否则生成一个新的对象。

下面以 Integer 类为例,展示相应的源码:

  1. public final class Integer extends Number implements Comparable<Integer> {
  2. // 静态内部类
  3. private static class IntegerCache {
  4. static final Integer cache[]; // 缓存数组
  5. ...
  6. }
  7. // 自动装箱
  8. public static Integer valueOf(int i) {
  9. if (i >= IntegerCache.low && i <= IntegerCache.high)
  10. return IntegerCache.cache[i + (-IntegerCache.low)];
  11. return new Integer(i);
  12. }
  13. }

Boolean 也有类似的缓存机制,但和上述提到的 5 个包装类不同的是, Boolean 并无静态内部类和缓存数组,而是直接定义了两个静态常量,并在自动装箱时进行判断,源码如下所示:

  1. public static final Boolean TRUE = new Boolean(true);
  2. public static final Boolean FALSE = new Boolean(false);
  3. public static Boolean valueOf(boolean b) {
  4. return (b ? TRUE : FALSE);
  5. }

FloatDouble 无类似的缓存机制。各包装类的缓存范围如下表所示:

包装类 缓存范围
Byte ALL
Short -128 - 127
Integer -128 - 127
Long -128 - 127
Character \u0000 - \u007F(0 - 127)
Boolean ALL
Float
Double

需要再次强调的是:只有在自动装箱时(即调用 valueOf() 方法),相应的缓存机制才会生效,通过 new 关键字会创建新的包装类对象。

  1. Integer integer1 = new Integer(1);
  2. Integer integer2 = new Integer(1);
  3. System.out.println(integer1 == integer2); // false
  4. Integer integer3 = 1;
  5. Integer integer4 = 1;
  6. System.out.println(integer3 == integer4); // true

包装类与基本数据类型的选择

《阿里巴巴 Java 开发手册》有以下规定:

  • 【强制】所有的 POJO 类属性必须为包装数据类型
  • 【强制】RPC 方法的返回值和参数必须使用包装数据类型
  • 【推荐】所有的局部变量使用基本数据类型