奇怪的字符串池行为

ioz*_*zee 72 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() + "");
  }
}
Run Code Online (Sandbox Code Playgroud)

输出是:

true
false
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*nik 110

"555" + ""
Run Code Online (Sandbox Code Playgroud)

是一个编译时常量,而

giveLiteralString() + ""
Run Code Online (Sandbox Code Playgroud)

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


另请参阅JLS§3.10.5(String Literals):

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

  • @sanbhat在我的回答中单击"编译时常量". (3认同)

Evg*_*eev 31

反编译此行后

System.out.println("555" == "555" + "");
Run Code Online (Sandbox Code Playgroud)

我有这个字节码

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

这相当于

  System.out.println(true);
Run Code Online (Sandbox Code Playgroud)

这意味着表达式"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
    ...
Run Code Online (Sandbox Code Playgroud)

这相当于

if (giveLiteralString() == new StringBuilder(giveLiteralString()).append("").toString()) {
...
Run Code Online (Sandbox Code Playgroud)

这将永远产生错误,因为在这里我们比较2个disctinct对象.

  • "这将始终产生错误" - 从技术上讲,StringBuilder不是*必需*来生成非实习字符串.简单地说,它没有充分的理由试图产生一个实习生. (2认同)
  • @HotLicks我读了它,因为返回了一个新的String对象.这总是新的,而不是池中的对象 (2认同)