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

Har*_*hah 23 java string

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

String x = new String("xyz");
String y = "abc";
x = x + y;
Run Code Online (Sandbox Code Playgroud)

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

das*_*ght 49

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

  1. 一个String对应于实习"xyz"字面
  2. 它的副本由 new String("xyz")
  3. 一个String对应于实习"abc"字面
  4. A String对应于连接"xyz" + "abc"

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

  • @ xuma202文字是一种特殊情况,因为从定义类到文字必须有某种形式的引用.根据实际的VM实现,它们甚至可能永远不会被垃圾收集(在旧的Hotspot VM中永久生成). (2认同)

Ste*_*n C 16

这个答案是纠正一些其他答案所提出的误解:

例如:

但编译器可能会用常量("xyzabc")替换x + y.@Binkan Salaryman

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

这是不正确的.该JLS状态如下:

15.18.1.字符串连接运算符+

....

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

为了限定为常量表达式,表达式中的变量名必须为:

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

其中"常量变量"定义为:

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

在这个例子中,没有xyfinal因此他们不是常数变量.即使它们是final,y 由于new在初始化中使用了运算符,它仍然不会是一个常量变量.


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

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

    System.out.println(x == "xyzabc");
Run Code Online (Sandbox Code Playgroud)

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


小智 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
    
    Run Code Online (Sandbox Code Playgroud)


Bin*_*man 8

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

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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用jdk1.7.0_67输出以下内容:

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

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

  • 有些字符串不是被创建的,而是被“保留”的。它们只是作为程序中的常量存在。读一下:http://stackoverflow.com/questions/2486191/java-string-pool (2认同)
  • 我永远不会说某些东西是在编译时“创建”的。就我而言,我们正在讨论动态分配多少字符串对象(在运行时)。 (2认同)

Ram*_*tra 8

答案是4.

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

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

  1. "xyz" (在非池内存中)
  2. "xyz" (在池内存中)
  3. "abc" (在游泳池记忆中)
  4. "xyzabc" (在非池内存中)

如果您的代码是这样的:

String x = "xyz";
String y = "abc";
x = x + y;
Run Code Online (Sandbox Code Playgroud)

然后答案是3.

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

资料来源:SCJP Sun认证Java 6程序员(第43页,第6章)

  • 字符串#4不在池内存中.看我的回答. (2认同)
  • @Ramswaroop - 1)除非表达式是常量表达式,否则将创建一个新的字符串对象.(根据常量表达式的严格JLS定义.)2)字符串对象通常不在池内存中创建.只有`String.intern()`创建的字符串才会进入字符串池.(这就是类加载器如何创建对应于字符串文字的字符串对象,以及字符串常量表达式......例如.) (2认同)
  • 通过计算*常量表达式生成的字符串文字和字符串*(参见JLS§15.28)是唯一隐式实现的字符串. (2认同)

归档时间:

查看次数:

7502 次

最近记录:

6 年,5 月 前