计算由Java代码创建的String对象

时间:2015-04-01 12:13:58

标签: java string

以下代码创建了多少个String对象?

String x = new String("xyz");
String y = "abc";
x = x + y;

我访问了很多网站,其中一些人说这行代码创建了3个对象,有些人说它创建了4个。我只是想知道在执行这行代码后创建了多少个对象。

13 个答案:

答案 0 :(得分:49)

在运行结束时,将有四个String个对象:

  1. 对应于实习String文字
  2. "xyz"
  3. new String("xyz")
  4. 创建的副本
  5. 对应于实习String文字
  6. "abc"
  7. String
  8. 对应的"xyz" + "abc"

    真正的问题是部分或全部这些对象归因于您的程序。可以合理地声称您的代码只创建了两个或多达四个String。尽管总共有四个String个对象,但对象1和3可能不一定由代码创建,因为它们位于常量池中,因此它们是在代码的直接控制之外创建的。

答案 1 :(得分:16)

本答案旨在纠正一些其他答案所引起的误解:

例如:

  

编译器可能会用x + y替换常量(“xyzabc”)。 @Binkan Salaryman

     

...和String对象4 [对应于串联的字符串]可以由编译器计算并转换为实习常量。 @dasblinkenlight

这是不正确的。 JLS说明了这一点:

  

15.18.1。 String Concatenation Operator +

     

...

     

除非表达式是常量表达式(第15.28节),否则新创建String对象(第12.5节)。

为了成为常量表达式,表达式中的变量名必须是:

  

引用常量变量的简单名称(第6.5.6.1节)(§4.12.4)。

其中“常量变量”定义为:

  

常量变量是基本类型或类型String的最终变量,使用常量表达式初始化(​​第15.28节)。

在此示例中,xy都不是final,因此它们不是常量变量。即使它们是finaly 仍然不会是常量变量,因为在初始化中使用了new运算符。


简而言之,Java编译器不允许使用内联常量"xyzabc"作为连接表达式的结果。

如果我在最后添加以下声明:

    System.out.println(x == "xyzabc");

它将始终打印false ...假设编译器符合Java语言规范。

答案 2 :(得分:12)

看看反编译的课程,你会看到一切:)答案应该是:

  • 两个字符串("xyz""abc")仅引用常量池中的位置,因此这些不是由您的代码创建的
  • 直接创建一个字符串(new String("xyz")
  • 字符串连接由编译器优化并更改为StringBuilder,以便间接创建最后一个字符串

    public java.lang.String method();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
     0: new           #2                  // class java/lang/String
     3: dup
     4: ldc           #3                  // String xyz
     6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
     9: astore_1
    10: ldc           #5                  // String abc
    12: astore_2
    13: new           #6                  // class java/lang/StringBuilder
    16: dup
    17: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
    20: aload_1
    21: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    24: aload_2
    25: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    28: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    31: astore_1
    32: aload_1
    33: areturn
    

答案 3 :(得分:8)

答案是 4

由于您使用了new关键字,Java将在普通(非池)内存中创建一个新的String对象,x将引用它。除此之外,文字“xyz”将被放置在字符串池中,这又是另一个字符串对象。

所以,4个字符串对象是:

  1. “xyz”(在非池内存中)
  2. “xyz”(在泳池内存中)
  3. “abc”(在池内存中)
  4. “xyzabc”(在非池内存中)
  5. 如果您的代码是这样的:

    String x = "xyz";
    String y = "abc";
    x = x + y;
    

    然后答案是 3

      

    注意:字符串#4位于非池内存中,因为字符串文字和通过计算常量表达式生成的字符串(请参阅JLS§15.28)是唯一隐式实现的字符串。

    来源:SCJP Sun认证Java 6程序员(页面:434,第6章)

答案 4 :(得分:7)

如果要测试实例,请运行此代码段并查看输出:

import static java.lang.System.identityHashCode;

public class Program {
    public static void main(String... args) {
        String x = new String("xyz");
        String y = "abc";
        String z = x + y;

        System.out.printf("x: %d | %d\n", identityHashCode(x), identityHashCode(x.intern()));
        System.out.printf("y: %d | %d\n", identityHashCode(y), identityHashCode(y.intern()));
        System.out.printf("z: %d | %d\n", identityHashCode(z), identityHashCode(z.intern()));
    }
}

我使用 jdk1.7.0_67

获得以下输出
  

x:414853995 | 1719175803个
  y:1405489012 | 1405489012个
  z:1881191331 | 1881191331

总共有4个String个实例......

答案 5 :(得分:3)

new String(”xyz“)肯定会创建一个新实例。 "abc""xyz"存储在类常量池中,x = x + y在引擎盖下创建StringBuilder,因此会创建new String,因此字符串数量为4这里。


但编译器可能会用x + y替换常量("xyzabc")。

答案 6 :(得分:3)

4 因为:

以下是:

String x = new String("xyz"); // 2 objects created: the variable and the constant
String y = "abc"; // 1 object created: the variable
x = x + y; // 1 object created: the one by the StringBuilder class

答案 7 :(得分:3)

你看到问题的不同答案的原因(部分)是不明确的。

  

“以下代码创建了多少个String对象?”

歧义在于“由以下代码创建”:

  • 是否询问(仅)执行代码所创建的String对象的数量?

  • 或者......是否询问在执行代码期间需要存在的String对象的数量?

你可能会争辩说代码隐式地创建了与文字对应的String对象,而不是在运行时,而是在加载时。但是,这些对象可以与使用相同文字的其他代码共享。 (如果你看起来很难,那么在类加载过程中可能会创建包含相同字符串的其他字符串对象。)

您看到不同答案的另一个原因是,创建的字符串数量并不完全清楚。各种规范规定在某些点创建新的String对象,但是关于是否可以创建中间String对象有“蠕动空间”。

例如,JLS声明new总是创建一个新对象,并且字符串连接运算符创建一个新对象,除非在某些明确指定的情况下(参见我的其他答案)。但是,规范并不禁止在幕后创建其他字符串。

但在这种情况下,如果我们假设我们使用的是现代热点JVM,那么:

  • 在代码开始执行之前存在2个字符串对象(对于字符串文字),
  • 执行代码时,会创建2个新的字符串对象(new+运算符)。

这些4字符串的存在/创建由JLS保证。

答案 8 :(得分:1)

答案是 4

String x = new String("xyz");//First Object

String y = "abc";//Second Object

x = x + y;//Third, fourth Object

答案 9 :(得分:1)

有时候让字节代码说话会更好 使用JAVAP检查代码

public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: new           #16                 // class java/lang/String
         3: dup
         4: ldc           #18                 // String xyz
         6: invokespecial #20                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1
        10: ldc           #23                 // String abc
        12: astore_2
        13: new           #25                 // class java/lang/StringBuilder
        16: dup
        17: aload_1
        18: invokestatic  #27                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        21: invokespecial #31                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        24: aload_2
        25: invokevirtual #32                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/St
ringBuilder;
        28: invokevirtual #36                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        31: astore_1
        32: return
      LineNumberTable:
        line 6: 0
        line 7: 10
        line 8: 13
        line 9: 32
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      33     0  args   [Ljava/lang/String;
              10      23     1     x   Ljava/lang/String;
              13      20     2     y   Ljava/lang/String;
}

现在从代码中看到

At `0: new` Creates a new String Object 
At `3:dup` ; make an extra reference to the new instance

    At `4:ldc #18` as seen literal "xyz" has been placed in the pool (one string Object) 
At `6: invokespecial;` ; now call an instance initialization method with parameter and creates a object in nonpool memory.
At `9: astore_1` Stores the above reference in local variable 1(i.e x)

所以此时我们有两个String对象

At `10:ldc #23` as seen literal "abc" has been placed in the pool (third string ) 

    At `12: astore_2` Stores the above reference in local variable (i.e y)

所以到这个时候我们有三个String Object

    28: invokevirtual #36 // Method java/lang/StringBuilder.toString:
()Ljava/lang/String;;(fourth String Object is Created)
  

所以我们在这段代码中总共有四个String对象。

由于我是编程的新手并且已经开始学习,仅仅几个月后我就会指出我是否在某个地方出错了,它的正确版本是什么。谢谢:))

答案 10 :(得分:1)

validates_uniqueness_of :name, :scope => :place

字符串是不可变的,因此如果需要更改任何现有的字符串变量,则将创建新对象以进行分配。第1行,第2行是字符串对象,其中第3行是对现有字符串变量的修改,因此需要进行新的分配以添加x + y。所以它应该创建创建3个对象。

答案 11 :(得分:-1)

1。在堆区域中创建的对象 “ xyz” //由'String x'和 xyzabc //由“ x + y”(串联)创建

2。在scp中创建的对象(字符串常量池) “ xyz” //为将来的目的而创建,不适用于垃圾收集和 “ abc” //由“字符串y”文字创建

因此在这种情况下创建的对象总数为4

答案 12 :(得分:-1)

答案是5

  1. 非池内存中的xyz
  2. 没有引用的池内存中的xyz
  3. 具有引用的池内存中的abc
  4. xyz仍位于非池内存中,引用已更改为非池内存中的xyzabc
  5. 没有引用的池内存中的xyzabc