在 Java 中不使用“new”关键字声明数组
Posted
技术标签:
【中文标题】在 Java 中不使用“new”关键字声明数组【英文标题】:Declaring arrays without using the 'new' keyword in Java 【发布时间】:2016-12-26 05:35:51 【问题描述】:以下两个声明有区别吗?
int arr[] = new int [5];
和
int arr1[] = 1,2,3,4,5;
arr1
是声明在栈上还是堆上?
【问题讨论】:
数组是java中的一个对象..所以它存储在堆上 @Prasanna no,就像对象中int
字段的值存储在堆上一样。
@PrasannaKumar 与对象中的 int 实例变量的情况相同。它驻留在对象内的堆中;在数组的情况下相同。
@Prasanna 只是一个原语并不意味着它驻留在堆栈中。局部原始变量在堆栈上;包含在对象中的原语包含在对象中,并且对象总是堆分配的。
注意你也可以new int []1,2,3,4,5
。
【参考方案1】:
明显的区别是一个全为零,另一个包含[1..5]。
但这是唯一的区别。两者都是 5 元素 int 数组,都以相同的方式分配。使用大括号而不是 new
声明只是语法上的便利。
注意,这种形式只能在声明数组时使用:
int[] blah =
但不是
int[] blah;
blah = ;
或
return ;
对象(数组就是对象)在堆上分配。
【讨论】:
@Andrew 已更新。我怀疑它的工作范围比您暗示的要广泛一些(例如,在表达式中分配给数组变量);但我们不要让事情复杂化:) @Muhammad 视情况而定。如果int i = 10;
是局部变量声明,则10在栈上;如果它是一个成员变量声明,它在堆上。 Integer i = 10;
与Integer.valueOf(10)
相同,所以i
指的是堆的值。
对于堆栈上的内容和堆上的内容似乎有些混淆。要记住的一件事是,无一例外,局部变量总是分配在堆栈上。总是。并且对象总是在堆上分配。现在,如果您声明对对象的引用,例如 Integer i = 10
或 int[] arr =
,则 references 分配在堆栈上,但它们引用的 objects 分配在堆。引用只是可以分配以指向另一个对象的指针。
将逃逸分析带入其中并不一定会改善答案。 EA 甚至可能导致一个对象永远不会被分配。实际上,术语“堆栈”和“堆”(C 等编程语言使用它的方式)与 JVM 所做的不匹配。最好说,包括数组在内的对象都存储在托管内存中,而忘记“堆栈”和“堆”这两个词。
@Holger JVM 规范没有提到“托管内存”;但确实提到了"heap"这个词:“Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的堆。堆是运行时数据区域,所有类实例和数组的内存都从该区域分配。”。 【参考方案2】:
Objects
驻留在heap
。 Arrays
是 Java 编程语言中的 object type
。官方文档here
【讨论】:
【参考方案3】:第一行在堆上放置一个新对象——一个包含四个元素的数组对象——每个元素包含一个默认值为 0 的 int。
第二个做同样的事情,但使用非默认值进行初始化。再深入一点,这条单行代码做了四件事:
声明一个名为 arr1 的 int 数组引用变量 创建一个长度为五(五个元素)的 int 数组。 用值 1,2,3,4,5 填充数组的元素 将新数组对象分配给引用变量 arr1如果您使用对象数组而不是基元:
MyObject[] myArray = new MyObject[3];
那么你在堆上有一个数组对象,有三个 MyObject 类型的空引用,但是你没有任何 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 虚拟机可能会执行逃逸分析并决定分配对象......在堆栈上”。我相信这是真的。所以你的问题的答案是:这取决于。通常在堆上。
【讨论】:
有趣。如果你不介意,我会在我的回答中包含这个事实——当然是引用这个答案。 请随意。我希望我能找到更好的来源,也许我应该看更长的时间。 还有一些 JVM 在运行时执行 Escape Detection 而不是在编译时执行 Escape Analysis。在这样的 JVM 上,对象将总是在堆栈上分配,并用标记标记,当 JVM 检测到标记标记超出本地范围时,它会将对象复制到堆并打补丁列出所有对它的引用。转义分析反其道而行之:在堆上分配对象,除非 EA 可以证明引用确实没有转义。不幸的是,EA 相当于解决了停机问题,因此会有分配可能在堆栈上但不能…… ... 被编译器证明是安全的,所以编译器能做的唯一明智的事情就是在堆上分配。逃逸检测发生在运行时,因此不受停机问题的限制。【参考方案5】:new int [5]
可以同时用于assignment和initialization,但1, 2
只能用作@987654323 @。 (注意new int[] 1, 2
也可以用作assignment和initialization)
new int [5]
将所有条目设置为零,但1, 2
和new int[] 1, 2
在各自的条目中设置1
和2
。
Both are on heap,你可以保存他们的对象引用。
int arr[] = new int [5];
// arr: object reference to the array
或
int arr[] = 1, 2, 3, 4, 5;
// arr: object reference to the array
有用的材料:
Java: define terms initialization, declaration and assignment【讨论】:
以上是关于在 Java 中不使用“new”关键字声明数组的主要内容,如果未能解决你的问题,请参考以下文章