第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 sum+=a[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]。
这与我们的编程意图不相符合,那么,能有什么办法使得对形参的修改影响到传递给它的形参吗?
这里使用的是传递数组元素,我们可以传递数组的名字,这样就可以实现对形参的修改影响其对应的实参的效果。
[NextPage]
5.1.5 传递数组到成员方法
在定义类的成员方法时,其参数的数据类型可以是基本数据类型,也可以是数组、字符串和类对象等引用类型。在将数组作为形参时,形参的定义格式如下:
访问控制符 返回值 方法名(数据类型[] 数组名)
方法中数组形参的形式实际上是数组的声明格式,前面我们学习过数组初始化的一种形式就是用已初始化后的数组对一个新声明的数组进行初始化,数组做形参就是这种情况,因此,要求传递的实参,其数据类型应该与在方法中定义的形参的数据类型相同,同时,形参和实参会具有相同的物理存储地址,因此,形参和实参是同一个数组,当然,在成员方法中队形参数组中各个元素的任何修改,都会使传递给它的实参数组的各个元素值发生变化,这种参数的传递方式被称为“双向地址传递”。
一般来说,如果成员方法的形参数据类型为基本数据类型,这时候参数传递方式是“单向值传递”,若果成员方法的形参数据类型为引用数据类型(数组,字符串、类对象等),则参数传递方式是“双向地址传递”。
采用数组名作形参的方式修改[例5-3]。
[例5-4]
class Change{
public void swap(int arr[]){
System.out.println("交换前形参:\n arr[0]="+arr[0]+" arr[1]="+arr[1]);
int temp;
temp=arr[0];
arr[0]=arr[1];
arr[1]=temp;
System.out.println("交换后形参:\n arr[0]="+arr[0]+" arr[1]="+arr[1]);
}
}
public class Test5_4 {
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);
System.out.println("交换后实参:\n arr[0]="+arr[0]+" arr[1]="+arr[1]);
}
}
运行结果为:
交换前实参:
arr[0]=5 arr[1]=8
交换前形参:
arr[0]=5 arr[1]=8
交换后形参:
arr[0]=8 arr[1]=5
交换后实参:
arr[0]=8 arr[1]=5
从运行结果可以看出,对形参数组arr两个元素arr[0]和arr[1]的修改影响到了实参数组arr的两个元素arr[0]和arr[1]。这里需要注意的是,形参和实参的名字都是arr,需要区分哪个是形参,哪个是实参。
向方法传递数组元素值的“单向值传递方式”和普通变量效果是一样的,因此,这种情况完全可以用普通变量来代替。在实际应用中,我们更多的是使用向方法传递数组名的“双向地址传递”方式。
5.1.6 数组元素间的比较
了解了数组元素的访问后,我们可以利用数组元素的遍历实现很多功能。
1.数组最大值、最小值和平均值
利用for循环遍历 数组的所有元素可以非常方便的求出数组的最大值、最小值和平均值。
[例5-5]]求出一个数组的最大值、最小值、平均值
import java.util.Scanner;
class Array {
//计算数组元素最大值
public int getMax(int[] a){
int max=a[0];
for(int i=0;i if(max max=a[i];
}
}
return max;
}
//计算数组元素最小值
public int getMin(int[] a){
int min=a[0];
for(int i=0;i if(min>a[i]){
min=a[i];
}
}
return min;
}
//数组元素求和
public int getSum(int[] a){
int sum=0;
for(int i=0;i sum+=a[i];
}
return sum;
}
//计算数组元素平均值,调用getSum方法
public double getAverage(int[] a){
double avg;
//因为getSum(a)和a.length都是int类型,有了(*1.0)后可以使两个int类型的值按double计算
avg=getSum(a)*1.0/a.length;
return avg;
}
}
public class Test5_5{
public static void main(String args[]){
//Sanner类是一个输入类,用于整数、实数、字符串等的输入
Scanner scan = new Scanner(System.in);
int[] a= new int[10];//初始化数组a
System.out.println("请连续输入10个整数");
//利用Scanner的 nextInt方法从键盘输入10个数
//使用a.length的方式,避免数组下标越界异常的发生
for(int i=0;i System.out.print("第"+(i+1)+"个整数:");
a[i]=scan.nextInt();
}
Array myArray = new Array();
System.out.println("数组的最大值为:"+myArray.getMax(a));
System.out.println("数组的最小值为:"+myArray.getMin(a));
System.out.println("数组的和为:"+myArray.getSum(a));
System.out.println("数组的平均值为:"+myArray.getAverage(a));
}
}
这里我们将对数组元素所做操作集中在一个类Array中实现,在需要使用这些方法的时候只要生成类Array的类对象,通过该类对象就可以调用这些方法了。而不是将这些方法放到主类中实现,直接通过主类的main方法调用。这是一种良好的编程习惯,便于代码复用,应予以提倡。
程序中使用到了Scanner类,下面予以介绍。
Scanner scan = new Scanner(System.in);
定义一个Scanner类的类对象,在定义过程中,Scanner有多个构造方法,这里我们使用的是其中一个,传递的参数System.in是标准输入,即从键盘输入。scan.nextInt()表示从键盘上读入一个整数,除此之外,Scanner还支持doble,float等数据类型数据的读入,分别对应方法nextDouble(),nextFloat()。
表5-1 Scanner类的常用方法
方法 |
说明 |
public int nextInt() |
获取一个int型整数 |
public long nextLong() |
获取一个long型整数 |
public foloat nextFloat() |
获取一个单精度实数 |
public double nextDouble() |
获取一个双精度实数 |
public String next() |
获取一个字符串 |
利用表5-1中的方法可以实现多种数据类型数组的读取,特别注意,方法next的返回值是String类型。
[NextPage]
2.数组的排序
数组中集中了多个数据类型相同的元素,为了更好的对数组元素操作,有时候对数组排序是比不可少的,因此,下面我们讨论如何对数组排序。排序算法在数据结构中有多个,这里算法不是我们讲解的重点,我们选择其中一种:冒泡排序(排序后元素值递增)进行讲解。
冒泡排序的关键点是从后向前对相邻的两个数组元素进行比较,若后面元素的值小于前面元素的值,则让两元素交换值;否则,不进行交换。依次遍历所有元素,这样,第一趟排序后数组中的最小值就是下标为0的元素了,依次类推,我们进行第二趟排序(这时候我们无需遍历所有元素,因为数组下标为0的元素,其值已经是最小,我们只需遍历从除下标为0的所有元素),经过第二趟后,下标为1的数组元素存储的是数组中次小的值,这样对于有n个元素的数组,循环执行n-1趟后便可完成排序。当然,也可以从前向后对两个数组元素进行排序,但此时是较大者的值向后移。
[例5-6]
import java.util.Scanner;
class SortClass{
//对数组排序,arr:数组名
public void sort(int[]arr){
int temp;//交换值时作为临时变量
for(int i=0;i for(int k=arr.length-1;k>i;k--){
if(arr[k]>arr[k-1]){//后者小于前者,需要交换两者的值
temp = arr[k];
arr[k]= arr[k-1];
arr[k-1]= temp;
}
}
paint(i+1,arr);//调用数组打印方法
}
}
//打印数组元素,用于排序时检测每趟的排序结果
//time:第几趟,arr:数组名
public void paint(int time,int[] arr){
System.out.print("\n第"+time+"趟排序:");
for(int i=0;i System.out.print(arr[i]+"\t");
}
}
}
public class Test5_6 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n=5;//数组元素个数
int[] arr= new int[n];
System.out.println("请从键盘上输入"+n+"个数:");
//利用Scanner的 nextInt方法从键盘输入10个数
for(int i=0;i System.out.print("第"+(i+1)+"个整数:");
arr[i]=scan.nextInt();
}
System.out.println("排序前数组元素值:");
for(int i=0;i System.out.print(arr[i]+"\t");
}
SortClass sc = new SortClass();//生成SortClass的类对象,用以调用sort方法
sc.sort(arr);//调用SortClass的sort方法
System.out.println("\n排序后数组元素值:");//字符串中的"\n"是换行操作
for(int i=0;i System.out.print(arr[i]+"\t");
}
}
}
运行结果如下:
请从键盘上输入5个数:
第1个整数:52
第2个整数:45
第3个整数:68
第4个整数:96
第5个整数:32
排序前数组元素值:
52 45 68 96 32
第1趟排序:96 52 45 68 32
第2趟排序:96 68 52 45 32
第3趟排序:96 68 52 45 32
第4趟排序:96 68 52 45 32
排序后数组元素值:
96 68 52 45 32
从结果中可以看出,经过sort方法处理后,数组元素按升序进行排列了,那么,是否可以让数组元素按从大到小顺序排序的,其实,只要改变判断条件“if(arr[k]>arr[k-1])”中的“>”为“<”即可实现了,读者可自行验证。
3.数组特定数据的查找从数组中查找
从数组中查找特定数据的最简单的办法是遍历数组中的所有元素,这种插好方法也称为线性查找。以下方法indexOf()用于查找arr数组中屈指为value的元素的索引位置,若找不到,返回-1。该方法采用的就是线性查找方式。
public int indexOf(int[] arr,int value){
for(int i=0;i if(arr[i]==value)
return i;//找到,返回对应下标
}//for循环执行完后,表示未找到,则返回-1
return -1;
}
线性查找的时间复杂度为O(n),它适用于小型数组或未排序的数组。对于大型数组,线性查找的效率比较低。如果数组已经排好序,那么我们可以采用高效的二叉查找算法。
二叉查找算法的中心思想史:查找数组中位于中间位置的元素,并将其与查找值进行比较,如果两者相等,就返回该数组元素的下标值;否则,将问题简化为查找已排序数组的一半元素——如果查找值小于数组的中间元素,就查找数组的前半部分,否则就查找数组的后半部分(假设数组按升序排序)。二叉查找的 时间复杂度是 O(log2n)。
例5-7给出了使用二叉查找算法的数组元素查找方法。
[例5-7]
class ArraySearch{
//使用二叉查找算法查找数组arr中值为value的数组元素下标,找不到返回-1
public int indexOf(int[] arr,int value){
int low=0;
int high=arr.length-1;
int middle;
while(low<=high){
middle=(low+high)/2;
if(arr[middle]==value){
return middle;
}
else
if(arr[middle] low=middle+1;
}
else{
high=middle-1;
}
}
return -1;
}
}
public class Test5_7 {
public static void main(String[] args) {
int[] arr={1,3,5,7,9,11,13};
ArraySearch as = new ArraySearch();
int index=as.indexOf(arr, 3);
System.out.println("3在数组arr中的下标位置为:"+index);
}
}
运行结果:(分找到、找不到两次实验)
找到:
3在数组arr中的下标位置为:1
找不到:(将查找的值改为:4)
4在数组arr中的下标位置为:-1
5.1.7 数组元素的复制
从前面的讲解中我们了解到,如果我们用一个已经初始化后的数组为另一个刚刚声明的数组做初始化,那么这两个数组实际上会指向同一个数组,对其中一个数组的操作会影响到另一个数组的值,在实际应该用中,经常会遇到只是想将数组的值赋值到另一个数组,被赋值的数组的修改对原数组不产生影响的情况。这时候我们就不能采用“int[]a=b”(b是一个已初始化的数组)了,可以通过如下的for循环完成:
for(int i=0;i b[i]=a[i];
}
在java中提供了一个现有的方法实现数组元素的复制,它就是“System.arraycopy”方法,该方法的原型为:
public static void arraycopy(Object src,int srcPos,
Object dest,int destPos, int length)
各参数的意义:
src - 源数组。
srcPos - 源数组中的起始位置。
dest - 目标数组。
destPos - 目标数据中的起始位置。
length - 要复制的数组元素的个数。
方法的具体含义:
从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。从 src 引用的源数组到 dest 引用的目标数组,数组的一个子序列被复制下来。源数组中位置在 srcPos 到 srcPos+length-1 之间的数组 元素被分别复制到目标数组中的 destPos 到 destPos+length-1 位置。
如果参数 src 和 dest 引用相同的数组对象,则复制的执行过程就好像首先将 srcPos 到 srcPos+length-1 位置的元素复制到一个带有 length 个元素的临时数组,然后再将此临时数组的内容复制到目标数组的 destPos 到 destPos+length-1 位置一样。 不会造成读和写的冲突。
在使用该方法时,需注意,如果参数的数据错误有可能会引起以下几种异常(有关异常的内容请参见第10章)。
NullPointerException异常:这个异常的产生可能是因为源数组src或目标数组dest并没有引用到一个数组的实例,即数组没有初始化,这样数组的引用值为null,就会产生这个异常。如果这个是因为为null,则目标数组并不会去做任何的修。
ArrayStoreException异常:这个异常的产生原因可能是来源或目标数组根本就不是数组,或者是来源目标数组不是基本类型的数组。Arraycopy只适用基本数据类型的数组。
IndexOutOfBoundsException异常:这个异常的产生原因是索引值指定错误。包括srcPos、destPos和length值为负数,或者srcPos+length 大于 src.length,destPos+length 大于 dest.length。这些情况中的任何一种都会引发IndexOutOfBoundsException异常。
例[5-8]
public class Test5_8 {
public static void main(String[] args) {
int[] a={12,43,54,56,78};
int[] b=new int[8];
System.arraycopy(a, 1, b, 2, 4);
for(int i=0;i System.out.print(b[i]+"\t");
}
}
}
由上面关于arraycopy的解释我们知道System.arraycopy(a, 1, b, 2, 4),表示将数组a中下标为1开始的元素复制给数组b中下标为2开始的4个元素。
运行结果为:
0 0 43 54 56 78 0 0
可能读者会有这样的疑问,既然我们自己通过for语句也可以控制两个数组的复制,那么为什么还需要使用arraycopy方法呢?
原因有两个:系统给定的方法时经过严格测试的方法,一般不存在错误,且考虑了各种异常情况,比自己写的考虑全面;再有,arraycopy在内部实现上使用的是JNI的方法(即调用本地的其他语言所写的程序的方法),运行速度上会比我们用java写的程序要快。
[NextPage]
5.1.8 数组元素的排序
前面我们自己编写了对数组排序的算法,其实,在java中提供了数组排序的方法,该方法是类Arrays的一个静态方法sort,该方法其中一个原型为:
public static void sort(int[] a,int fromIndex,int toIndex)
各参数意义:
a - 要排序的数组
fromIndex - 要排序的第一个元素的索引(包括)
toIndex - 要排序的最后一个元素的索引(不包括)
该方法具体含义:
对指定 int 型数组的指定范围按数字升序进行排序。排序的范围从索引 fromIndex(包括)一直到索引 toIndex(不包括)。(如果 fromIndex==toIndex,则排序范围为空。)当然,如果想要对数组所有元素进行排序的话,那么我们可以使用sort的另一个重载方式:
public static void sort(int[] a)
sort排序算法是一个经过调优的快速排序法。sort方法可以针对数组元素为基本类型的数组进行排序,因此,其重载方法比较多,共17个之多,有兴趣的读者可以查阅 JDK文档了解详情。
我们利用sort方法实现一个数组的排序。
[例5-9]
import java.util.Arrays;
import java.util.Scanner;
public class Test5_9 {
public static void main(String args[]){
Scanner scan = new Scanner(System.in);
int n=5;//数组元素个数
int[] arr= new int[n];
System.out.println("请从键盘上输入"+n+"个数:");
//利用Scanner的 nextInt方法从键盘输入10个数
for(int i=0;i System.out.print("第"+(i+1)+"个整数:");
arr[i]=scan.nextInt();
}
Arrays.sort(arr);//Arrays.sort(arr,1,3);
System.out.print("排序后:");
for(int i=0;i System.out.print(arr[i]+"\t");
}
}
}
运行结果为:
请从键盘上输入5个数:
第1个整数:14
第2个整数:24
第3个整数:15
第4个整数:53
第5个整数:36
排序后:14 15 24 36 53
5.1.9 对象数组
前面对数组的讨论,其数据类型都是基本数据类型,但实际应用中往往需要把不同类型的数据组合成一个有机的整体,以便于引用。例如,一个学生的姓名、性别、年龄和各科学习成绩等都是与这名学生密切相关的,对于一个班甚至一个学校的学生有都具有这样的特性。若果我们在编写程序的时候将每个学生的这些信息定义为独立的简单变量,那么,是很难反映他们之间的内在联系的,这时候我们需要使用类和对象章节中讲到的类的概念,将这些基本信息都集中到一个类中去实现,再将类实例化为对象,对象与个体学生相对应。
由自定义的对象作为数组元素所构成的数组被称为对象数组。也就说说,对象数组时指数组的每个元素都是具有相同属性的对象。或者说,如果一个类有若干个对象,我们可以把这些对象用一个对象数组来存放。
[例5-10]设有若干名学生,每个学生有姓名,性别和成绩三个属性,要求将每个学生作为一,个对象,建立获取对象名字的成员方法getName和获取对象性别的成员方法 getSex,以及输出对象的全部数据成员的成员方法print。
class Student{
private String name;
private char sex;
private int score;
Student(String name,char sex,int score){
this.name=name;
this.sex = sex;
this.score= score;
}
public String getName(){
return this.name;
}
public char getSex(){
return this.sex;
}
void print(){
System.out.println("Name:"+this.name+"\tSex:"+this.sex+"\tScore:"+this.score);
}
}
public class Test_10 {
public static void main(String[] args) {
Student[] stu = new Student[3];
stu[0] = new Student("张三",'男',88);
stu[1] = new Student("李四",'女',95);
stu[2] = new Student("王五",'男',79);
for(int i=0;i stu[i].print();
}
}
}
运行结果:
Name:张三 Sex:男 Score:88
Name:李四 Sex:女 Score:95
Name:王五 Sex:男 Score:79
5.1.10 命令行参数
对于java application程序来说,要想程序运行,必须有一个方法main,其格式为:
public static void main(String[] args)
在以前的程序中,我们只是使用,但从未使用过args参数,那么,这个String类型的参数是干什么的呢?
对于java的输入,我们可以使用Scanner类进行输入,也可以使用后面章节介绍的专门的输入流输入,也可以使用本节将要介绍的args参数输入。
Args参数表示main方法接收了一个字符串数组,一般我们称为命令行参数。
我们通过例5-11来解释args参数的输入问题
public class Test5_11 {
public static void main(String[] args) {
System.out.println("查看args参数:");
if(args.length==0){
System.out.println("没有使用args参数,args数组长度为0");
}
else{
for(int i=0;i System.out.print(args[i]+"\t");
}
}
}
}
既然是命令行参数,那么我们使用命令行的方式编译、运行该程序。
运行结果如下图:
图5-1 使用命令行方式运行5-11
Java程序执行时采用 java Test5_11 I like java的方式,那么args数组的内容为
Args[0]=”I”;
Args[1]=”like”;
Args[2]=”java”;
可见,程序名并没有存储在args数组中,args存储程序名后边的所有字符串。
这里需要注意的是,平时我们在使用main方法时,因为不给args传递字符串参数,因此,args数组的长度为0,即数组的长度可以为0,长度为0的数组表示数组没有元素,与数组为null不同。
Int[] a= new int[0];
Int[] b = null;
前者表示一个数组长度为0的数组,可以访问该数组,但后者数组指向null,调用会引发NullPointerException。
如果使用的是Eclipse,按如下方式设置:
(1)点击菜单Run,选择子菜单Run Configurations
(2)在如下界面中输入args的参数值,这里输入的是“I love java”
图5-2 使用Eclipse运行5-11的参数设置