动态数组创建
如果在编写程序时,不确定数组中需要多少个元素,那么该怎么办呢?你可以直接使用 new 在数组中创建元素。下面例子中,尽管创建的是基本类型数组,new 仍然可以工作(不能用 new 创建单个的基本类型数组):
// housekeeping/ArrayNew.java // Creating arrays with new import java.util.*; public class ArrayNew { public static void main(String[] args) { int[] a; Random rand = new Random(47); a = new int[rand.nextInt(20)]; System.out.println("length of a = " + a.length); System.out.println(Arrays.toString(a)); } }
输出:
length of a = 18 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
数组的大小是通过 Random.nextInt()随机确定的,这个方法会返回 0 到输入参数之间的一个值。 由于随机性,很明显数组的创建确实是在运行时进行的。此外,程序输出表明,数组元素中的基本数据类型值会自动初始化为空值(对于数字和字符是 0;对于布尔型是 false)。Arrays.toString() 是 java.util 标准类库中的方法,会产生一维数组的可打印版本。
本例中,数组也可以在定义的同时进行初始化:
int[] a = new int[rand.nextInt(20)];
如果可能的话,应该尽量这么做。
如果你创建了一个非基本类型的数组,那么你创建的是一个引用数组。以整型的包装类型 Integer 为例,它是一个类而非基本类型:
// housekeeping/ArrayClassObj.java // Creating an array of nonprimitive objects import java.util.*; public class ArrayClassObj { public static void main(String[] args) { Random rand = new Random(47); Integer[] a = new Integer[rand.nextInt(20)]; System.out.println("length of a = " + a.length); for (int i = 0; i < a.length; i++) { a[i] = rand.nextInt(500); // Autoboxing } System.out.println(Arrays.toString(a)); } }
输出:
length of a = 18 [55, 193, 361, 461, 429, 368, 200, 22, 207, 288, 128, 51, 89, 309, 278, 498, 361, 20]
这里,即使使用 new 创建数组之后:
Integer[] a = new Integer[rand.nextInt(20)];
它只是一个引用数组,直到通过创建新的 Integer 对象(通过自动装箱),并把对象赋值给引用,初始化才算结束:
a[i] = rand.nextInt(500);
如果忘记了创建对象,但试图使用数组中的空引用,就会在运行时产生异常。
也可以用花括号括起来的列表来初始化数组,有两种形式:
// housekeeping/ArrayInit.java // Array initialization import java.util.*; public class ArrayInit { public static void main(String[] args) { Integer[] a = { 1, 2, 3, // Autoboxing }; Integer[] b = new Integer[] { 1, 2, 3, // Autoboxing }; System.out.println(Arrays.toString(a)); System.out.println(Arrays.toString(b)); } }
输出:
[1, 2, 3] [1, 2, 3]
在这两种形式中,初始化列表的最后一个逗号是可选的(这一特性使维护长列表变得更容易)。
尽管第一种形式很有用,但是它更加受限,因为它只能用于数组定义处。第二种和第三种形式可以用在任何地方,甚至用在方法的内部。例如,你创建了一个 String 数组,将其传递给另一个类的 main() 方法,如下:
// housekeeping/DynamicArray.java // Array initialization public class DynamicArray { public static void main(String[] args) { Other.main(new String[] {"fiddle", "de", "dum"}); } } class Other { public static void main(String[] args) { for (String s: args) { System.out.print(s + " "); } } }
输出:
fiddle de dum
Other.main()的参数是在调用处创建的,因此你甚至可以在方法调用处提供可替换的参数。
可变参数列表
类似 C 语言的可变参数列表(C 通常把它称为"varargs")来创建和调用方法。
这可应用在参数个数或类型未知的场景。由于所有类都最后继承自 Object 类,所以你可以创建一个以 Object 数组为参数的方法,类似如下调用:
// housekeeping/VarArgs.java // Using array syntax to create variable argument lists class A {} public class VarArgs { static void printArray(Object[] args) { for (Object obj: args) { System.out.print(obj + " "); } System.out.println(); } public static void main(String[] args) { printArray(new Object[] {47, (float) 3.14, 11.11}); printArray(new Object[] {"one", "two", "three"}); printArray(new Object[] {new A(), new A(), new A()}); } }
输出:
47 3.14 11.11 one two three A@15db9742 A@6d06d69c A@7852e922
printArray() 的参数是 Object 数组,使用 for-in 语法遍历打印。标准 Java 库能输出有意义的内容,但这里创建的是类的对象,打印出的内容是类名,后面跟着一个 @ 符号以及多个十六进制数字。因而,默认行为(若未重写toString())就是打印类名和对象的地址。
你可能看到像上面这样编写的 Java 5 前的代码,可产生可变的参数列表。
Java 5 中,这种特性终于添加了进来,就像在 printArray() 中看到的那样:
// housekeeping/NewVarArgs.java // Using array syntax to create variable argument lists public class NewVarArgs { static void printArray(Object... args) { for (Object obj: args) { System.out.print(obj + " "); } System.out.println(); } public static void main(String[] args) { // Can take individual elements: printArray(47, (float) 3.14, 11.11); printArray(47, 3.14F, 11.11); printArray("one", "two", "three"); printArray(new A(), new A(), new A()); // Or an array: printArray((Object[]) new Integer[] {1, 2, 3, 4}); printArray(); // Empty list is OK } }
输出:
47 3.14 11.11 47 3.14 11.11 one two three A@15db9742 A@6d06d69c A@7852e922 1 2 3 4
有了可变参数,你就不用再显式地编写数组语法,当你指定参数时,编译器实际上会为你填充数组。你获取的仍然是一个数组,这就是为什么 printArray() 可使用 for-in 迭代数组。
但这不仅仅只是从元素列表到数组的自动转换。注意程序的倒数第二行,一个 Integer 数组(通过自动装箱创建)被转型为一个 Object 数组(为了移除编译器的警告),并传递给了 printArray()。显然,编译器会发现这是一个数组,不会执行转换。因此,如果你有一组事物,可以把它们当作列表传递,而如果你已经有了一个数组,该方法会把它们当作可变参数列表来接受。
程序的最后一行表明,可变参数的个数可以为 0。当具有可选的尾随参数时,这一特性会有帮助:
// housekeeping/OptionalTrailingArguments.java public class OptionalTrailingArguments { static void f(int required, String... trailing) { System.out.print("required: " + required + " "); for (String s: trailing) { System.out.print(s + " "); } System.out.println(); } public static void main(String[] args) { f(1, "one"); f(2, "two", "three"); f(0); } }
输出:
required: 1 one required: 2 two three required: 0
这段程序展示了如何使用除了 Object 类之外类型的可变参数列表。这里,所有的可变参数都是 String 对象。可变参数列表中可以使用任何类型的参数,包括基本类型。下面例子展示了可变参数列表变为数组的情形,并且如果列表中没有任何元素,那么转变为大小为 0 的数组: