Skip to content

Latest commit

 

History

History
88 lines (76 loc) · 4.57 KB

02_Boxing_and_Unboxing.md

File metadata and controls

88 lines (76 loc) · 4.57 KB

《《《 返回首页
《《《 上一节

装箱与拆箱

  • 回想一下,Java中的每种类型都是引用类型或基本类型。 引用类型是任何类,接口或数组类型。 所有引用类型都是Object类的子类型, 任何引用类型的变量都可以设置为null。 如下表所示,有八种基本类型,每种都有相应的类库的引用类型。 类库位于java.lang包:

    原始类型 引用类型
    byte Byte
    short Short
    int Integer
    long Long
    float Float
    double Double
    boolean Boolean
    char Character
  • 将原始类型转换为相应的引用类型称为装箱,并将引用类型转换为相应的原始类型称为拆箱。

  • 带泛型的Java在适当的地方自动装箱和拆箱。 如果int类型的表达式E出现在需要Integer类型值的地方, 那么装箱会将其转换为新的Integer(e)(但是,它可能会缓存经常出现的值)。 如果Integer类型的表达式E出现在期望int类型的值处, 则拆箱将其转换为表达式e.intValue()。例如,序列

      List<Integer> ints = new ArrayList<Integer>();
      ints.add(1);
      int n = ints.get(0);

    相当于序列:

      List<Integer> ints = new ArrayList<Integer>();
      ints.add(Integer.valueOf(1));
      int n = ints.get(0).intValue();
  • 调用Integer.valueOf(1)与新的Integer(1)表达式类似,但是可能会缓存一些值以提高性能,正如我们稍后解释的那样。

  • 在这里,再次,是代码来找到一个整数列表的总和,方便打包为一个静态的方法:

      public static int sum (List<Integer> ints) {
      int s = 0;
      for (int n : ints) { s += n; }
      return s;
      }
  • 为什么参数的类型是List<Integer>而不是List<int>? 因为类型参数必须始终绑定到引用类型,而不是基本类型。 为什么结果有类型int和不是Integer? 因为结果类型可能是原始类型或引用类型,使用前者比后者更有效。 拆箱在列表中的每个整数都绑定到int类型的变量n时发生。

  • 我们可以重写这个方法,用Integer来替换每个int的出现:

      public static Integer sumInteger(List<Integer> ints) {
        Integer s = 0;
        for (Integer n : ints) { s += n; }
        return s;
      }
  • 此代码编译,但执行了很多不必要的工作。 循环的每个迭代将sn中的值拆箱,执行加法操作,然后再次结束结果。 使用Sun当前的编译器,测量结果显示该版本大约为比原来慢60%。

  • 注意这个! 装箱和拆箱的一个微妙之处在于==在原语和参考类型上的定义是不同的。 在int类型上,它是由值相等来定义的, 而Integer类型则由对象标识来定义。 所以下面的两个断言使用SunJVM成功:

      List<Integer> bigs = Arrays.asList(100,200,300);
      assert sumInteger(bigs) == sum(bigs);
      assert sumInteger(bigs) != sumInteger(bigs); // not recommended
  • 在第一个断言中,拆箱会导致值进行比较,所以结果是相同的。在第二个断言中,没有拆箱,两个方法调用返回不同整数对象, 所以即使两个整数对象都表示结果也是不相等的相同的值,600。我们建议您不要使用==来比较类型的值Integer。 要么首先取消装箱,所以==比较int类型的值,否则使用equals比较Integer类型的值。

  • 更精妙的是盒装值可以被缓存。 装箱时需要缓存一个介于-128127之间的intshort值, 一个介于'\ u0000'和之间的char值'\ u007f',一个字节或一个布尔值; 并在装箱时允许进行缓存。 因此,与我们之前的例子相比,我们有以下几点:

      List<Integer> smalls = Arrays.asList(1,2,3);
      assert sumInteger(smalls) == sum(smalls);
      assert sumInteger(smalls) == sumInteger(smalls); // not recommended
  • 这是因为6小于128,所以装箱数值6总是返回正确的同一个对象。 一般来说, 没有规定两次是否装箱相同的值返回相同或不同的对象,所以前面显示的不等式断言也可以取决于实施失败或成功。 即使是小的值,为什么==将正确比较Integer类型的值,我们建议不要使用它。 它更清晰和更清洁使用equals而不是==来比较引用类型的值,例如IntegerString

《《《 下一节
《《《 返回首页