HOWTO · Java

在 Java 的构造函数中初始化数组

这篇文章是关于在 Java 的构造函数中初始化一个数组。

本页内容

创建 Java 对象时,如果数组的初始内容取决于构造函数参数或其他初始化步骤,就应在构造函数中初始化数组字段。如果 Java 的默认值正合适,只需分配数组即可。在构造函数中声明的局部数组变量只在构造函数运行期间可用,不会成为对象状态。

在 Java 构造函数中初始化数组字段

将数组声明为实例字段,然后在构造函数中为它赋一个数组引用。每次创建新对象时都会运行构造函数,因此可以根据该对象的输入设置字段,也可以为该对象准备计算得出的值。

数组字段和它所引用的数组是两个不同的东西:字段保存引用,数组保存元素。使用 new int[size] 创建数组时,会分配数组元素,并为每个元素赋予默认值。将局部数组赋给字段(例如 this.values = values)会保存该引用;如果对象需要拥有独立的内容,请使用副本。

使用 Java 的数组默认值

在构造函数体运行之前,Java 就会为实例字段赋予默认值。构造函数创建数组时,Java 也会初始化数组元素:数值基本类型的元素初始值为零(整数类型为 0,float 和 double 为 0.0),char 元素为 \u0000,boolean 元素为 false,而 String 等引用类型的元素为 null。

例如,private int[] values = new int[3]; 会创建一个包含三个零值元素的实例字段。你也可以先声明 private int[] values;,再在构造函数中赋值 values = new int[3];;元素的初始值仍然是零。如果写入这些相同的值并无其他用途,就不必再添加循环。

在构造函数中赋予特定值

对于固定内容,可以使用数组初始化器;如果每个对象都应以调用方提供的内容作为初始值,则可以将这些值作为构造函数参数传入。例如,this.values = new int[] {1, 2, 3}; 会让字段包含这三个值。基于参数的构造函数可以直接赋值,也可以复制输入,具体取决于调用方之后修改数组时是否也应影响对象。

下面的示例将几种做法放在同一个类中:创建一个使用默认值填充的字段、复制传入的值、用同一个值填充另一个字段、根据索引计算值,并在构造期间使用局部数组。程序会显示每个数组,方便你根据自己的类选用相应部分。

import java.util.Arrays;

public class ArraySetup {
    private final int[] defaults = new int[3];
    private final int[] supplied;
    private final int[] repeated = new int[3];
    private final int[] calculated = new int[3];

    public ArraySetup(int[] source) {
        supplied = Arrays.copyOf(source, source.length);
        Arrays.fill(repeated, 7);
        Arrays.setAll(calculated, index -> (index + 1) * 2);

        int[] local = {4, 5};
        System.out.println("Local: " + Arrays.toString(local));
    }

    public static void main(String[] args) {
        int[] input = {3, 6, 9};
        ArraySetup item = new ArraySetup(input);
        System.out.println("Defaults: " + Arrays.toString(item.defaults));
        System.out.println("Supplied: " + Arrays.toString(item.supplied));
        System.out.println("Repeated: " + Arrays.toString(item.repeated));
        System.out.println("Calculated: " + Arrays.toString(item.calculated));
    }
}

在构造函数中创建局部数组

在构造函数中声明 int[] local = {4, 5}; 这样的变量,会创建一个局部变量。构造函数中的代码可以使用它,但构造函数返回后,其他方法无法访问它。如果该数组应成为所创建对象状态的一部分,就应将它赋给实例字段,例如 this.values = new int[] {4, 5};。

局部数组仍可用作构建字段时的临时输入,也可用于只在初始化期间需要的计算。要分清这一点:如果字段或其他对象保存了数组引用,数组对象仍可能被访问;但仅有构造函数中的局部变量并不能让数组持续可用。

使用 Arrays.copyOf()、Arrays.fill() 或 Arrays.setAll()

应根据数组内容的确定方式选择辅助方法。Arrays.copyOf(source, source.length) 会创建一个新数组,其中包含源数组的元素。请求的长度较短或较长时,它会复制能够容纳的元素;较长副本中多出的元素会获得其类型的默认值。对于对象数组,这是浅拷贝:复制的是引用,而不是引用所指向的对象。

如果每个元素都应设为同一个值,请使用 Arrays.fill(array, value)。如果要根据索引计算每个值,请使用 Arrays.setAll(array, index -> expression)。在示例中,填充操作会将数组中的元素都设为 7,而索引函数会依次赋予偶数。这些方法会修改传入的数组,不会创建替代数组。

选择合适的初始化方式

如果默认值就是所需的初始状态,请创建指定长度的数组并使用默认值。少量固定值可用数组初始化器;如果内容由调用方决定,则可通过构造函数参数传入。对象需要拥有独立的数组结构时,请复制输入数组;需要重复填入同一个值时使用 Arrays.fill(),需要根据索引赋值时使用 Arrays.setAll()。只要数组需要在构造完成后作为对象状态继续使用,就应将它声明为实例字段。