如何在Java中分配对象数组?

3

在学习Java教程时,我一直在使用以下语法来分配数组:

// Ok, all the elements are zero upon creation
int[] a = new int[5];

// I can set all the elements however I want!
for (int i = 0; i < a.length; i++)
     a[i] = i+1

自从我开始使用类之后,事情让我感到困惑:
class Employee
{
    public Employee(String name, int age) {
         emp_name = n;
         emp_age = a;
    }

    void setName(String name) {
          emp_name = name;
    }

    void setAge(int age) {
          emp_age = age;
    }

    private String emp_name;
    private String emp_age;

}

我在主函数中使用这个类,如下所示:

Employee[] staff = new Employee[3];

我希望这行代码能够返回一个由三个对象组成的数组,这些对象应该通过构造函数进行默认初始化。

然而,当我执行以下代码时,运行时会抛出异常。

staff[0].setName("Test");

在C++中,这是相当简单的,不需要额外的new:
Employee *e[3];

经过进一步搜索,我发现我仍然需要为数组中的每个元素分配内存才能开始使用它们。如果是这样,使用new运算符的目的是什么?它不是已经为数组分配了内存吗?为什么在int数组中没有这种情况发生?


Employee[] staff = new Employee[3]; 是一个大小为三的 Employee 数组,可以容纳三个 Employee 实例。int[] a = new int[5]; 是一个包含五个基本整数类型的数组。实质上并没有什么不同,只是使用方式不同而已。 - Shahzeb
请移除C++标签,因为您的问题不是关于C++,而是关于Java。Java和C++是不同的编程语言,对数组的处理方式也不同。 - Thomas Matthews
3个回答

7

当你创建一个对象数组时,你实际上创建了一个充满null对象的数组。该数组充满“虚无”。只有在显式创建Employee之后,才会真正创建一个Employee对象。

Employee[] staff = new Employee[3];

此时您的数组看起来是这样的:

[null] [null] [null]

接下来,您可以通过以下方法创建一个员工:

staff[0] = new Employee();

此时,会调用默认的Employee构造函数,你的数组现在在第一个位置有一个Employee对象:

[Employee1][null][null]

现在,由于第一个位置上已经有了一个实际的Employee对象,你应该能够调用:

staff[0].setName("Test");

更新:

关于问题:

为什么要使用 new 运算符?它不已经为数组分配了内存吗?为什么 int 数组不会出现这种情况?

类似的问题已经在这里这里被问过。简单来说,当你创建对象数组时,实际上是创建了一个引用数组。刚开始,所有这些引用都指向 null 对象。当你执行 staff[0] = new Employee(); 时,你实际上做了两件事情:

  1. 创建并为 "new" 的 Employee 分配内存
  2. 告诉 staff[0] 现在指向新的 Employee 对象,而不是 null

4
在Java中,所有的“对象”实际上都只是引用(指针,有点像)。因此,您正确地认为Java自动初始化数组中的值,但在第二个数组中,您有一个引用数组,它们基本上是内存地址,因此被初始化为null
您之所以会遇到异常,是因为您正在调用null引用上的方法。另外,请注意,在创建基元数组时,您正在使用具有不同索引的赋值运算符。在对象示例中,您只是立即使用数组中的值,而没有对它们进行赋值。因此,从技术上讲,在基元数组示例中所做的事情可以在对象数组中完成,因为它只是简单的赋值。

Java使用引用而不是指针。这里有一些区别 - Turing85
我知道它们之间有差异,但基本上你可以将它们视为相同的,既然@cpx谈到了C++和指针,我想将引用比作指针会更有助于他的理解。 - Adam Evans
1
特别是如果他/她来自C/C++背景,不建议错误使用术语。人们可能会假设一些不正确的事情(例如,“指针算术和内存重新解释是可能的”)。 - Turing85
在C++中,new的目的始终是分配一些内存。当我思考语句Employee[] staff = new Employee[3];时,它并没有分配任何东西,这让我有点困惑。它只是声明了一个数组,但没有对内存做任何操作。所以,如果我认为它只是分配了一个引用数组,因为它们位于堆上,因此我们使用new运算符,那么我的理解是正确的吗?当我第一次看到代码时,它似乎像是我正在创建一个二维数组,即首先使用new运算符创建一个数组,然后为数组中的每个元素使用new运算符。 - cpx
@cpx 你说得对,“它只是在分配一个引用数组,因为它们存在于堆上,所以我们使用'new' 运算符”。语句“Employee[] staff = new Employee[3];”告诉JVM“给我一个Employee对象的引用数组”。这个语句中并没有指示给你实际的员工;你需要在其他语句中告诉JVM如何做到这一点。 - Adam Evans

3

在Java中,当您创建一个对象数组时,默认情况下数组中每个元素的值为null。没有调用任何构造函数。您需要遍历该数组并为每个元素创建一个对象。

例如:

for(int i=0; i<staff.length; i++){
    staff[i] = new Employee("name" + i, 20);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接