奇怪的字符串池行为

2022-08-31 13:38:34

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

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

这对我来说是一个很大的惊喜。任何人都可以解释一下吗?我认为在编译时正在发生一些事情。但是,为什么添加到字符串中会有什么不同呢?""


答案 1
"555" + ""

编译时常量,而

giveLiteralString() + ""

不是。因此,前者仅编译为字符串常量“555”,后者编译为实际的方法调用和串联,从而产生新的 String 实例。


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

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


答案 2

反编译此行后

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

对于 javac 构建此字节码giveLiteralString() == giveLiteralString() + ""

    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个不相关的对象。