【问题标题】:Declaring arrays without using the 'new' keyword in Java在 Java 中不使用“new”关键字声明数组
【发布时间】:2023-03-06 17:25:02
【问题描述】:

以下两个声明有区别吗?

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}

标签: java arrays new-operator


【解决方案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 = 10int[] arr = {},则 references 分配在堆栈上,但它们引用的 objects 分配在堆。引用只是可以分配以指向另一个对象的指针。
  • 将逃逸分析带入其中并不一定会改善答案。 EA 甚至可能导致一个对象永远不会被分配。实际上,术语“堆栈”和“堆”(C 等编程语言使用它的方式)与 JVM 所做的不匹配。最好说,包括数组在内的对象都存储在托管内存中,而忘记“堆栈”和“堆”这两个词。
  • @Holger JVM 规范没有提到“托管内存”;但确实提到了"heap"这个词:“Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的堆。堆是运行时数据区域,所有类实例和数组的内存都从该区域分配。”。
【解决方案2】:

Objects 驻留在heapArrays 是 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]可以同时用于assignmentinitialization,但{1, 2}只能用作@987654323 @。 (注意new int[] {1, 2} 也可以用作assignmentinitialization

      • new int [5] 将所有条目设置为零,但{1, 2}new int[] {1, 2} 在各自的条目中设置12

      • 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
        

      有用的材料:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多