73

我有一些奇怪的字符串池行为的问题。我==用来比较相等的字符串以找出它们是否在池中。

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

4 回答 4

110
"555" + ""

是一个编译时常数,而

giveLiteralString() + ""

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


另请参阅JLS §3.10.5(字符串文字)

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

于 2013-07-08T11:16:03.603 回答
32

反编译这一行后

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" + ""编译为 boolean 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 个不同的对象。

于 2013-07-08T11:24:09.273 回答
4

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

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

于 2013-07-08T11:25:30.803 回答
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() + "");
  }
于 2017-04-12T05:07:53.720 回答