corejava11(5.4 对象包装器和自动装箱)

5.4 对象包装器和自动装箱

有时,您需要将像int这样的基元类型转换为对象。所有基元类型都有类对应。例如,类Integer对应于基元类型int。这些类通常称为包装器(wrappers)。包装器类有明显的名称:Integer、Long、Float、Double、Short、Byte、Character和Boolean。(前六个继承自通用的超类Number。)包装类是不可变的,在构造包装后不能更改包装值。它们也是final的,所以您不能将它们子类化。

假设我们需要一个整数数组列表。不幸的是,尖括号内的类型参数不能是基元类型。不可能形成ArrayList<int>。这里需要Integer包装类。可以声明Integer对象的数组列表。

var list = new ArrayList<Integer>();

小心

arraylist<integer>的效率远低于int[]数组,因为每个值都单独包装在一个对象中。当程序员的便利性比效率更重要时,您只希望将此构造用于小型集合。

幸运的是,有一个非常有用的特性,可以很容易地将int类型的元素添加到ArrayList<Integer>中。调用

list.add(3);

会自动转换到

list.add(Integer.valueOf(3));

这个转换叫做自动装包。

注意

你可能认为自动包装更为一致,但“装包”的比喻取自C#。

相反,当您将一个Integer对象分配给一个int值时,它会自动解包。也就是说,编译器会转换

int n = list.get(i);

int n = list.get(i).intValue();

自动装箱和拆箱甚至可以使用算术表达式。例如,可以将递增运算符应用于包装引用:

Integer n = 3;
n++;

编译器自动插入指令以取消对象的装箱、增加结果值并将其装箱。

在大多数情况下,您会产生一种错觉,即原始类型和它们的包装器是一个相同的类型。只有一点他们有很大的不同:身份。如您所知,==运算符应用于包装对象,只测试对象是否具有相同的内存位置。因此,以下比较可能会失败:

Integer a = 1000;
Integer b = 1000;
if (a == b) . . .

然而,Java实现如果选择,则将通常出现的值封装到相同的对象中,从而比较可能成功。这种模棱两可不是你想要的。补救方法是在比较包装器对象时调用equals方法。

注意

自动装箱规范要求将-128和127之间的boolean、byte、char<=127、short和int包装为固定对象。例如,如果在前面的例子中a和b被初始化为100,那么比较就必然成功。

关于自动装箱还有其他一些微妙之处。首先,由于包装类引用可以为null,因此自动解包可以引发NullPointerException:

Integer n = null;
System.out.println(2 * n); // throws NullPointerException

此外,如果在条件表达式中混合IntegerDouble类型,则Integer将取消装箱、提升为double并装箱为Double

Integer n = 1;
Double x = 2.0;
System.out.println(true ? n : x); // prints 1.0

最后,让我们强调装箱和拆箱是由编译器提供的,而不是虚拟机。编译器在生成类的字节码时插入必要的调用。虚拟机只执行这些字节码。

您经常会看到数字包装器的另一个原因。Java设计者发现包装器是放置某些基本方法的便利场所,例如将数字串转换为数字的方法。

要将字符串转换为整数,请使用以下语句:

int x = Integer.parseInt(s);

这与Integer对象无关。parseInt是一个静态方法。但是Integer类是一个很好的地方。

API注释显示了Integer类的一些更重要的方法。其他数字类实现相应的方法。

小心

有些人认为包装器类可以用来实现可以修改数字参数的方法。但是,这是不正确的。回想第4章,不可能编写一个增加整数参数的Java方法,因为参数总是通过值传递给Java方法。

public static void triple(int x) // won't work
{
	x = 3 * x; // modifies local variable
}

我们可以用Integer而不是int来克服这个问题吗?

public static void triple(Integer x) // won't work
{
	. . .
}

问题是Integer对象是不可变的:包装器中包含的信息无法更改。不能使用这些包装类创建修改数字参数的方法。

If you really want to write a method to change numeric parameters,
you can use one of the holder types defined in the
org.omg.CORBA package: IntHolder, BooleanHolder, and

so on. Each holder type has a public (!) field value through which
you can access the stored value.

如果您真的想编写一个方法来更改数字参数,那么可以使用org.omg.CORBA包中定义的holder类型之一:IntHholder、BooleanHolder等等。每个持有者类型都有一个公共字段value,通过该值可以访问存储值。

public static void triple(IntHolder x)
{
	x.value = 3 * x.value;
}

java.lang.Integer 1.0

  • int intValue()
    以int形式返回此整数对象的值(重写Number类中的IntValue方法)。
  • static String toString(int i)
    返回表示以10为基数的数字i的新String对象。
  • static String toString(int i, int radix)
    允许返回radix参数指定的基数中数字i的表示形式。
  • static int parseInt(String s)
  • static int parseInt(String s, int radix)
    返回包含在字符串s中的整数。字符串必须表示以10为底(对于第一个方法)或以radix参数给定的基数(对于第二个方法)表示的整数。

java.text.NumberFormat 1.1

  • Number parse(String s)
    返回数值,假定指定的String代表数字。

猜你喜欢

转载自blog.csdn.net/nbda1121440/article/details/90667878
今日推荐