奇怪的字符串池行为

时间:2013-07-08 11:13:29

标签: java string

我有一个奇怪的字符串池行为的问题。 我正在使用==来比较相等的字符串,以确定它们是否在池中。

public class StringPoolTest {
  public static void main(String[] args) {
    new StringPoolTest().run();
  }

  String giveLiteralString() {
    return "555";
  }

  void run() {
    String s1 = giveLiteralString() + "";
    System.out.println("555" == "555" + "");
    System.out.println(giveLiteralString() == giveLiteralString() + "");
  }
}

输出结果为:

true
false

这对我来说是个大惊喜。有人能解释一下吗? 我认为这个问题发生在编译时。但是为什么将""添加到String会产生任何差异?

4 个答案:

答案 0 :(得分:110)

"555" + ""

compile-time constant,而

giveLiteralString() + ""

是没有的。因此前者只编译成字符串常量“555”,后者编译成实际的方法调用和连接,从而产生一个新的String实例。

<小时/> 另请参阅JLS §3.10.5 (String Literals)

  

在运行时通过串联计算的字符串是新创建的   因此不同。

答案 1 :(得分:31)

反编译此行后

System.out.println("555" == "555" + "");

我得到了这个字节码

    LINENUMBER 8 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ICONST_1
    INVOKEVIRTUAL java/io/PrintStream.println(Z)V
    ...

相当于

  System.out.println(true);

这意味着表达式"555" == "555" + ""编译为布尔true

对于giveLiteralString() == giveLiteralString() + "" javac构建了这个字节码

    LINENUMBER 8 L0
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String;
    NEW java/lang/StringBuilder
    DUP
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String;
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
    IF_ACMPNE L1
    ...

相当于

if (giveLiteralString() == new StringBuilder(giveLiteralString()).append("").toString()) {
...

总会产生错误,因为我们在这里比较了两个不同的对象。

答案 2 :(得分:4)

在第二种情况下,编译器已经认识到+ ""是一种无操作,因为""是一个已知为零长度的编译时值。但是编译器仍然需要检查giveLiteralString的结果为null(因为在非优化的情况下由于+操作会发生空检查),所以最简单的就是不尝试优化。

结果,编译器生成代码以执行连接,并创建一个新字符串。

答案 3 :(得分:0)

编译时间连接 由常量表达式计算的字符串在编译时完成,并视为常量或文字表示字符串或表达式的值在编译时已知或计算,因此编译器可以检查字符串池中的相同值并返回相同的字符串对象引用

运行时连接 值已知或无法在编译时计算的字符串表达式,但取决于运行时的输入或条件,编译器将不知道字符串的值,因此总是使用StringBuilder附加字符串并始终返回新字符串。 我想这个例子会更好地澄清它。

public static void main(String[] args) {
    new StringPoolTest().run();
  }
  String giveLiteralString() {
    return "555";
  }

  void run() {
    System.out.println("555" + 9 == "555" + 9);  
    System.out.println("555"+Integer.valueOf(9) == "555" + Integer.valueOf(9)); 
    System.out.println(giveLiteralString() == giveLiteralString());
    // The result of runtime concatenation is a fresh string.
    System.out.println(giveLiteralString() == giveLiteralString() + "");
  }