Java 即使没有直接初始化也会创建对象吗?

Does Java create object even if it's not initialized directly?

如果我像这样直接初始化字符串数组 String[] Distro = Distros.split(","); 那么它会创建一个对象,因为变量 Distro 保存着数组。

但是如果我这样做那么它也会创建一个对象吗?

        String Distros = "CentOS,RHEL,Debian,Ubuntu";

        for (String s : Distros.split(",")) {
            System.out.println(s);
        }

我的目标是减少对象创建以最大程度地减少垃圾。

方法 split(delimiter) returns 来自基于定界符的字符串的字符串数组,你所做的为每个创建字符串数组,它的范围在每个之后结束所以它符合条件GC 释放它

    String Distros = "CentOS,RHEL,Debian,Ubuntu";
    for (String s : Distros.split(",")) {
        System.out.println(s);
    }

,相当于

    String Distros = "CentOS,RHEL,Debian,Ubuntu";

    System.out.println("start scope");
    {
      String[] splitArray = Distros.split(",");
      for (String s : splitArray) {
        System.out.println(s);
      }
    }
    System.out.println("end scope");

你的推理“然后它会创建一个对象,因为变量 Distro 持有数组”表明你混淆了对象创建和变量赋值。

对象是由表达式Distros.split(",")创建的,不是后面的赋值。当您认为 split 方法是一个普通的 Java 方法创建和返回数组而不知道调用者将如何处理结果时,这应该变得很明显。

当操作发生在性能关键代码中时,您可能会使用

int p = 0;
for(int e; (e = Distros.indexOf(',', p)) >= 0; p = e+1)
    System.out.println(Distros.substring(p, e));
System.out.println(Distros.substring(p));

相反。值得指出的是,这节省了数组创建但仍执行子字符串的创建,这是它更昂贵的方面。在不知道您实际要对子字符串做什么的情况下,不可能说是否有可以节省子字符串创建的替代方法¹。

但是这个循环相对于split方法还是有优势的。 split 方法创建所有子字符串和 returns 一个包含对它们的引用的数组,强制它们在整个循环期间同时存在。上面的循环在需要时调用 substring 并且在转到下一个时不保留引用。因此,字符串不会一直存在,垃圾收集器可以根据当前内存利用率自由决定何时收集它们。


¹ 我假设打印只是一个例子。但是为了保持示例,您可以替换

    System.out.println(Distros.substring(p, e));

    System.out.append(Distros, p, e).println();

问题是,这仅隐藏了子字符串的创建,至少在最终将在幕后执行子字符串创建的参考实现中是这样。

另一种选择是

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                        new FileOutputStream(FileDescriptor.out)));
try {
    int p = 0; for(int e; (e = Distros.indexOf(',', p)) >= 0; p = e+1) {
        bw.write(Distros, p, e - p);
        bw.write(System.lineSeparator());
    }
    bw.write(Distros, p, Distros.length() - p);
    bw.write(System.lineSeparator());
    bw.flush();
}
catch(IOException ex) {
    ex.printStackTrace();
}

真正编写字符串而不创建子字符串。但它迫使我们处理潜在的异常,PrintStream 通常隐藏这些异常。