奇怪的string池行为

我有一个奇怪的string池行为的问题。 我正在使用==比较相等的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中添加""会有所作为?

 "555" + "" 

是一个编译时常量 ,而

 giveLiteralString() + "" 

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


另见JLS§3.10.5(string文字) :

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

反编译这行后

 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); 

这意味着expression式"555" == "555" + ""编译为布尔值true

对于giveLiteralString() == giveLiteralString() + "" javac构build的这个字节码

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

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

结果,编译器生成代码来执行连接,并创build一个新的string。

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

运行时串联stringexpression式的值是已知的或者在编译时不能被求值,但是取决于运行时的input或条件,编译器将不知道string的值,因此总是使用StringBuilder来追加string,并总是返回一个新的string。 我想这个例子会更好地阐明它。

 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() + ""); }