概述及定义
在Java语言中,数组是动态创建的对象。
数组的元素:数组对象所包含的变量。
数组的元素类型(component type):数组所有元素都具有的相同的类型。
源数组的成员类型(element type):数组元素类型也可以是数组类型,递归考察数组元素,最终能找到不是数组类型的元素, 该元素的类型就叫作“源数组成员类型”,并且在这个数据结构级别上的元素就称为“源数组的成员”。
数组声明
Java支持两种数组声明的语法:
1 | type[] identifier; |
第一种声明方式是把[]作为类型的一部分,第二种则作为变量声明符的一部分。第二种语法继承自C,不推荐使用。
不过,这两种声明方式是可以混合使用的,这种“混合标记法”很特别但不太常见,比如:
1 | byte[] b[]; // 等价于 byte[][] b; |
在声明单个数组变量时,“混合标记法”看起来很奇怪,所以通常要么用上述第一种声明语法,要么用第二种; 但是在一个声明语句中声明多个源数组成员类型相同的数组变量时,“混合标记法”是相当简洁的。
无论什么情况下,我们都应该尽量使用第一种声明语法,它使得数组类型更为清晰直观,可读性较强。 第二种声明语法或说“混合标记法”从可读性上来说都是比较差的,而且在一条声明语句中声明多个变量也是不推荐的。
数组创建及初始化
数组可以在声明的同时创建。
数组的创建有两种方式:数组创建表达式或数组初始化器。
数组创建表达式
语法如下:
1 | type[] array = new type[length]; |
比如:
1 | double[] ds = new double[10]; |
使用数组创建表达式创建数组时,只指定了数组长度,而没有指定数组元素的初始值,系统将为每个元素赋默认初始值, 这称为“动态初始化”。设置默认初始值的操作是通过将数组元素对应的内存“清零”而达成的。
数组初始化器
语法如下:
1 | type[] array = {e1, ...}; |
比如:
1 | int[] ints = {1, 2, 3,}; |
数组初始化器是用花括号括起来并用逗号分隔的表达式列表。最后一个表达式后的逗号是可选的(有则会被忽略), 这一特性使得维护长列表变得更容易。
使用数组初始化器创建数组时,指定的是所有数组元素的初始值,系统将根据表达式列表分配相应长度的空间,由于数组元素初始值是指定的, 所以也叫“静态初始化”。
需要注意的是,数组初始化器是一个数组常量,而数组常量只能在初始化操作中使用。因此,如果不是声明赋值就不能使用数组初始化器, 否则是一个编译错误。如果只是一般的为数组变量赋值,应该使用以下语法:
1 | array = new type[]{e1, ...}; |
数组的嵌套
数组的元素也可以是数组,这就形成了嵌套,数组声明中方括号对数量表示数组嵌套的深度,每一对方括号对应一个维度。
Java中的数组均属于嵌套数组,而常提及的“多维数组”,严格来说,在Java中是没有对应概念的,但是C#中有多维数组。 因此,对比Java和C#中相应的数组概念,将有助于我们理解数组的嵌套和多维。
C#中多维数组指数组有多个维度,比如:
1 | int[,] array = new int[4, 2]; // C# |
声明创建了一个二维数组,是4行2列的。严格来说,Java中没有与之对应的多维数组的概念与语法。
C#中还有一种数组叫作“交错数组”。交错数组是元素为数组的数组。交错数组元素的维度和大小可以不同。交错数组有时称为“数组的数组”。
C#中交错数组的概念跟Java中的嵌套数组的概念就很类似了,而且语法也几乎一样。
说回嵌套数组,虽然Java没有严格意义上的多维数组,但是,如果把嵌套数组每个级别的元素的维度大小设置为一样的,功能就与多维数组相似了。
在Java语言中,通俗意义上的“多维数组”概念上指的其实是每个级别的子数组维度都相同的嵌套数组,通常说的是“矩形数组”的概念。
而嵌套数组最大的特点是,数组元素的维度和大小可以不同,这种与“矩形数组”相区别的嵌套数组通常称为“参差数组”。
1 | int[][] ints = { {1, 2}, {1, 2, 3} }; |
数组的复制
嵌套数组的clone是浅复制,即它只创建单个新数组,子数组是共享的。
通常更多的是数组数据的复制,System
类中提供了复制数组数据的方法:
1 | public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) |
此外,在java.util.Arrays
类中提供了更多数组操作的方法,当然也包括复制数据的方法。
数组类型
尽管数组类型不是类,但是每一个数组的Class对象起到的作用都像是如此:直接超类是Object
;实现Cloneable
和java.io.Serializable
接口。
但是,值得注意的是,数组类型的超类型关系与超类关系不同。Integer[]
的直接超类型是Number[]
,但直接超类是Object
。