【问题标题】:String object creation in java [duplicate]java中的字符串对象创建[重复]
【发布时间】:2012-12-20 19:42:16
【问题描述】:

可能重复:
Questions about Java’s String pool

我对 java Strings 对象的创建有疑问。

String s1 = "Hello"+"world";
String s2 = s1+"Java";

在这个程序中将创建多少个 String 对象以及如何创建?请解释一下。 谢谢。

【问题讨论】:

    标签: java string


    【解决方案1】:

    答案是 3

    每次 JVM 启动都会创建两个 String 对象:

    • “你好世界”
    • “Java”

    两者都是interned,因为它们是常量(在编译时已知)。

    每次运行此代码时都会重复使用它们。将创建一个 StringBuilder 来连接上面的两个 String。对它们的引用将分配给 s1 和 s2。

    这是代码的字节码:

       0:   ldc #37; //String Helloworld
       2:   astore_1
       3:   new #39; //class java/lang/StringBuilder
       6:   dup
       7:   aload_1
       8:   invokestatic    #41; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
       11:  invokespecial   #47; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
       14:  ldc #50; //String Java
       16:  invokevirtual   #52; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       19:  invokevirtual   #56; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       22:  astore_2
       23:  return
    

    【讨论】:

    • 嗯。我要说四个,因为连接运算符不会追加,而是实例化一个新字符串。你有一个参考链接吗? s1 呢,它不是一个常数?
    • @jmort253 虽然 s1 是一个“变量”,但 s1 的值在编译时是可确定的,因此为它创建了一个内部字符串常量(参见链接)。这个答案是正确的。请注意,问题询问的是创建了多少字符串对象,而不是字符串引用。
    • 嗨@Nambari,请查看实习链接,该链接在此上下文中特别提到了 Java。波西米亚人,感谢您的链接。 +1
    • 还可以查看 jls 15.28。
    • @jmort253 其实我是一半正确(但现在)看到编辑的答案与“证明”
    【解决方案2】:

    你不能说创建了多少Strings,因为由于JVM的不同实现存在一些差异。

    由于String 是一个不可变类,天真的答案是 5。但经过一些优化(例如,使用 StringBuffer/StringBuilder 将只有 2 个Strings。

    因为 concats 将通过 append()-calls 进行汇总。

    编辑:因为这里有一些不同的答案,所以解释了我为什么说 5:

    1. “你好”
    2. “世界”
    3. (s1)“Helloworld”
    4. “Java”
    5. (s2)“HelloworldJava”

    【讨论】:

    • 不,JLS 专门定义了编译时常量字符串表达式。将有两个字符串常量和一个在运行时创建的字符串。 Jls 15.28.
    • 这就是我所说的关于课程StringbufferStringBuilder的部分。编译器将使用这些来最小化使用的文字数量。
    • 我明白,但是您列表中的字符串 1 和 2 将不会由符合规范的 JVM 创建,因为您列表中的字符串 3 是一个 编译-time 根据规范恒定。同样,字符串 5不是编译时常量,因此必须创建一个新字符串 (jls 15.18.1)。
    • 为了结束循环,特定于实现的一件事是连接创建了多少临时字符串。在两个字符串的情况下,使用直接连接与 StringBuilder 没有什么不同。我猜编译器会发疯,一次创建一个字符的字符串,这样临时字符串就和字符一样多,但这很愚蠢。
    【解决方案3】:

    如果你看编译后的代码,你很容易猜到:

    String s1 = "Helloworld";
    String s2 = (new StringBuilder(String.valueOf(s1))).append("Java").toString();
    

    我们不能仅仅通过查看源代码来准确地知道,因为许多优化都是在执行之前由编译器完成的。

    这里我们看到为 s1 创建了 1 个 String 对象,为 s2 创建了另一个 String 对象。字符串池中有 2 个字符串文字:“Helloworld”和“Java”

    【讨论】:

    • YES "Hello"+"world" 将被编译器优化为单个字符串
    【解决方案4】:

    如果你反编译你的 Program.class 你会看到真正的代码

    String s1 = "Helloworld";
    String s2 = (new StringBuilder(String.valueOf(s1))).append("Java").toString();
    

    似乎是 10 个对象,因为每个 String 内部都有 char[] value 这是一个单独的对象 + StringBuilder 内部的另一个 char[]

    【讨论】:

    • 你检查过字节码了吗?因为我认为不会创建任何字符串生成器。
    • 是的,第一行是 NEW java/lang/StringBuilder ...
    【解决方案5】:

    答案是 3。

    您可以通过以下方式查看反汇编结果:

    javap -verbose YourClass
    

    常量池包括:

    ...
    const #17 = Asciz       Helloworld;
    ...
    const #30 = Asciz       Java;
    ...
    

    这意味着两个字符串(“Helloworld”和“Java”)是编译时常量表达式,会被自动放入常量池中。

    代码:

    Code:
     Stack=3, Locals=3, Args_size=1
     0:   ldc     #16; //String Helloworld
     2:   astore_1
     3:   new     #18; //class java/lang/StringBuilder
     6:   dup
     7:   aload_1
     8:   invokestatic    #20; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
     11:  invokespecial   #26; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
     14:  ldc     #29; //String Java
     16:  invokevirtual   #31; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     19:  invokevirtual   #35; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     22:  astore_2
     23:  return
    

    表示s2是由StringBuilder.append()和toString()创建的。

    为了让这更有趣,javac 可以优化常量折叠中的代码。您可以猜测以下代码创建的字符串的数量:

    final String s1 = "Hello" + "world";
    String s2 = s1 + "Java";
    

    "final" 表示 s1 是常量,可以帮助 javac 构建 s2 和实习生 s2 的值。所以这里创建的字符串数是 2。

    【讨论】:

      【解决方案6】:

      是的,将创建五个 String 对象。字符串是不可变的;以下是步骤 - 1.第一个“你好” 2.然后是另一个对象“Hello World” 3.然后是另一个对象“Java” 4.然后s1+"Java" 然后最后会创建 s2。

      【讨论】:

        【解决方案7】:

        实际上不会只创建两个字符串字面量的字符串对象。 当字符串像你一样被初始化时,它们是文字而不是对象。如果您想创建 String 对象,您可以执行以下操作

        String a = new String("abcd");
        

        【讨论】:

          猜你喜欢
          • 2011-02-26
          • 2014-12-23
          • 1970-01-01
          • 2014-11-22
          • 1970-01-01
          • 1970-01-01
          • 2018-08-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多