Java语言程序设计(第3版)
上QQ阅读APP看书,第一时间看更新

5.1 创建和使用数组

数组是几乎所有程序设计语言都提供的一种数据存储结构。数组是名称相同,下标不同的一组变量,用来存储一组类型相同的数据。下面就来介绍声明、初始化和使用数组。

5.1.1 数组定义

使用数组一般需要如下三个步骤:

(1)声明数组:声明数组名称和元素的数据类型。

(2)创建数组:为数组元素分配存储空间。

(3)数组的初始化:为数组元素赋值。

1.声明数组

使用数组之前需要声明,声明数组就是告诉编译器数组名和数组元素类型。数组声明可以使用下面两种等价形式。

这里,elementType为数组元素类型,可以是基本数据类型(如boolean型或char类型),也可以是引用数据类型(如String或Employee类型等);arrayName为数组名,它是一个引用变量;方括号指明变量为数组变量,既可以放在变量前面也可以放在变量后面,推荐放在变量前面,这样更直观。

例如,下面声明了几个数组:

注意:数组声明不能指定数组元素的个数,这一点与C/C++不同。

上面声明的数组,它们的元素类型分别为double型和String型。在Java语言中,数组是引用数据类型,也就是说数组是一个对象,数组名就是对象名(或引用名)。数组声明实际上是声明一个引用变量。如果数组元素为引用类型,则该数组称为对象数组,如上面的words就是对象数组。所有数组都继承了Object类,因此,可以调用Object类的所有方法。

提示:Java语言的数组是一种引用数据类型,即数组是对象。数组继承Object类的所有方法。

2.创建数组

数组声明仅仅声明一个数组对象引用,而创建数组是为数组的每个元素分配存储空间。创建数组使用new语句,一般格式为:

该语句功能是分配arraySize个elementType类型的存储空间,并通过arrayName来引用。例如:

注意:Java数组的大小可以在运行时指定,这一点C/C++不允许。

数组的声明与创建可以写在一个语句中。例如:

当用new运算符创建一个数组时,系统就为数组元素分配了存储空间,这时系统根据指定的长度创建若干存储空间并为数组每个元素指定默认值。对数值型数组元素默认值是0;字符型元素的默认值是'\u0000';布尔型元素的默认值是false;如果数组元素是引用类型,其默认值是null。

前面两个语句分别分配了5个double型和3个String类型的空间,并且每个元素使用默认值初始化。两个语句执行后效果如图5-1所示。数组marks的每个元素都被初始化为0.0,而数组words的每个元素被初始化为null。

图5-1 marks数组和words数组示意

对于引用类型数组(对象数组)还要为每个数组元素分配引用空间。例如:

上面语句执行后效果如图5-2所示。

图5-2 words数组元素创建后的效果

3.访问数组元素

声明了一个数组,并使用new运算符为数组元素分配内存空间后,就可以使用数组中的每一个元素。数组元素的使用方式是:

其中,index为数组元素下标或索引,下标从0开始,到数组的长度减1。例如,上面定义的words数组定义了三个元素,所以只能使用words[0]、words[1]和words[2]这三个元素。数组一经创建大小不能改变。

数组作为对象提供了一个length成员变量,它表示数组元素的个数,访问该成员变量的方法为arrayName.length。

下面程序演示了数组的使用和length成员的使用。

程序5.1 ArrayDemo.java

程序运行结果为:

为了保证安全性,Java运行时系统要对数组元素的范围进行越界检查,若数组元素下标超出范围,运行时将抛出ArrayIndexOutOfBoundsException异常。例如,下面代码抛出异常。

4.数组初始化器

声明数组同时可以使用初始化器对数组元素初始化,在一对大括号中给出数组的每个元素值。这种方式适合数组元素较少的情况,这种初始化也称为静态初始化。

用这种方法创建数组不能指定大小,系统根据元素个数确定数组大小。另外可以在最后一个元素后面加一个逗号,以方便扩充。

上面两句还可以写成如下更简单的形式:

5.1.2 增强的for循环

如果程序只需顺序访问数组中每个元素,可以使用增强的for循环,它是Java 5新增功能。增强的for循环可以用来迭代数组和对象集合的每个元素。它的一般格式为:

该循环的含义为:对expression(数组或集合)中的每个元素identifier,执行一次循环体中的语句。这里,type为数组或集合中的元素类型;expression必须是一个数组或集合对象。

下面使用增强的for循环实现求数组marks中各元素的和,代码如下:

5.1.3 数组元素的复制

经常需要将一个数组的元素复制到另一个数组中。一种方法是将数组元素逐个复制到目标数组中。设有一个数组source,其中有4个元素。现在定义一个数组target,与原来数组类型相同,元素个数相同。使用下面方法将源数组的每个元素复制到目标数组中。

除上述方法外,还可以使用System类的arraycopy()方法,格式如下:

其中,src为源数组;srcPos为源数组的起始下标;dest为目的数组;destPos为目的数组下标;length为复制的数组元素个数。下面代码实现将source中的每个元素复制到数组target中。

使用arraycopy()方法可以将源数组的一部分元素复制到目标数组中。需要注意的是,如果目标数组不足以容纳源数组元素,会抛出异常。

程序5.2 ArrayCopyDemo.java

程序运行结果为:

注意:不能使用下列方法试图将数组source中的每个元素复制到target数组中。

上述两条语句实现对象的引用赋值,两个数组引用指向同一个数组对象,如图5-3所示。

图5-3 将source赋值给target的效果

5.1.4 数组参数与返回值

数组可以作为方法的参数和返回值。

可以将数组对象作为参数传递给方法。例如,下面代码定义了一个求数组元素和的方法。

注意:由于数组是对象,因此将其传递给方法是按引用传递。当方法返回时,数组对象不变。但要注意,如果在方法体中修改了数组元素的值,则该修改反映到返回的数组对象。

一个方法也可以返回一个数组对象。例如,下面的方法返回参数数组的元素反转后的一个数组。

有了上述方法,可以使用如下语句实现数组反转。

5.1.5 可变参数的方法

Java语言允许定义方法(包括构造方法)带可变数量的参数,这种方法称为可变参数(variable argument)方法。具体做法是,在方法参数列表的最后一个参数的类型名之后、参数名之前使用省略号。例如:

这里,参数values被声明为一个double型值的序列。其中参数的类型可以是引用类型。对可变参数的方法,调用时可以为其传递任意数量指定类型的实际参数。在方法体中,编译器将为可变参数创建一个数组,并将传递来的实际参数值作为数组元素的值,这相当于为方法传递一个指定类型的数组。

程序5.3 VarargsDemo.java

程序定义了带可变参数的方法average(),它的功能是返回传递给该方法多个double型数的平均值。该程序调用了average()方法并为其传递三个参数,输出结果为72.0。

在可变参数的方法中还可以有一般的参数,但是可变参数必须是方法的最后一个参数。例如,下面定义的方法也是合法的:

注意:在调用带可变参数的方法时,可变参数是可选的。如果没有为可变参数传递一个值,那么编译器将生成一个长度为0的数组。如果传递一个null值,将产生一个运行时NullPointerException异常。

5.1.6 实例:随机抽取4张牌

从一副有52张的纸牌中随机抽取4张,打印抽取的是哪几张牌。可以定义一个有52个元素的名为deck的数组,用0~51填充这些元素。

设元素值0~12为黑桃,13~25为红桃,26~38为方块,39~51为梅花。然后打乱每个元素的牌号值(洗牌),之后从中取出前4张牌,最后用cardNumber/13确定花色,用cardNumber%13确定哪一张牌。

程序5.4 DeckOfCards.java

下面是一次运行结果:

5.1.7 实例:一个整数栈类

栈是一种后进先出(last in first out, LIFO)的数据结构,在计算机领域应用广泛。例如,编译器就使用栈来处理方法调用。当一个方法被调用时,方法的参数和局部变量被推入栈中,当方法又调用另一个方法时,新方法的参数和局部变量也被推入栈中。当方法执行完返回调用者时,该方法的参数和局部变量从栈中弹出,释放其所占空间。

可以定义一个类模拟栈结构。为简单起见,设栈中存放int类型值,StackOfIntegers程序的代码如下。

程序5.5 StackOfIntegers.java

该栈类使用数组实现。元素存储在名为elements的整型数组中,当创建栈对象时将同时创建一个数组对象。使用默认构造方法创建的栈包含10个元素,也可以使用带参数构造方法指定数组初始大小。变量size用来记录栈中元素个数,下标为size−1的元素为栈顶元素。如果栈空,size值为0。

StackOfIntegers类实现了栈的常用方法,其中包括push()将一个整数存入栈中;pop()方法为元素出栈方法;peek()方法返回栈顶元素但不出栈;empty()方法返回栈是否为空;getSize()方法返回栈中元素个数。

程序5.6 StackOfIntegersDemo.java

程序运行结果为: