18

我有两个问题:

public static void main(String[] args) {
  String s1 = "bla";
  String s2 = "b" +"l" + "a";
  String s3 = "b".concat("l").concat("a");

  if(s1 == s2) 
        System.out.println("Equal");
  else
        System.out.println("Not equal");
  if(s1 == s3) 
        System.out.println("Equal");
  else
        System.out.println("Not equal");
}
  • 为什么s1ands2指向同一个对象,而s1ands3不指向同一个对象?(没有使用new关键字)。

  • 如果我从用户那里得到一个字符串并将这些行添加到上面的代码中:

    BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
    String name=in.readLine();
    if(name.equals("test"))
        s1 = s1 + "xyz";
    

    如果用户输入xyz程序就会打印Not equal,当用户输入另一个东西时程序会输出Equal。这是否意味着池会随着整个程序的执行而改变?优化器是否在编译时工作并继续在runtime?

4

3 回答 3

15

为什么 s1 和 s2 指向同一个对象,而 s1 和 s3 却没有?(没有使用 new 关键字)。

因为连接发生在编译时,因此完成的字符串进入常量池,与第一个示例相同。这是编译器“已知”的特殊情况。这实际上意味着长字符串,以这种方式连接多行,仍然可以获得与简单字符串常量相同的性能改进的好处。

在第二个示例中,您在运行时执行计算,因此它不会成为常量池的一部分。

但是请注意,在 JLS 中,字符串常量池中可以和不可以进入的细节故意保持模糊,因此不同的实现可能会以不同的方式进行优化。它指定了关于必须进入的内容的某些规则,但不要依赖此行为在实现之间保持一致。

于 2013-01-21T21:38:16.180 回答
9

为什么 s1 和 s2 指向同一个对象,而 s1 和 s3 却没有?(没有使用 new 关键字)。

由于String在 Java 中是Immutable,所以字符串类的任何方法都会返回一个新的 String 对象(虽然有一些例外 - 一个是substring方法)。因此concat,方法创建了一个新字符串,该字符串进入Heap,并且不添加到常量池中。

s1ands2而言,这两个字符串在编译时都是已知的,因此它们是相同的字符串文字。

请注意,第二个字符串中的连接操作:-

String s2 = "b" +"l" + "a";

在编译时进行评估,结果与第一个字符串相同,并且向常量池添加一个条目。

于 2013-01-21T21:37:27.373 回答
3

有时(当编译器很明显 String 的值在运行时是什么时)编译器使用字符串池,在其他情况下则不使用。

实际上,您的代码不应取决于使用或不使用池的事实。

您不能总是运行 main,因此如果您想查看是否使用了池中的 String,您可以使用javap反编译代码,清单相对不言自明。

于 2013-01-22T05:33:56.837 回答