Skip to content

Latest commit

 

History

History
214 lines (114 loc) · 187 KB

10-数组.md

File metadata and controls

214 lines (114 loc) · 187 KB

第十章 数组

在 Java 编程语言中,数组是对象(4.3.1),是动态创建的,并且可以赋值给类型 Object(4.3.2)的变量。类 Object 的所有方法可以在数组上调用。

数组对象包含许多变量。变量的数量可以是零,在这种情况下,数组被称为是空的。数组中包含的变量没有名称;反之通过使用非负整数索引值的数组访问表达式来引用它们。这些变量被称为数组的组件。如果数组具有 n 个组件,我们说 n 是数组的长度;使用从 0 到 n-1(包括)的索引来引用数组的组件。

数组的所有组件具有相同的类型,称为数组的组件类型。如果数组的组件类型是 T,则数组本身的类型写作 T[]。

类型 float 的数组组件的值总是浮点值集(4.2.3)的元素;类似地,类型 double 的数组组件的值总是双精度值集的元素。不允许类型 float 的数组组件的值是浮点扩展指数值集的元素同时也不是浮点值集的元素,也不允许类型 double 的数组组件的值是双精度扩展指数值集的元素同时也不是双精度值集的元素。

数组的组件类型本身可以是数组类型。此类数组的组件可以包含子数组的引用。如果从任何数组类型开始考虑其组件类型,然后(如果也是数组类型)该类型的组件类型,等等,最终必须到达一个不是数组类型的组件类型;这被称为原始数组的元素类型,而这层数据结构的组件被称为原始数组的元素。

在某些情况下,数组的元素可以是数组:如果元素类型是 Object 或 Cloneable 或 java.io.Serializable,则某些或所有元素都可能是数组,因为任何数组对象都可以赋值给这些类型的任何变量。

10.1. 数组类型

数组类型在声明和强制转换表达式(15.16)中使用。

数组类型写作元素类型的名称,后跟若干个空方括号对 []。括号对的数量表示数组嵌套的深度。

数组类型中的每个括号对可以被类型注解(9.7.4)标注。注解应用于跟在它后面的括号对(或可变参数声明中的省略号)。

数组的元素类型可以是任何类型,无论基元还是引用。特别是:

    * 允许数组用接口类型作为元素类型。

      此类数组的元素可能具有 null 引用或任何实现此接口的类型的实例作为其值。

    * 允许数组用 abstract 类类型作为其元素类型。

      此类数组的元素可能具有 null 引用或此 abstract 类的任何本身不是 abstract 的子类的实例作为其值。

数组的长度不是其类型的一部分。

数组类型的父类型在 4.10.3 中指定。

数组类型的父类型关系与父类型关系不同。根据 4.10.3,Integer[] 的直接父类型是 Number[],但根据 Integer[](10.8)的 Class 对象,Integer[] 的直接父类型是 Object。这实际上不重要,因为 Object 也是所有数组类型的父类型。

10.2. 数组变量

数组类型的变量持有对象的引用。声明一个数组类型的变量并不创建一个数组对象或为数组组件分配任何空间。它仅仅创建变量本身,其可以包含一个数组的引用。但是,声明符(8.3,9.3,14.4.1)的初始化器部分可以创建数组,然后,它的引用变成此变量的初始值。

avatar

变量的数组类型依赖于在变量声明的开始处作为类型的一部分出现的括号对,或作为变量声明符的一部分,或两者。特别地,在字段、形式参数或局部变量(8.3,8.4.1,9.3,9.4,14.4.1.,14.14.2,15.27.1)的声明中,变量的数组类型由以下表示:

    * 在声明开始处出现的元素类型;然后,

    * 任何跟在声明符(不适用于可变参数)中的变量的 Identifier 之后的括号对;然后,

    * 任何在声明开始处的类型中出现的括号对(其中可变参数的省略号被视为括号对)。

方法(8.4.5)的返回类型可以是数组类型。精确的数组类型依赖于在方法声明开始处作为类型的一部分出现的括号对,或方法的形式参数列表之后,或两者。数组类型由以下表示:

    * 在 Result 中出现的元素类型;然后,

    * 任何跟在形式参数列表后面的扩汉对;然后,

    * 任何在 Result 中出现的括号对。

我们不建议在数组变量声明中使用“混合的表示法”,其中括号对同时出现在类型和声明中;方法声明中也不建议,其中括号对同时出现在形式参数列表之前和之后。

avatar

avatar

一旦创建一个数组对象,它的长度永不改变。为了使数组变量引用不同长度的数组,不同数组的引用必须赋值给此变量。

数组类型的单个变量可能包含不同长度数组的引用,因为数组的长度不是其类型的一部分。

如果数组变量 v 具有类型 A[],其中 A 是引用类型,则 v 可以持有任何数组类型 B[] 的实例的引用,假设 B 可以赋值给 A(5.2)。这可能在随后的赋值中导致运行时异常;有关讨论请参见 10.5。

10.3. 数组创建

通过数组创建表达式(15.10.1)或数组初始化器(10.6)创建数组。

数组创建表达式指定元素类型,嵌套数组的层数,以及至少一层嵌套的数组的长度。数组的长度可用作 final 实例变量 length。

数组初始化器创建数组,并为其所有组件提供初始值。

10.4. 数组访问

通过数组访问表达式(15.10.3)来访问数组组件,此表达式由值是后跟用 [ 和 ] 封闭的索引表达式的数组引用组成。

所有数组都是从 0 开始。长度 n 的数组可以通过 0 到 n-1 的整数来索引。

avatar

必须通过 int 值对数组进行索引;shrot、byte 或 char 值也可以用作索引值,因为它们受到一元数字提升(5.6.1)的影响,并且会变成 int 值。

试图用 long 索引值访问数组组件会产生一个编译时错误。

所有的数组访问都在运行时检查;试图使用小于零或打大于或等于数组长度的索引会导致抛出(15.10.4)一个 ArrayIndexOutOfBoundsException。

10.5. 数组存储异常

对于类型为 A[] 的数组,其中 A 是引用类型,此数组组件的赋值在运行时检查,以确保正在用于赋值的值可以赋值给组件。

如果正在用于赋值的值的类型与组件类型不是赋值兼容(5.2)的,则抛出一个 ArrayStoreException。

如果数组的组件类型不是可具体化的(4.7),则 Java 虚拟机无法执行前一段中描述的存储检查。这就是禁止使用非具体化元素类型的数组创建表达式的原因。可以声明数组类型的变量,其元素类型是非具体化的,但将数组创建表达式的结果赋值给该变量必然会导致未检查警告(5.1.9)。

avatar

10.6. 数组初始化器

数组初始化器可以在字段声明(8.3,9.3)或局部变量声明(14.4)中指定,或作为数组创建表达式(15.10.1)的一部分,以创建一个数组并提供一些初始值。

ArrayInitializer:
    { [VariableInitializerList] [,] }

VariableInitializerList:
    VariableInitializer {, VariableInitializer}

为了方便,以下来自 8.3 的产生式在这显示:

VariableInitializer:
    Expression
    ArrayInitializer

数组初始化器写作由括号 { 和 } 封闭的逗号分隔的表达式列表。

尾部逗号可以出现在数组初始化器中最后一个表达式的后面,并被忽略。

每个变量初始化器必须与数组的组件类型是赋值兼容的(5.2),否则发生一个编译时错误。

如果正在用于初始化的数组的组件类型不是可具体化的(4.7),则这是一个编译时错误。

被构造的数组的长度等于直接由数组初始化器的括号封闭的变量初始化器的数量。为该长度的新数组分配空间。如果没有足够的空间分配该数组,则数组初始化器由抛出一个 OutOfMemoryError 突然地完成。否则,将创建一个指定长度的一维数组,并且数组的每个组件初始化为其默认值(4.12.5)。

然后由数组初始化器的括号封闭的变量初始化器立即从左到右执行,以它们在源代码中出现的原文顺序。第 n 个变量初始化器指定第 n-1 个数组组件的值。如果变量初始化器的执行突然地完成,则数组初始化器的执行以同样的理由突然地完成。如果变量初始化器表达式正常地完成,则数组初始化器正常地完成,用新初始化的数组的值。

如果组建类型是数组类型,则指定组件的变量初始化器本身可以是一个数组初始化器,即,可以嵌套数组初始化器。在这种情况下,嵌套的数组初始化器的执行构造并初始化一个数组对象,通过以上算法的递归应用,并且将它赋值给组件。

avatar

10.7. 数组成员

数组类型的成员如下所示:

    * public final 字段 length,其包含了数组组件的数量。length 可以是正数或零。

    * public 方法 clone,其重写了类 Object 中相同名称的方法,且不抛出任何异常。数组类型 T[] 的 clone 方法的返回类型 是 T[]。

      多维数组的 clone 是表面的,也就是说它只创建一个新数组。子数组是共享的。

    * 所有从类 Object 继承的成员;不是继承的唯一的 Object 方法是它的 clone 方法。

      另一个特别注意的 Object 的 public 和非 public 方法之间的不同,请参见 9.6.4.4。

因此,数组具有与以下类相同的 public 字段和方法:

avatar

注意,以上代码中强制转换为 T[] 会生成一个未检查警告(5.1.9),如果数组真是以这种方式实现的。

avatar

avatar

10.8. 数组的 Class 对象

每个数组有一个相关联的 Class 对象,与具有相同组件类型的所有其它数组共享。

尽管数组类型不是类,每个数组的 Class 对象看起来就像:

* 每个数组类型的直接父类是 Object。

* 每个数组类型实现接口 Cloneable 和 java.io.Serializable。

avatar

avatar

10.9. 一个字符数组不是一个 String

在 Java 编程语言中,不像 C,一个 char 数组不是一个 String, String 和 char 数组也不是由 '\u0000'(NUL 字符)终止的。

String 对象是不可变的,即,其内容从不改变,而 char 数组具有可变的元素。

类 String 中的方法 toCharArray 返回字符数组,其包含与该 String 一样的字符序列。类 StringBuffer 在可变的字符数组上实现了有用的方法。