在C中初始化数组极易堕落,并且相当贫苦。C++通过“荟萃初始化”使其更安详(注释⑥)。Java则没有象C++那样的“荟萃”观念,因为Java中的所有对象都是工具。但它确实有本身的数组,通过数组初始化来提供支持。
数组代表一系列工具可能根基数据范例,所有沟通的范例都封装到一起——回收一个统一的标识符名称。数组的界说和利用是通过方括号索引运算符举办的([])。为界说一个数组,只需在范例名后简朴地跟从一对空方括号即可:
int[] al;
也可以将方括号置于标识符后头,得到完全一致的功效:
int al[];
这种名目与C和C++措施员习惯的名目是一致的。然而,最“通顺”的也许照旧前一种语法,因为它指出范例是“一个int数组”。本书将沿用那种名目。
编译器不答允我们汇报它一个数组有多大。这样便使我们回到了“句柄”的问题上。此时,我们拥有的一切就是指向数组的一个句柄,并且尚未给数组分派任何空间。为了给数组建设相应的存储空间,必需编写一个初始化表达式。对付数组,初始化事情可在代码的任那里所呈现,但也可以利用一种非凡的初始化表达式,它必需在数组建设的处所呈现。这种非凡的初始化是一系列由花括号关闭起来的值。存储空间的分派(等价于利用new)将由编译器在这种环境下举办。譬喻:
int[] a1 = { 1, 2, 3, 4, 5 };
那么为什么还要界说一个没有数组的数组句柄呢?
int[] a2;
事实上在Java中,可将一个数组分派给另一个,所以能利用下述语句:
a2 = a1;
我们真正筹备做的是复制一个句柄,就象下面演示的那样:
//: Arrays.java // Arrays of primitives. public class Arrays { public static void main(String[] args) { int[] a1 = { 1, 2, 3, 4, 5 }; int[] a2; a2 = a1; for(int i = 0; i < a2.length; i++) a2[i]++; for(int i = 0; i < a1.length; i++) prt("a1[" + i + "] = " + a1[i]); } static void prt(String s) { System.out.println(s); } } ///:~
各人看到a1得到了一个初始值,而a2没有;a2将在今后赋值——这种环境下是赋给另一个数组。
这里也呈现了一些新对象:所有数组都有一个本质成员(无论它们是工具数组照旧根基范例数组),可对其举办查询——但不是改变,从而获知数组内包括了几多个元素。这个成员就是length。与C和C++雷同,由于Java数组从元素0开始计数,所以能索引的最大元素编号是“length-1”。如超出界线,C和C++会“冷静”地接管,并答允我们胡乱利用本身的内存,这正是很多措施错误的来源。然而,Java可保存我们这受这一问题的损害,要领是一旦高出界线,就生成一个运行期错误(即一个“违例”,这是第9章的主题)。虽然,由于需要查抄每个数组的会见,所以会耗损必然的时间和多余的代码量,并且没有步伐把它封锁。这意味着数组会见大概成为措施效率低下的重要原因——假如它们在要害的场所举办。但思量到因特网会见的安详,以及措施员的编程效率,Java设计人员照旧应该把它看作是值得的。
措施编写期间,假如不知道在本身的数组里需要几多元素,那么又该怎么办呢?此时,只需简朴地用new在数组里建设元素。在这里,纵然筹备建设的是一个根基数据范例的数组,new也能正常地事情(new不会建设非数组的根基范例):
//: ArrayNew.java // Creating arrays with new. import java.util.*; public class ArrayNew { static Random rand = new Random(); static int pRand(int mod) { return Math.abs(rand.nextInt()) % mod + 1; } public static void main(String[] args) { int[] a; a = new int[pRand(20)]; prt("length of a = " + a.length); for(int i = 0; i < a.length; i++) prt("a[" + i + "] = " + a[i]); } static void prt(String s) { System.out.println(s); } } ///:~
由于数组的巨细是随机抉择的(利用早先界说的pRand()要领),所以很是明明,数组的建设实际是在运行期间举办的。除此以外,从这个措施的输出中,各人可看到根基数据范例的数组元素会自动初始化成“空”值(对付数值,空值就是零;对付char,它是null;而对付boolean,它却是false)。
虽然,数组大概已在沟通的语句中界说和初始化了,如下所示:
int[] a = new int[pRand(20)];
若操纵的是一个非根基范例工具的数组,那么无论如何都要利用new。在这里,我们会再一次碰着句柄问题,因为我们建设的是一个句柄数组。请各人调查封装器范例Integer,它是一个类,而非根基数据范例:
//: ArrayClassObj.java // Creating an array of non-primitive objects. import java.util.*; public class ArrayClassObj { static Random rand = new Random(); static int pRand(int mod) { return Math.abs(rand.nextInt()) % mod + 1; } public static void main(String[] args) { Integer[] a = new Integer[pRand(20)]; prt("length of a = " + a.length); for(int i = 0; i < a.length; i++) { a[i] = new Integer(pRand(500)); prt("a[" + i + "] = " + a[i]); } } static void prt(String s) { System.out.println(s); } } ///:~
#p#分页标题#e#
在这儿,甚至在new挪用后才开始建设数组:
Integer[] a = new Integer[pRand(20)];
它只是一个句柄数组,并且除非通过建设一个新的Integer工具,从而初始化了工具句柄,不然初始化历程不会竣事:
a[i] = new Integer(pRand(500));
但若健忘建设工具,就会在运行期试图读取空数组位置时得到一个“违例”错误。
下面让我们看看打印语句中String工具的组成环境。各人可看到指向Integer工具的句柄会自动转换,从而发生一个String,它代表着位于工具内部的值。
亦可用花括号关闭列表来初始化工具数组。可回收两种形式,第一种是Java 1.0答允的独一形式。第二种(等价)形式自Java 1.1才开始提供支持:
//: ArrayInit.java // Array initialization public class ArrayInit { public static void main(String[] args) { Integer[] a = { new Integer(1), new Integer(2), new Integer(3), }; // Java 1.1 only: Integer[] b = new Integer[] { new Integer(1), new Integer(2), new Integer(3), }; } } ///:~
这种做法大大都时候都很有用,但限制也是最大的,因为数组的巨细是在编译期间抉择的。初始化列表的最后一个逗号是可选的(这一特性使长列表的维护变得越发容易)。
数组初始化的第二种形式(Java 1.1开始支持)提供了一种更轻便的语法,可建设和挪用要领,得到与C的“变量参数列表”(C凡是把它简称为“变参表”)一致的结果。这些结果包罗未知的参数(自变量)数量以及未知的范例(假如这样选择的话)。由于所有类最终都是从通用的根类Object中担任的,所以能建设一个要领,令其获取一个Object数组,并象下面这样挪用它:
//: VarArgs.java // Using the Java 1.1 array syntax to create // variable argument lists class A { int i; } public class VarArgs { static void f(Object[] x) { for(int i = 0; i < x.length; i++) System.out.println(x[i]); } public static void main(String[] args) { f(new Object[] { new Integer(47), new VarArgs(), new Float(3.14), new Double(11.11) }); f(new Object[] {"one", "two", "three" }); f(new Object[] {new A(), new A(), new A()}); } } ///:~
此时,我们对这些未知的工具并不能采纳太多的操纵,并且这个措施操作自动String转换对每个Object做一些有用的工作。在第11章(运行期范例标识或RTTI),各人还会进修如何观测这类工具的精确范例,使本身能对它们做一些有趣的工作。