5.4 对象包装器与自动装箱
有时 需要将 int 这样的基本类型转换成对象.所有的基本类型都有一个与之对应的类.例如,基本类型 int 对应Integer类,这些类称为 包装器(wrapper).这些对象包装器类拥有很鲜明的名字:Integer,Long,Float,Double,Short,Byte,Character,Void和Boolean(前6个类派生于公共的超类Number).对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值.同时,对象包装器还是 final,因此不能定义它们的子类.假想定义一个整型数组列表,而尖括号中的类型参数不允许是基本类型,也就是说不允许写成ArrayList<int>,这里就用到了Integer对象包装器类.可以声明一个Integer对象的数组列表.
ArrayList<Integer> list = new ArrayList<>();
警告:由于每个值分别包装在对象中,所以ArrayList<Integer>的效率远远低于int[]数组,因此,应该用它构造小型集合,其原因是此时程序员操作的方便性要比执行效率更加重要.
相反地,当将一个Integer对象赋给一个 int 值时,就会自动地拆箱.也就是说,编译器将下列语句:
int n = list.get(i);
翻译成
int n = list.get(i).intValue();
甚至在算数表达式中也能够自动地装箱和拆箱.
在很多情况下,容易有一种假象,即基本类型与它们的对象包装器是一样的,只是它们的相等性不同.==运算符可以应用于对象包装器对象,只不过检测的是对象是否指向同一个存储区域,因此,下面的比较比较通畅不会成立:
Integer a = 1000;
Integer b = 1000;
if (a == b) ...
解决这个问题的办法是在两个包装器对象比较时调用equals方法.使用数值对象包装器还有另外一个好处.Java设计者发现,可以将某些基本方法放置在包装器中.例如,将一个数字字符串转换成数值.
5.5 参数数量可变的方法
前面看到过这样的方法:printf .例如,下面的方法调用:System.out.printf("%d", n);
和
System.out.printf("%d %s", n, "widgets");
在上面两条语句中,尽管一个调用包含两个参数,另一个调用包含三个参数,但它们调用的都是同一个方法,printf方法是这样定义的:
public class PrintStream
{
public PrintStream printf(String fmt, Object ... args) { return format(fmt, args); }
}
实际上,printf接收两个参数,一个是格式字符串,另一个是Object[]数组,其中保存着所有的参数.