第六章.数组

一:数组概述

6.1 数组概述
在C语言的学习中,我们已经知道:数组是存储在一个连续的内存块中的元素集合。数组中的每个元素必须是相同的数据类型,并且通过索引进行区分。数组中的第一个元素的索引为0。
在Java中,创建数组有两个步骤:
 声明一个对数组的引用;
 使用new关键字初始化数组,并指定数组的大小。
在Java中,数据要么是基础数据类型的,要么是一个引用类型。而数组不是八种基础数据类型之一,所以数组是引用类型。所以,一个数组需要一个引用来访问它,并且需要new关键字来初始化。同一个数组中的所有元素是同一种类型的数据类型。
要声明一个数组引用,可以用中括号。例如,如下的语句声明了一个对int类型数组的引用:
int [] sums;
在Java中,声明数组时,我们也可以将括号放在数组名后。例如:
int sums [];
这是为了保持对C和C++的兼容性。我们推荐将括号放在数组名前面,因为这样可以使用引用声明更清晰。
在上述的数组引用声明后,sums引用可以指向任何int类型的数组,不管数组中有多少元素。因为sums是一个引用,它也可以赋值为null。
数组的大小在数组实例化时决定。将一个整数放在中括号中来指定数组的大小。例如,如下的语句将sums赋值为一个有20个元素的新数组:
sums = new int[20];
因为数组必须在连续的内存空间中,所以在内存初始化分配后,数组的大小就不能改变了。如果数组的大小20太小了,那么就必须初始化一个新的更大的数组,老的数组就被垃圾回收了。例如,如下的语句将sums赋值为一个有30个元素的更大的数组。
sums = new int[30];
如果有20个元素的数组不再在程序中被引用,那么该数组就被垃圾回收。
将sums赋值为20个int类型元素的数组,然后赋值为有30个int类型元素的数组,演示sums可以指向任何大小的int类型数组。
我们可以声明一个数组类型的引用,并且在同一语句中初始化该数组对象。如下的语句声明了一个对double类型数组的引用temps,temps在同一语句中被赋值为一个新的有31个元素的double类型数组:
double [] temps = new double[31];合并后的,等同于Scanner,类这些。
与sums类似,temp可以指向任何大小的double类型数组。现在,它指向的是有31个元素的double类型数组。
因为数组对象是使用new关键字初始化的,在数组对象分配内存空间后,数组元素被初始化。搜易,数组元素的初始值将是默认值(见表4-5)。例如,temps数组中的31个double类型的元素的初始值是0.0,sums数组中的30个int类型的元素的初始值为0。
6.2 访问数组
数组中的元素通过用对该数组的一个引用、一个用于描述我们要访问数组哪一个元素的整型索引值来访问。数组的第一个元素的索引为0,第二元素的索引为1,依此类推。
例如,如下的语句声明了一个有20个整型元素的数组,并给分别给元素赋值,第一个元素是1,第二个元素是2,最后一个元素是191:
int [] sums = new int[20];
sums[0] = 1;
sums[1] = 2;
sums[19] = 191;
为了给20个元素赋值,就需要20条语句。显然,用for循环赋值更方便。例如:
sums[0] = 1;
for(int i = 1; i < 20; i++){
sums[i] = sums[i-1] + i;
}
数组sums的第一个元素是1,然后用for循环初始化剩下的19个元素。
6.3 数组长度
如果数组sums的大小为20:
int [] sums = int[20];
sums的最后一个元素的索引为19。如果使用索引20,虽然编译器会允许编译,但是它是无效的。所以,我们要特别小心,因为如下的语句是可以通过编译的:
sums[20] = 211;
但是,在运行时这条语句就会导致一个ArrayIndexOutOfBoundsException异常发生。Java语言的数组与其它语言的数组的不同之处在于:Java中的数组是对象。这样做的好处之一为:Java中的每个数组都有一个length属性来代表数组的大小。
使用length属性可以极大地减少数组访问越界的可能性。例如,如下的for循环通过使用length属性作为循环控制变量的上限,打印出数组sums中的元素:
for(int i = 0; i < sums.length; i++) {
System.out.println(“sums[” + i + "] = " + sums[i]);
}
我们可以注意到for循环中的布尔表达式用“小于” sums.length,而不是“小于或等于”sums.length。这是因为sums的大小是20,所以sums.length的值为20。如果我们用“小于或等于”,就会在循环中访问sums[20] ,从而导致异常发生。这是编程中常会犯的错误之一。
6.4 引用数组
Java中有九种类型的数组:八种基础数据类型中每一种是一种数组类型、引用类型的数组类型。sums和temps数组就是基础数据类型数组的示例。我们可以声明任何引用类型的数组。例如,如下的语句声明了一个Employee(他是一个类)类型的引用数组:
Employee [] myCompany;
这样,变量myCompany就可以指向任何Employee类型的引用数组。
如下的语句将一个新的有500个元素的Employee类型引用数组赋值给myCompany:
myCompany = new Employee[500];
该数组有500个Employee类型的引用,每个都初始化为null。
数组myCompany中的元素可以和基础数据类型数组一样,通过索引来访问。例如,如下的语句将一个新的Employee对象赋值给myCompany的第228个元素:
myCompany[227] = new Employee(“George Washington”, “Mount Vernon”, 1);
要调用该Employee对象上的方法,我们要一起使用索引和点运算符。假如Employee类有一个mailCheck()方法,那么如下的语句将调用myCompany数组中第228个元素的该方法:
myCompany[227].mailCheck();
为演示数组引用的用法,我们将使用代码清单6.1所示的Employee类,为Employee对象的集合创建一个数组:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19: 20: 21: /*代码清单6.1 Employee.java
Employee类代表公司中的员工
*/

public class Employee {
public String name;
public String address;
public int number;

public Employee(String name, String address, int number) {
	System.out.println("正在创建一个Employee对象");
	this.name = name;
	this.address = address;
	this.number = number;
}

public void mailCheck() {
	System.out.println("邮寄支票给:" + this.name + ",地址为:" + this.address);
}

}
代码清单6.2所示的ArrayDemo程序实例化并使用了几个数组。研究程序,并判断其输出。
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26: 27: 28: /*代码清单6.2 ArrayDemo.java
演示数组的使用
*/

public class ArrayDemo {
public static void main(String[] args) {
int[] sums;
sums = new int[20];
sums[0] = 1;
for (int i = 1; i < 20; i++) {
sums[i] = sums[i - 1] + i;
}
for (int i = 0; i < sums.length; i++) {
System.out.println(“sums[” + i + "] = " + sums[i]);
}
System.out.println(sums.toString());
double[] temps = new double[31];
temps[0] = 85.0;
temps[1] = 712.5;
temps[2] = 76.0;
Employee[] myCompany;
myCompany = new Employee[500];
myCompany[227] = new Employee(“张三”, “东大街八号”, 1);
myCompany[227].mailCheck();
System.out.println(“数组myCompany的长度为:” + myCompany.length);
System.out.println(temps[31]);
}
}
运行ArrayDemo程序,输出结果如图6.1所示。
关于ArrayDemo程序,我们需要指出的是:
 因为数组都是对象,其类类型继承自java.lang.object。这意味着在数组上我们可以调用Object的任何方法。在ArrayDemo程序中,sums数组调用了toString()方法。
 语句“正在创建一个Employee对象”只打印了一次,表明在内存中只创建了一个Employee对象。
 main()方法中最后一条语句访问temps[31],能通过编译,但是会导致一个如图6.1输出中所示的ArrayIndexOutOfBoundsException(数组索引越界)异常。

图6.1 ArrayDemo程序的输出
6.5 数组初始化
在Java中,我们可以用一条语句声明一个数组引用,实例化一个数组,并且填充数组元素。这个过程称为数组初始化,它对创建一个包含已知数据的小数组是很有用的。
数组初始化程序在创建数组时,不需要使用new关键字。数组中的元素使用大括号列出,元素和元素之间用逗号分隔开。例如,如下的数组初始化程序创建了一个有五个int类型元素的数组:
int [] odds = {1, 3, 5, 7, 9};
odds的第一个元素是1,第二个元素是3,以此类推。注意在右大括号后必须有一个分号。
代码清单6.3所示的ArrayInitDemo程序用于演示数组初始化程序。研究该程序,并判断其输出。最终输出结果如图6.2所示。
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24: 25: 26: /* 代码清单6.3 ArrayInitDemo.java
演示数组初始化
*/

public class ArrayInitDemo {
public static void main(String[] args) {
int[] odds = { 1, 3, 5, 7, 9 };
System.out.println("odds.length = " + odds.length);
for (int i = 0; i < odds.length; i++) {
System.out.println(“odds[” + i + “] = " + odds[i]);
}
String[] daysOfWeek = { “星期六”, “星期天”, “星期一”, “星期二”, “星期三”, “星期四”, “星期五” };
System.out.println(”\ndaysOfWeek.length = " + daysOfWeek.length);
for (int i = 0; i < daysOfWeek.length; i++) {
System.out.println(“daysOfWeek[” + i + “] = " + daysOfWeek[i]);
}
Employee[] employees = { new Employee(“李四”, “武侯祠大街1号”, 1),
new Employee(“王二”, “胜利街3号”, 2),
new Employee(“伍一”, “滨河路1号”, 3) };
System.out.println(”\nemployees.length = " + employees.length);
for (int i = 0; i < employees.length; i++) {
employees[i].mailCheck();
}
}
}

图6.2 ArrayInitDemo程序的输出
值得注意的是:数组初始化程序只能用在声明新数组引用时。例如,如下的语句是一个有效的数组初始化程序的用法:
String [] weekend = {“星期六”, “星期天”};
但是,在一条语句中声明引用,而在另一条语句中将引用赋值给一个初始化程序是无效的:
String [] weekend;
weekend = {“星期六”, “星期天”}; //编译不能通过
数组初始化程序只能在将其赋值给一个新声明的数组引用时使用,并且必须在一条语句内完成。
6.6 数组复制
因为数组的大小是固定的,所以在使用数组时,经常不得不创建一个更大或更小的数组,然后将已存在的数组的内容复制到新的数组中。我们可以编写一个for循环将一个数组的内容复制给另一个,也可以用System类中的静态方法arraycopy()。
arraycopy()方法的签名如下:
public static void arraycopy(Object source, int sourcePos, Object destination, int destinationPos, int length)
sourcePose指示要复制的源数组,而destinationPos指示要复制给哪个目标数组。length参数代表要复制的元素的数目。
代码清单6.4所示的ArrayCopyDemo程序演示了arraycopy()方法的使用,在程序中我们将10个整数从一个数组复制到另外一个数组。研究该程序,并判断其输出。最终输出如图6.3所示。
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15: 16: 17: 18: 19: /代码清单6.4 ArrayCopyDemo.java
演示数组的复制
/
public class ArrayCopyDemo {
public static void main(String[] args) {
int[] odds = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
System.out.println("
第一次输出 ");
for (int i = 0; i < odds.length; i++) {
System.out.println(“odds[” + i + “] = " + odds[i]);
}
System.out.println(”
第二次输出 **");
int[] temp = odds;
odds = new int[20];
System.arraycopy(temp, 0, odds, 4, temp.length);
for (int i = 0; i < odds.length; i++) {
System.out.println(“odds[” + i + "] = " + odds[i]);
}
}
}
在ArrayCopyDemo程序中,通过如下语句,odds引用被赋值给temp引用:
int [] temp = odds;
在这个赋值运算时,odds指向一个有10个整型元素的数组。因此,temp也指向该数组。此后,odds被赋值为一个有20个整型元素的数组,该数组的所有元素初始都为0。
temp中的前10个元素,这也是temp中的全部元素,被复制到一个有20个int类型元素的新数组odds中,复制过来的10个元素在新数组中的起点索引是4。观察图6.3的输出,我们可以看到odds中的其它元素都是0,因为还没有给它们赋值。

图6.3 ArrayCopyDemo程序的输出
6.7 多维数组
以上讨论的数组都是一维数组。在Java中,我们还可以创建多维的数组。与一维数组一样,多维数组也是对象,需要一个引用。该引用是在数据类型和变量名之间用多个中括号来声明的。例如,如下的语句声明了一个对一个二维int类型数组的引用:
int [] [] sums;
当初始化二维数组时,必须用两个int类型整数来指定行数和列数。例如,如下的语句将sums赋值为一个新的10X12个int类型元素的数组:
sums = new int[10][12];
这个数组有120个int类型元素,因为在内存中有10个含有12个元素的int类型数组。引用sums指向一个大小为10的数组,每个数组又包含10个int类型的数组引用。10个int类型的数组引用中的每一个又指向一个有12个int类型元素的数组,所以该数组就有120个元素。
如下的是一个三维数组引用:
String [] [] [] dims;
初始化该数组需要三个值,每个值用于一个维数,例如:
dims = new String[5][5][4];
这样dims数组有包含5 * 5 * 4 = 100个String对象。
在一个二维数组中的每一个元素需要两个索引值来访问。在sums数组中,第一个索引值在0和9之间,第二个索引值在0和11之间。如下的语句将值5赋给sums的第四行第三列的元素:
sums[3][2] = 5;
使用二维数组通常会要用到嵌套循环。代码清单6.5所示的DoubleArray程序用一个嵌套循环填充sums数组的值。研究该程序,并判断其输出。最终结果如图6.4所示。
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: 21: 22: /* 代码清单6.5 DoubleArray.java
演示多维数组
*/
public class DoubleArray {
public static void main(String[] args) {
System.out.println(“初始化一个二维数组”);
int[][] sums = new int[10][12];
System.out.println(“用数据填充数组”);
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 12; col++) {
sums[row][col] = row + col;
}
}
System.out.println(“显示数组元素”);
for (int row = 0; row < sums.length; row++) {
for (int col = 0; col < sums[row].length; col++) {
System.out.print(sums[row][col] + “\t”);
}
System.out.println();
}
}
}

图6.4 DoubleArray程序的输出
在DoubleArray程序中,第一个嵌套for循环分别使用10和12做为行循环和列循环的循环控制变量。虽然这样做没有太大问题,但是我们最好利用length属性。第二嵌套for循环用sums.length作为行循环控制变量,使用sums[row].length作为列循环控制变量。
6.8 Java文档工具javaDoc
Java语言一个独特且特别有用的功能就是javadoc工具,使用该工具,可以获取Java源代码中的注释,并生成HTML页面。这就鼓励开发者在代码中添加注释,因为产生的HTML网页可以让同事和其他需要了解一个类的人所用,并且不需要看实际的源代码。
Java SE API的文档就是使用javadoc工具生成的。该文档可以在Sun的网站http://java.sun.com/javase上找到。我们可以下载该文档,也可以在线浏览。
运行javadoc工具可以为每个类创建HTML页面,页面中包含有类的详细信息。类页面的信息包括类的继承层次、字段的摘要、构造器和方法,以及每个字段、构造器和方法的详细描述。
图6.6展示的是java.lang.String类的javadoc页部分。

图6.6 String类的文档页面
Java SE文档分为三个帧。左上角的帧列出Java SE中所有的包。点击包名,该包中所有的接口和类就会出现在左下角的帧中。点击类名,类的文档页就出现在右边的帧中。
javadoc工具查找源代码中以如下格式出现的特殊注释:
/**
*/
与类的通用信息有关的注释直接出现在类声明之前,而与类的字段、方法或构造器有关的注释直接出现在类成员声明之前。此外,我们还可以使用如下javadoc标记之一来表述特定类型的注释信息:
 @author:表示源代码的作者名称。
 @depracated:表示随着程序版本的提升,当前类成员已经过期,仅为了保证兼容性依然存在,以此告之开发者不应再用这个API。
 {@docRoot}:代表当前页面要输出到的针对于根目录的相对路径。
 @exception:被方法所用,列出抛出的异常。
 {@link 包.类#成员的链接文字}:创建一个对特定类成员的链接。
 {@linkplain 包.类#成员的链接文字}:如{@link}相同,但是用纯文本字体代替了代码字体。
 @param:用于描述方法的形式参数。
 @return:用于描述方法的返回值。
 @see:创建一个“参见”XX条目的链接,如类、方法、变量等。
 @since:描述成员存在或改变时的版本号。
 @serial、@serialField和@serialData:用于串行化用途。
 @throws:与@exception标记相同。
 {@value}:用于显示常量静态字段的值。
 @version:描述类的软件版本。
代码清单6.6所示的Television类,包含了了javadoc注释,并演示了javadoc标记的使用。
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61: 62: 63: package electronics;

/**

  • Television类用于代表有频道和音量设置的标准电视机。
  • 这个特别的javadoc注释将出现在文档页的开始。
  • @author 张三
  • @version 1.2
    */

public class Television {
/**
* 字段channel代表当前收看的频道。
/
public int channel;
/
*
* 这个字段是private,默认是不会出现在文档页面上。 page.
*/
private int volume;

/**
 * 构造一个Television对象,频道为4,音量为5。
 */
public Television() {
	this(4, 5);
	System.out.println("在Television()内部");
}

/**
 * 构造一个Television对象,带有两个参数。
 * 
 * @param c
 *            初始频道。
 * @param v
 *            初始音量。
 */
public Television(int c, int v) {
	System.out.println("在Television(int, int)内");
	channel = c;
	setVolume(v);
}

/**
 * 字段volume的访问器方法。
 * 
 * @return 返回当前音量。
 */
public int getVolume() {
	return volume;
}

/**
 * 改变音量,并且参数值在0和10之间。
 * 
 * @param v
 *            television的新音量。该值必须在0和10之间。
 */
public void setVolume(int v) {
	if (v >= 0 && v <= 10) {
		volume = v;
	}
}

}
我们可以在一到多个类或者一个到多个包上运行javadoc。javaodc命令有很多选项,要详细的查看这些选项,只需要在命令行提示窗下运行javadoc即可。
如下的javadoc命令用于为electronics包生成HTML页面,生成的页面包含author和version标记:
javadoc -author -version -d . electronics
运行该命令后会生成很多页面。Television文档页如图6.7所示。注意,private字段volume没有出现在文档页上,因为我们指定private为javadoc选项之一。

Figure 12.7 Documentation page for the Television class.
在上机实训中,我们将练习如何在一个类中使用javadoc注释,并运行javadoc工具。
为了保证javadoc工具成功运行,源代码必须保存在一个匹配类的包名的目录结构中。例如,在electronics包中的类Television保存的文件名必须为.\electronics\Television.java。
6.9 上机实训
本阶段实训将练习数组和javadoc工具的使用。
6.9.1 使用数组
在本实训中,我们将更改实训5.4中的Powerball类,用单个数组记录每个彩球。
实现步骤

  1. 在编辑器中打开实训5.3中的Powerball类。
  2. 删除代表五个白球的五个成员变量。添加一个成员变量,该成员变量是对一个int类型数组的引用。
  3. 在Powerball的构造器中,用一个大小为5的新的int类型的数组给前一步中的成员变量赋值。
  4. 更改Powerball类的其它部分,让它可以使用int类型的数组存储5个白球的值。当在displayResults()方法中显示结果时,使用for循环和数组的length属性。
    6.9.2 使用javadoc
    在本实训中,我们将运行javadoc工具,为Powerball类创建文档。
    实现步骤
  5. 首先,从硬盘上删除Powerball.class字节码文件。
  6. 将Powerball类添加到lottery包中。
  7. 使用-d标记编译类,编译完成后会创建一个\lottery目录。
  8. 将Powerball.java文件从当前目录移动的熬\lottery目录中。
  9. 在Powerball类中所有地方添加上javadoc注释,包含所有成员变量、方法和构造器。对方法参数使用@param标记,对返回值使用@return标记。添加@author和@version标记,以及任何想试验的标记。
  10. 使用命令行方式在lottery包上运行javadoc:
    javadoc –author –version –private lottery
  11. 如果获得一个错误,指出包或者类没有找到,那么要么从包含\lottery的目录上运行javadoc命令,或者将包含\lottery的目录添加到CLASSPATH中。
  12. 打开创建的index.html文件,检查注释。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值