在Java中声明数组而不使用'new'关键字

30

以下两个声明有什么区别吗?

int arr[] = new int [5];

int arr1[] = {1,2,3,4,5};

数组 arr1 是在栈上还是堆上声明的?


4
数组是Java中的一个对象,因此它存储在堆上。 - Sandeep Reddy Goli
2
@Prasanna 不,与对象中的int字段的值存储在堆上的方式相同。 - Andy Turner
2
@PrasannaKumar 对象中的int实例变量与数组相同,都存储在堆内存中。 - Pravat Panda
2
@Prasanna 简单地说,原始数据类型并不意味着它们驻留在堆栈中。本地原始变量位于堆栈上;包含在对象内部的原始数据类型也在对象内部,而对象是始终在堆上分配的。 - Andy Turner
4
注意,您还可以使用new int[]{1,2,3,4,5} - Boris the Spider
显示剩余6条评论
5个回答

36

显而易见的区别是一个数组全是零,另一个包含[1..5]。

但这是唯一的区别。两者都是有5个元素的int数组,都是以相同的方式分配的。使用括号而不需要new只是语法上的方便。

请注意,只有在声明数组时才能使用这种形式:

int[] blah = {}

但不是

int[] blah;
blah = {};
或者
return {};

对象(包括数组)是在堆上分配的。


2
@Muhammad 这取决于情况。如果 int i = 10; 是一个局部变量声明,那么10就在栈上;如果它是一个成员变量声明,那么它就在堆上。Integer i = 10; 等同于 Integer.valueOf(10),所以 i 引用的是堆中的值。 - Andy Turner
1
似乎有些混淆关于栈和堆上的内容。需要记住的一件事是,除外,局部变量总是分配在栈上。总是。而对象总是分配在堆上。现在,如果您声明一个对象的引用,例如Integer i = 10int[] arr = {},则_引用_分配在栈上,但它们所指向的_对象_分配在堆上。这些引用只是可以被分配到另一个对象的指针。 - Klitos Kyriacou
2
将逃逸分析引入其中并不一定会改善答案。EA甚至可能导致对象永远不会被分配。实际上,“堆栈”和“堆”(编程语言如C所使用的方式)与JVM所做的不匹配。最好说,包括数组在内的对象存储在托管内存中,并忘记“堆栈”和“堆”这些词。 - Holger
1
@Holger,JVM规范没有提到“托管内存”,但提到了“堆”这个词(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.3):“Java虚拟机有一个堆,该堆在所有Java虚拟机线程之间共享。堆是运行时数据区,用于分配所有类实例和数组的内存。”。 - Andy Turner
1
@Andy Turner:这就是我说的,它不是像C语言那样使用的堆。如果堆是“为所有类实例和数组分配内存的运行时数据区域”,那么在堆上分配的Java对象仍然是在堆上,根据定义。这就是我评论的全部意义,“堆”在JVM中的定义与C语言等编程语言的使用方式不同。这就是为什么我建议不要使用“堆”这个词,而是使用术语“托管内存”,这是俗称“垃圾收集内存”的正确术语。 - Holger
显示剩余17条评论

8
第一行将一个新对象放在堆上——一个包含四个元素的数组对象,每个元素都包含一个默认值为0的int。
第二行也是这样做,但使用非默认值进行初始化。更深入地讲,这一行代码执行了四个操作:
  • 声明了一个名为arr1的int数组引用变量
  • 创建了一个长度为五的int数组(五个元素)
  • 将数组的元素填充为1、2、3、4、5
  • 将新的数组对象分配给引用变量arr1
如果您使用的是对象数组而不是原始类型:
MyObject[] myArray = new MyObject[3];

然后,您在堆上拥有一个数组对象,其中包含三个类型为MyObject的null引用,但您没有任何MyObject对象。下一步是创建一些MyObject对象并将它们分配给由myArray引用的数组中的索引位置。

myArray[0]=new MyObject();
myArray[1]=new MyObject();
myArray[2]=new MyObject();

总结:数组必须在构造时指定大小。JVM需要这个大小来为新的数组对象在堆上分配适当的空间。


4

我同意其他答案,无论您使用哪种声明方式,大多数情况下都会在堆上分配数组。不过,根据Can Java allocate a list on stack?中的顶级答案,“在特殊情况下,Java虚拟机可能会执行逃逸分析并决定将对象分配到堆栈上”。我相信这是真的。因此,对于您的问题的答案是:取决于情况。通常是在堆上分配。


有趣。如果您不介意,我会在我的答案中引用这个事实 - 当然要注明出处。 - Andy Turner
请放心,尽管去做。我希望我能找到一个更好的来源,也许我应该再多找一会儿。 - Ole V.V.
2
还有一些JVM在运行时执行逃逸检测而不是在编译时执行逃逸分析。在这样的JVM上,对象将始终在堆栈上分配,并带有标记。当JVM检测到标记标签逃逸本地作用域时,它将把对象复制到堆并修补所有引用。逃逸分析的工作方式相反:除非EA可以证明引用不会逃逸,否则在堆上分配对象。不幸的是,EA等价于解决停机问题,因此可能会有可以在堆栈上分配但无法分配的分配... - Jörg W Mittag
1
编译器可以证明在堆上分配内存是安全的,因此编译器能做的唯一明智的事情就是在堆上进行分配。逃逸检测发生在运行时,因此不受停机问题的限制。 - Jörg W Mittag

4
  • new int [5] can be used for both assignment and initialization, but {1, 2} only can be used as declaration with initialization. (Note that new int[] {1, 2} also can be used as both assignment and initialization)

  • new int [5] sets all entries to zero, but {1, 2} and new int[] {1, 2} sets 1 and 2 in respective entries.

  • Both are on heap, you can save their object reference.

    int arr[] = new int [5];
    // arr: object reference to the array
    

    or

    int arr[] = {1, 2, 3, 4, 5};
    // arr: object reference to the array
    

有用的资料:


3

对象存储在中。在Java编程语言中,数组对象类型。官方文档在此


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