【问题标题】:Number of objects created during string concatenation字符串连接期间创建的对象数
【发布时间】:2014-03-10 13:12:55
【问题描述】:

谁能告诉我在执行下面代码中的System.out.println 语句时将创建多少个对象

int i=0;
int j=1;
System.out.print("i value is "+ i + "j value is "+j);

【问题讨论】:

  • 这取决于实现 - 热点上可能有 5 个...为什么重要?
  • 我只能看到这里创建了 1 个字符串对象。看我的回答。如果有人有其他想法,请提供解释而不是说随机数。
  • 这个问题不清楚是否也应该考虑编译时创建的对象;目前所有提供的答案都将其排除在外。在编译时有 4 个分配,2 个继承自 Object(两个字符串)
  • 我同意@assylias。这似乎不是特别重要。我不认为知道这个细节会让一个人成为更好的 Java 开发人员

标签: java string object


【解决方案1】:

你应该在这里考虑两点:

  1. 当您说 String 是不可变的时,如果您尝试更改值,它将创建一个新对象。
  2. String 对象将在您使用 Literal(String s = "Pool") 方式或使用 new 关键字 (String s = new String("Heap and Pool") 编写代码时创建,这里 new 关键字是指堆,即 Literal " Heap and Pool”指的是字符串常量池)。

在这种情况下,上述答案是正确的。将创建五个 String 对象。

【讨论】:

    【解决方案2】:

    5 个对象

    1. "i value is ".会形成一个String对象。
    2. "i 值为 "+i。此连接操作将形成第二个对象。
    3. “j value is”将形成第三个对象。
    4. "i value is "+i + "j value is ".4th object 由于连接而形成。
    5. "i 值为 "+ i + "j 值为 " + j 。最后的连接将形成第 5 个对象。

    【讨论】:

      【解决方案3】:

      这将创建一个StringBuilder 对象(以及该对象内部使用的任何内容),添加值,最后StringBuilder 将创建一个带有结果的String 对象。

      【讨论】:

        【解决方案4】:

        如果你真的想知道发生了什么,为什么不看看字节码呢?

        我将您的代码包装在一个主函数中,对其进行编译,然后使用javap -c Test.class 对其进行反汇编。这是输出(使用 Oracle Java 7 编译器):

        Compiled from "Test.java"
        class Test {
          Test();
            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_0
               1: istore_1
               2: iconst_1
               3: istore_2
               4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
               7: new           #3                  // class java/lang/StringBuilder
              10: dup
              11: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
              14: ldc           #5                  // String i value is
              16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
              19: iload_1
              20: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
              23: ldc           #8                  // String j value is
              25: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
              28: iload_2
              29: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
              32: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              35: invokevirtual #10                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
              38: return
        }
        

        在此方法中分配的唯一对象StringBuilder(通过位置7处的new指令)。但是,invoked 的其他方法可能会自己分配一些东西,我非常怀疑 StringBuilder.toString 会分配一个 String 对象。

        【讨论】:

        • 代码中的 cmets 建议只创建一个 StringBuilder 对象,然后将 + 运算符的所有其他给定参数附加到 StringBuilder 对象,最后从 StringBuilder 调用 toString() 方法对象,并将返回的 String 值传递给 print() 方法。
        【解决方案5】:

        代码

        int i=0;
        int j=1;
        System.out.print("i value is "+ i + "j value is "+j);
        

        创建 3 个对象。

        我的理由:

        Java 中的基本数据类型不是对象,也不继承自 Object。所以 int i=0; and int j=1; 不创建对象。

        现在System.out.print("i value is "+ i + "j value is "+j); 包含不可变的String,并且对字符串的操作成本很高。我们可以这样拆分操作。

        ("i value is ").concat(i)  // creates one object let say obj1
        obj1.concat("j value is ")   //creates another let say obj2
        obj2.concat(j)            // creates the final string let say obj3;
        

        在示例中,字符串操作str1.concat(str2) 是通过使用两个String 对象完成的,它创建第三个对象并更改引用,从而产生一种错觉,认为它实际上是第一个字符串对象,即str1。因此 str1 将有一个新的 String ,其中包含旧 str1 的值和 str2 连接的值。

        这是我以我有限的知识所相信的。如果我错了,请纠正我。

        【讨论】:

        • 我认为("i value is").concat(i) 生成了 2 个对象。首先它必须使“我的价值是”。然后它必须concati 给它返回一个新的字符串。
        • 我是obj1="i value is";obj2=obj1.concat(i);obj3="j value is";obj4=obj2.concat(obj3);obj5=obj4.concat(j);
        • "i value is" 在语句运行之前已经存在。
        • @Cruncher 我已经找到了,但我没有找到任何有效的文档。我相信("i value is").concat(i)i value is 视为输入而不是对象。你有任何有效的文件来支持你的论点吗?
        • @cHao 你是说在concat操作完成之前为"i value is"创建了一个对象吗?你有任何支持文件吗,我想了解里面发生了什么。!
        【解决方案6】:

        如果它确实依赖于实现,那么如果它被评估为:

        StringBuilder sb = new StringBuilder("i value is ");
        sb.append(i);
        sb.append(j);
        String newStr = sb.toString();
        

        将有 2 个对象。

        【讨论】:

        • 我看到这里至少有两个!
        • 所以 连接期间它实际上将是 两个 对象,对吧? StringBuilderString
        • 评论者,请提出一个更正,而不是说:那是错误的
        • "i value is" 为一。 StringBuilder#append 可能会创建额外的字符串 + 最后一个。
        • 我们关心 StringBuilder 创建的对象吗?有某种类型的后备存储(可能是一个数组,我还没有查看),可以在操作过程中调整其大小。
        【解决方案7】:

        String 是不可变的意味着你不能改变对象本身。
        每次创建新的字符串对象时执行连接。
        所以在上面的例子中

        1. int i
        2. int j
        3. “i 值为”
        4. "i 值为 "+i
        5. "i值为"+i+"j值为"
        6. "i值为"+i+"j值为"+j

        上例一共创建了六个对象。

        【讨论】:

        • int 是原始类型,而不是对象。另一方面是Integer
        • @xlm 所以它取决于在连接期间是否将原语转换为整数。我怀疑是这样的。
        【解决方案8】:

        编译器将代码转换为如下形式:

        int i=0;
        int j=1;
        StringBuilder temp = new StringBuilder(); // creates a StringBuilder
        temp.append("i value is "); // creates or re-uses a String
        temp.append(i); // might create a String
        temp.append("j value is"); // creates or re-uses a String
        temp.append(j); // might create a String
        String temp2 = temp.toString(); // creates a String
        System.out.print(temp2);
        

        这取决于你是否计算“i值是”和“j值是”字符串,它们被创建一次然后重复使用。

        如果你计算它们,那么至少 4 个,否则至少 2 个。

        实际上,每个 String 都有自己的 char[] 来实际存储字符串。所以这是 7 或 3,而不是 4 或 2。

        StringBuilder 也有一个 char[],当您向其中添加更多数据时,可能需要创建新的 char[]。 String.valueOf 或 System.out.print 也可能在您背后创建对象如果没有外部工具,您将无法了解它们。因此“至少”。

        【讨论】:

        • 您确定temp.append(String.valueOf(i)) 吗?由于有一个 append 变体采用 int 参数,因此我假设使用了它。
        • @Ɍ.Ɉ 这不是决定性的。可以单独确定和添加字符串表示的字符,而无需创建String。只是整体效果是一样的。
        • 刚刚测试过,它确实调用了temp.append(i)。编辑答案。
        【解决方案9】:

        只有 1 个,即用于打印字符串。

        拇指规则 - 每当字符串连接完成时,就会创建一个新对象。

        【讨论】:

          【解决方案10】:

          只有1,字符串对象才被连接起来。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-18
            • 2013-03-25
            相关资源
            最近更新 更多