声明数组时需要确定数组名,数组的维数和数组元素的数据类型。数组名是符合java标识符定义规范的用户自定义标识符,它是数组类对象的引用类型变量的名字,声明引用类型变量时,系统只为该变量分配引用空间,其值为null。数组的维数用“[]”的个数来确定,对于只有一个“[]”的称为一维数组,对于含有两个或者更多“[]”的数组统称为多维数组。其中一维数组是数组最常用的形式。
第5章 数组
在前面的讲述中,我们知道,如果想要计算100个整数相加,那么则需要定义100个变量,然后将这100个整数变量相加,应该说,这是一件多么枯燥的事情,如果 是1000个整数,甚至是10000个整数呢?相信任何人都不愿意去定义10000个变量来做加法操作了。那么,有没有简单的方法来替代上述变量的定义方式呢?
实际上,java中可以定义数组,用数组的方式可以实现上述的定义 100个,1000个甚至10000个数据类型相同的变量。
Int a[] = new int[100]; 这条语句就可以实现定义100个变量的目的,可以通过a[0],a[1],…a[99]来访问这些变量。
本章主要来讲解数组的基本概念及数组的应用。
5.1 一维数组
声明数组时需要确定数组名,数组的维数和数组元素的数据类型。数组名是符合java标识符定义规范的用户自定义标识符,它是数组类对象的引用类型变量的名字,声明引用类型变量时,系统只为该变量分配引用空间,其值为null。数组的维数用“[]”的个数来确定,对于只有一个“[]”的称为一维数组,对于含有两个或者更多“[]”的数组统称为多维数组。其中一维数组是数组最常用的形式。
5.1.1 一维数组的声明
声明数组的语法格式:
数据类型 数组名[];
或
数据类型[] 数组名
例如:
int a[];//声明数组名为a的int型一维数组
double[] b[];//声明数组名为b的double型的一维数组
数组时由相同类型的数组组成的有序集合,因此,在声明数组的时候,要么声明数组的所有元素为int类型,要么都是double类型,而不能声明同时包含int类型和double类型数据的数组。
5.1.2 一维数组的初始化
我们知道,只声明了数组,这时候其值为null,要想使用数组,还需要做初始化工作。数组初始化为声明的数组指定数组元素个数,为数组元素分配空间并赋值。数组初始化可以通过如下几种方式完成。
1.直接指定初值的方式
用直接指定初值的方式初始化数组是指在声明数组的同时将数组元素的初值依次写入赋值号(=)后的一对大括号({})内,同时,根据 值的个数确定数组元素的个数,为它分配足够的存储空间并将这些值写入相应存储的存储单元。
如:
Char a[] = {‘N’,’C’,’E’,’P’,’U’};
从该语句可以得到信息:该数组名为a,数组元素的数据类型为char,每个元素占用2个字节的存储空间,大括号中共有5个值,因此数组元素的个数为5,系统会为该数组分配2*5=10个字节的连续存储空间。经过数组初始化后,a数组的5个元素值分配情况为:a[0]=’N’, a[1]=’C’,a[2]=’E’,a[3]=’P’,a[4]=’U’。
在java中,数组的下标是从0开始的,因此对于含有n个元素的数组a来说,其最小下标为0,最大下标为n-1,而不是n。
2. 用new关键字初始化数组
用关键字new初始化数组时,除了为数组指定数组元素个数、分配存储空间外,还会为数组元素按照数据类型的不同赋初值。具体如下:
若为数值型,数组元素的默认值为0,;
若为布尔型,数组元素的默认值为false;
若为引用型,数组元素的默认值为空(null)。
(1) 先声明数组,再初始化数组。这里实际上是两条语句,具体格式如下:
数据类型[] 数组名; 或数据类型 数组名[];
数组名=new 数据类型[数组长度];
第一条语句是数组声明语句,第二条语句则是初始化语句。需要注意的是这两条语句中数组名、数据类型必须一致,否者会产生错误或意想不到的结果。
数组的长度可以是直接的int类型常数,也可以是已经赋值的int类型变量或int类型表达式。
例如:
Int n=10;
Int[] a;
a = new int[n];
这样,在创建数组时提供了一定的灵活性,可以在程序中根据变量n的大小动态创建数组,需要注意的是,虽然n是变量,但根据n的值创建的数组一旦创建,数组的长度就固定下来了,不在允许修改。
上面的语句中a数组中含有10个元素,初始化工作会使数组中的10个元素根据不同数据类型被赋以初值,因为这里是int,所以,10个元素的初值为0。
(2)在声明数组的同时用new关键字初始化数组。实际上,这种方式是将第一种方式的两条语句合并为一条语句。格式如下:
数据类型[] 数组名 = new 数据类型[数组长度];
或
数据类型 数组名[] = new 数据类型[数组长度]
例如:
Int n=10;
Int[] a = new int[n];
(3) 用一个已经初始化的数组对数组初始化
除了上述两种形式的初始化外,还可以直接使用一个已经初始化的数组对新声明的数组进行初始化。
格式如下:
数据类型[] 数组名=已初始化数组
例如:
Int[]a=new int[10];
Int[]b=a;
这种初始化会使得数组a与b具有相同的存储地址,即,a数组与b数组指向了同一个物理地址,任何对数组a的改变都会使数组b做相应的修改,同样,任何对数组b的修改也会使a数组的内容发生变化。
要想验证上述观点可以使用java的输出语句System.out.println(数组名)的方式来查看:
System.out.println(a);
System.out.println(b);
的输出结果是否相同,这里输出的是地址,如果相同表明数组a和b的地址相同,即它们实际上是一个数组的两种表现形式。
3.混合方式
有时候,数组的初始化可以综合上面两种方式进行,如下语句
int[] a = new int[]{1,2,3,4};
同样可达到初始化的目的,这是一种常用的初始化方式,在今后的学习中读者还会见到。
5.1.3 一维数组的使用
定义好一维数组后,我们来看如何使用一维数组,使用一维数组,实际上就是使用数组中所包含的元素,一维数组元素的使用格式如下:
数组名[数组下标],下标的范围最小值为0,最大值为数组长度-1。
例如:
public class HelloWorld {
public static void main(String[] args) {
int[]a={1,2,3,4,5,6,7,8,9};
int sum=0;
for(int i=0;i<9;i++){
sum+=a[i];
}
System.out.println("sum="+sum);
}
}
该程序的功能是对数组中所有元素求和并输出,从程序中可以看出数组a含有9个元素,因此,其下表范围为0-8,在for循环中有“i<9”。
数组元素的访问一般离不开for循环,离开for循环的数组在使用时也就失去了数组的优势,上述程序如果不使用for循环,对于数组所有元素的求和,则只能用 a[0]+a[1]+…+a[8]的方式,与我们定义9个独立的变量进行相加是一样的。
我们说数组的下标是有范围的,对于C或者C++语言来说,数组的定义也与此类似,但在使用时,如果数组的下标超出范围后,在C或者C++中,程序不出错,且数组元素的值是我们不可预知的一个值,那么java中是否也是如此呢,改变上面程序中的语句 for(int i=0;i<9;i++){
为 for(int i=0;i<10;i++){
编译程序,程序出错,出错信息如下:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 9
at HelloWorld.main(HelloWorld.java:6)
这里出错的信息是数组下标越界异常,也就是说,我们的程序中有超出数组下标的数组元素访问,这里是a[9],数组中只有9个元素,最大到a[8],因此程序报错。由于java和C++的不同,我们在访问数组元素的时候就需要特别小心,在访问数组元素时,应确保数组下标不越界。上述程序是通过人为的方式检查数组元素个数,然后根据数组元素个数设置for循环的循环条件,有没有更好的方式来保证数组下标不越界呢?
有的,在java中,为数组提供了一个数据成员length来存储数组的长度,我们可以使用length数据成员来确保数组下标不越界。修改上述程序如下:
public class HelloWorld {
public static void main(String[] args) {
int[]a={1,2,3,4,5,6,7,8,9};
int sum=0;
for(int i=0;i }
System.out.println("sum="+sum);
}
}
这里,用a.length替换了原来的9,这样,在访问数组元素的时候就不会发生数组下标越界异常了,建议读者在今后的数组元素访问时尽量采用a.length的形式。
5.1.4 传递数组元素到成员方法
数组元素可以单独作为变量使用,因此,数组元素可以像其他普通变量一样,做为实参传递给成员方法供其使用。若数组元素的数据类型是基本数据类型,则数组元素作为成员方法的实参与用普通变量作为实参一样,都是单向值传递,即只能由数组元素传递给形参,程序中队形参的任何改变不会影响到传递给它的数组元素的值。
如下程序的意图是将数组的两个元素值进行交换,程序如下:
[例5-3]
class Change{
public void swap(int a,int b){
System.out.println("交换前形参:\n a="+a+" b="+b);
int temp;
temp=a;
a=b;
b=temp;
System.out.println("交换后形参:\n a="+a+" b="+b);
}
}
public class Test5_3 {
public static void main(String[] args) {
int[] arr={5,8};
Change c = new Change();
System.out.println("交换前实参:\n arr[0]="+arr[0]+" arr[1]="+arr[1]);
c.swap(arr[0], arr[1]);
System.out.println("交换前实参:\n arr[0]="+arr[0]+" arr[1]="+arr[1]);
}
}
程序运行结果为:
交换前实参:
arr[0]=5 arr[1]=8
交换前形参:
a=5 b=8
交换后形参:
a=8 b=5
交换前实参:
arr[0]=5 arr[1]=8 从运行结果可以看出,传递数组元素arr[0],arr[1]后,其对应的形参a,b经过swap方法中的计算后,其值发生了交换,但其交换并未影响到传递给它们的实参arr[0]和arr[1]。
这与我们的编程意图不相符合,那么,能有什么办法使得对形参的修改影响到传递给它的形参吗?
这里使用的是传递数组元素,我们可以传递数组的名字,这样就可以实现对形参的修改影响其对应的实参的效果。