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