所谓数组是指一组相关数据类型的数据元素的集合,并且各数据元素在内存中按顺序连续存放。数组为每个元素都分配了索引(或下标),编号从0开始。数组本身属于引用数据类型,数组变量只是一个引用,通过这个引用访问它所指向的有效内存(数组对象本身),访问某个数组元素时需要使用该数组的数组名和该元素在数组中的索引。
注意:
在数组中可以存放任意类型的元素,既可以是基本数据类型中的值,也可以是引用数据类型的值(此时为对象数组),但同一个数组中存放的元素类型必须一致。
数组分为一维数组和多维数组。当数组中每个元素都只带有一个下标时,这种数组就是一维数组。一维数组实质上是一组相同类型数据的线性集合,是数组中最简单的一种数组。
1.一维数组的声明
为了在程序中使用一个数组,必须声明一个引用该数组的变量,并指明该变量引用的数组类型。声明一维数组的语法格式为:
或
可见,数组的声明有两种形式:一种是中括号“[]”跟在元素数据类型之后,另一种是中括号“[]”跟在变量名之后。对于以上两种语法格式而言,Java推荐采用第一种声明格式。
注意:
声明一个数组,只是得到了一个存放数组的引用变量,并没有为数组元素分配内存空间,因此还不能使用。
2.一维数组的创建
创建数组,就是为该数组分配内存空间,这样,数组的每一个元素才有一个空间进行存储。在Java中可以使用new关键字来给数组分配空间,分配空间的语法格式如下:
其中,数组长度就是数组中能存放的元素个数,显然应该为大于0的整数。一旦声明了数组的大小,就不能再修改。
注意:
数组元素下标的取值范围从0开始,到“数组长度-1”为止。访问超过元素下标取值范围的数据元素将会产生数组下标越界异常。
当然,也可以在声明数组时就给它分配空间,语法格式如下:
数据类型[]数组名=new数据类型[数组长度];∥例如,char[]c=new char[3];
注意:
(1)创建数组,其实就是创建数组对象,JVM根据数据类型和数组长度在堆中为该数组分配内存空间,并将数组中的各元素根据其数据类型执行默认初始化工作,最后将该数组对象的首地址赋值给栈中的数组引用变量。
(2)数组对象提供了一个length属性来表示数组的长度,调用该属性的语法为“数组名.length”,例如,本例中数组c的下标最大为c.length-1。数组对象的length属性在数组遍历中尤其有用。
3.一维数组的初始化
所谓数组的初始化就是为数组的每个元素赋初始值。在Java中,数组的初始化有以下三种方式:
1)默认初始化
数组一经创建,内存中每个元素都会有一个该数据类型的默认值。例如语句“char[]c=new char[3];”中各元素c[0]、c[1]、c[2]的默认初始化值为\u0000。
2)静态初始化
静态初始化是指创建数组时指定每个数组元素的初始值,系统会根据初始化情况得到数组的长度。静态初始化语法格式为:
例如:
或者在一条语句里完成,声明数组的同时静态初始化,语法格式为:
例如:
说明:该语句相当于声明了一个一维char数据类型的数组,数组名为c;在内存中定义了3个char类型的数组元素(c.length为3),依次为c[0]、c[1]、c[2],分别初始化为c[0]=a、c[1]=b、c[2]=c。
注意:
不能在静态初始化时指定数组长度。例如,下面这种写法是错误的:
char[]c=new char[3]{a,b,c};
关于静态初始化的语法格式,还有一种简化写法:
例如:
注意:
使用这种方式时,数组的声明和初始化操作要同步,即不能省略数组变量的数据类型。例如,下面这种写法是错误的:
char[]c;
c={'a','b','c'};
3)动态初始化
动态初始化是指默认初始化后,再由程序为数组元素赋值。
例如:
4.一维数组的内存分配
数组本身属于引用数据类型,对于引用数据类型的内存分配需要结合栈和堆理解。
【例2-5】
假设main方法中有一条语句“int[]x=new int[]{1,2,3};”,请说明其内存分配过程。
内存分配情况如图2-11所示。
图2-11 数组内存分配过程(www.xing528.com)
说明:
(1)语句“=”左边,表示声明一个数组变量,JVM在栈内存定义一个int类型的数组引用变量x。
(2)语句“=”右边,Java关键字new表示在堆内存分配对象空间,在堆里分配了3个int类型的数组元素,默认初始化值为0,随后立即分别修改为1,2和3。
(3)JVM将堆内存中数组对象所在数组首地址赋值给栈内存数组引用变量x。
5.一维数组的遍历
所谓数组的遍历,是指沿着某条搜索路线,依次对数组中的每个结点均做一次且仅做一次访问。
当数组中的元素数量不多时,要获取数组中的全部元素,可以使用下标逐个获取元素。但是,如果数组中的元素过多,再使用单个下标则显得烦琐。通常,遍历数组都是使用for循环语句实现的,数组的长度通过数组的length属性获得。
【例2-6】
给定int类型数组{1,2,3},利用for循环语句遍历该数组中的全部元素,并将元素的值输出。
程序Array Demo.java如图2-12所示。
图2-12 一维数组的遍历
说明:
(1)程序第6行打印数组引用变量arr,实际上调用的是arr的toString()方法,arr里存放的是堆内存里数组对象的地址,通过arr找到该数组对象。
(2)在JDK 1.5之后,增加了foreach循环,也称为增强for循环,能在不使用索引的情况下遍历数组或者集合。利用foreach循环,程序第7~10行也可以改成如下语句:
foreach循环遍历数组arr,每次把取得的数组元素赋值给int型临时变量x,每次打印的其实是临时变量x,因此,foreach循环无法改变原数组内容。
6.多维数组
多维数组是指数组维数在二维或以上的数组,多维数组实质上是多个一维数组嵌套组成的。以二维数组为例,二维数组就是一个特殊的一维数组,数组中的每一个元素又是另外一个一维数组。实际开发中,使用最多的就是一维数组,多维数组中较常见的就是二维数组,更高维度的数组几乎不用,下面以二维数组为例进行讲解。
二维数组的声明有如下2种语法格式:
二维数组在使用前,需要先创建,即分配空间。如果要创建一个m行×n列的二维数组,语法格式为:
其中,行数m表示这个二维数组有多少个一维数组,列数n表示每一个一维数组的元素个数。
或者用一条语句完成上述二维数组的声明和创建,语法格式为:
【例2-7】
以main()方法中int[][]x=new int[2][3];语句为例,说明二维数组内存分配情况。
分析:上面的代码相当于定义了一个2×3的二维数组,这个二维数组的长度为2,即可以将其看成2个int[]类型的一维数组,每一个一维数组中的元素又是一个长度为3的一维数组。二维数组内存分配如图2-13所示。
图2-13 二维数组内存分配
说明:
(1)语句“=”左边,在main栈帧中声明一个int[][]型引用变量x,用于存放二维数组的地址。
(2)语句“=”右边,根据指定的行数,JVM在堆中分配空间,创建一个长度为2的一维数组,数组中的元素x[0]和x[1]都是int[]型引用型变量,用于存放一维数组的地址。将分配好的数组内存地址赋值给变量x。
根据指定的列数,在堆中为每一行分配空间,然后将分配好的数组内存地址赋值给x[0]、x[1],即x[0]指向包含x[0][0]、x[0][1]、x[0][2]这三个元素的一维数组,而x[1]指向包含x[1][0]、x[1][1]、x[1][2]这三个元素的一维数组。
【例2-8】
定义一个指定行列的二维数组,并初始化,最后输出该二维数组中的所有元素值。
程序Array Demo2.java如图2-14所示。
图2-14 指定行列的二维数组初始化及遍历
说明:程序第4行,当创建好一个二维数组后会执行默认初始化;第5、6行打印默认初始化后x[0]和x[0][0]的值;第7、8行是程序对数组元素动态初始化,为其中2个数组元素赋值,剩余的元素保持默认值,不做修改;第10~14行用双重for循环完成该二维数组的遍历,外层循环对应二维数组的行,内层循环对应每行中的列。
如果在创建二维数组时,只知道行,不知道列,那么,创建二维数组的语法格式为:
该格式指定这个二维数组有多少个一维数组,但是不指定每个一维数组有多少个元素,而是后续程序动态指定,那时,每个一维数组的长度可以相同,也可以不相同。
【例2-9】
举例说明指定行但不指定列的二维数组的初始化情况。
程序Array Demo3.java如图2-15所示。
图2-15 只指定行不指定列的二维数组初始化
说明:程序第4行没有指定列时,引用变量y[0]和y[1]的值为null;第8、9行指定列分别创建一维数组,并将地址值分别赋给y[0]、y[1]。
和一维数组类似,二维数组也可以通过静态初始化的方式定义,其语法格式为:
例如:
这种格式在声明二维数组的同时分配空间并指定初始化值,适合已确定知道数组元素的情况。简化后的语法格式为:
例如:
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。