链接方法与在 Java 中创建临时变量是否会影响内存分配?

Does chaining methods vs making temporary variables in Java impact memory allocation?

在Java中,如果我在一个对象上执行多个方法,我可以将它们链接起来,或者我可以创建一个临时变量,就像这样

链接

    System.out.println( str.substring(0,4).substring(0,2));

温度变量

    String tmp = str.substring(0,4);
    tmp = tmp.substring(0,2);
    System.out.println(tmp);

显然,在此示例中差异可以忽略不计,但当您对数千个 strings/some 其他对象执行此操作时可能会产生影响。

我的问题是,就不进行额外的对象分配或填充堆(从而使 GC 更快地被调用)而言,这些更多 "efficient" 之一吗?

我试图在几个字符串的循环中比较两者的字节码,但它看起来很相似,最后几行没有。我不理解所有的字节码调用,所以我不确定这些是否与分配新对象有关。

Compiled from "TestNoTmp.java"
public class TestNoTmp {
  public TestNoTmp();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_4
       1: anewarray     #2                  // class java/lang/String
       4: dup
       5: iconst_0
       6: ldc           #3                  // String These
       8: aastore
       9: dup
      10: iconst_1
      11: ldc           #4                  // String Are__
      13: aastore
      14: dup
      15: iconst_2
      16: ldc           #5                  // String Some_
      18: aastore
      19: dup
      20: iconst_3
      21: ldc           #6                  // String Strings
      23: aastore
      24: astore_1
      25: aload_1
      26: astore_2
      27: aload_2
      28: arraylength
      29: istore_3
      30: iconst_0
      31: istore        4
      33: iload         4
      35: iload_3
      36: if_icmpge     69
      39: aload_2
      40: iload         4
      42: aaload
      43: astore        5
      45: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      48: aload         5
      50: iconst_0
      51: iconst_4
      52: invokevirtual #8                  // Method java/lang/String.substring:(II)Ljava/lang/String;
      55: iconst_0
      56: iconst_2
      57: invokevirtual #8                  // Method java/lang/String.substring:(II)Ljava/lang/String;
      60: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      63: iinc          4, 1
      66: goto          33
      69: return
}

public class TestTmp {
  public TestTmp();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_4
       1: anewarray     #2                  // class java/lang/String
       4: dup
       5: iconst_0
       6: ldc           #3                  // String These
       8: aastore
       9: dup
      10: iconst_1
      11: ldc           #4                  // String Are__
      13: aastore
      14: dup
      15: iconst_2
      16: ldc           #5                  // String Some_
      18: aastore
      19: dup
      20: iconst_3
      21: ldc           #6                  // String Strings
      23: aastore
      24: astore_1
      25: aload_1
      26: astore_2
      27: aload_2
      28: arraylength
      29: istore_3
      30: iconst_0
      31: istore        4
      33: iload         4
      35: iload_3
      36: if_icmpge     77
      39: aload_2
      40: iload         4
      42: aaload
      43: astore        5
      45: aload         5
      47: iconst_0
      48: iconst_4
      49: invokevirtual #7                  // Method java/lang/String.substring:(II)Ljava/lang/String;
      52: astore        6
      54: aload         6
      56: iconst_0
      57: iconst_2
      58: invokevirtual #7                  // Method java/lang/String.substring:(II)Ljava/lang/String;
      61: astore        6
      63: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
      66: aload         6
      68: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      71: iinc          4, 1
      74: goto          33
      77: return
}

在您的示例中,您使用的是不可变的字符串。在您的代码中:

str.substring(0,4).substring(0,2)

第一次调用 substring 必须生成一个新的 String 对象,因为 str 不能被修改。类似地,第二次调用该新 String 对象上的 substring 将创建另一个新 String 对象。

字节码的不同只是编译器调用方法顺序的结果。在 TestTmp 情况下,所有字符串操作都发生在调用 PrintStream 之前。对于 TestNoTmp,String 调用发生在 PrintStream 调用中,当您查看代码时,这是非常合乎逻辑的。

为了回答您的问题,这对对象分配和 GC 影响没有影响。