Java中数值类型的自动装箱拆箱

695 阅读3分钟

注:本专栏文章均为本人原创,未经本人授权请勿私自转载,谢谢。

自动装箱就是自动将基本数据类型转换为包装器类型,而自动拆箱就是自动将包装器类型转换为基本数据类型。

// 自动装箱,相当于 Integer number1 = Integer.valueOf(1000);
Integer number1 = 1000;
​
// 自动拆箱,相当于 int number2 = number1.intValue();
int number2 = number1;  

装箱过程是通过调用包装器的 valueOf()  方法实现的,而拆箱是通过调用包装器的 xxxValue()  方法实现的。

注意:在装箱调用 valueOf()  方法时,包装器类型中会有一段区间内的小数缓存(Boolean 定义了两个静态常量当做缓存;Byte、Character、Short、Integer、Long 具有一定范围的小数缓存;Double、Float 无缓存),在这种情况下,两个不同数值的装箱操作获得的结果为同一对象引用。

equals(Object) 和 == 方法中的装箱与拆箱

  • equals(Object) 方法参数限定必须为 Object 对象,而基本数据类型不是 Object 的子类,故当参数为基本数据类型时,会触发装箱操作传入该基本类型对应的包装类
    需要注意的一点是,在每个数值类型 equals(Object) 方法的第一行都是判定传入类型是否与当前包装类类型一致,若不一致,即使值相等也会返回 false。即 Long.valueOf(100).equals(100) 返回值为 false。
  • == 运算符两边都是包装类时比较的是它们是否指向同一个引用,而如果其中有一个操作数是表达式(包含算术运算),会触发自动拆箱比较两者的数值

以下为 equals(Object) 方法装箱操作的测试代码:

@Test
public void test_boxing() {
    Integer num1 = 128;
    int num2 = 128;
    System.out.println(num1.equals(num2)); // true
​
    Long num3 = 128L;
    System.out.println(num3.equals(num1)); // false
    System.out.println(num3.equals(num2)); // false
}

以下为 == 方法拆箱操作的测试代码:

@Test
public void test_unboxing() {
    // 两边都是同类型包装类,不拆箱,比较的是引用,命中数值缓存,所以返回 true
    Integer num1 = 127;
    Integer num2 = 127;
    System.out.println(num1 == num2);
​
    // 两边都是同类型包装类,不拆箱,比较的是引用,但没命中数值缓存,所以返回 false
    Integer num3 = 128;
    Integer num4 = 128;
    System.out.println(num3 == num4);
​
    // 不同类型的对象无法直接使用 == 进行比较
    // Long num5 = 127L;
    // System.out.println(num1 == num5);
​
    // 对于不同包装类间的比较,尝试使用算数运算避开编译期的类型检查。
    // 查看字节码后可以看到,这样相当于将 num3 和 num4 分别拆箱加和之后,将结果提升为 long 类型再与 num6 进行比较。
    Long num6 = 256L;
    System.out.println(num6 == num3 + num4); // 等价于 System.out.println(num5 == (long)(num3 + num4));
}

算数运算符的拆箱

加减乘除运算符都是为基本数据类型而服务的,所以包装类使用这些算数操作也必须进行拆箱:

@Test
public void test_math() {
    Integer num1 = 128;
    Integer num2 = 128;
    Integer num3 = 256;
    // 两边都是同类型包装类,不拆箱,比较的是引用,但没命中数值缓存,所以返回 false
    System.out.println(num1 == num2);        // false
    // num1+num2 算数运算触发拆箱,结果为 int 类型的 256,然后再跟 num3 比又拆箱,所以返回 true
    System.out.println(num3 == num1 + num2); // true
}

注意

最好不要将包装类作为 synchronized() 的 object 来使用,因为缓存可能会带来意想不到的后果。