简体   繁体   English

奇怪的字符串池行为

[英]Strange String pool behavior

I've got a question of some strange String pool behavior. 我有一个奇怪的字符串池行为的问题。 I'm using == to compare equal Strings to find out whether they're in the pool or not. 我正在使用==来比较相等的字符串,以确定它们是否在池中。

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

The output is: 输出是:

true
false

which is a big surprise for me. 这对我来说是个大惊喜。 Could anyone explain this please? 有人能解释一下吗? I think something about this is taking place at the compilation time. 我认为这个问题发生在编译时。 But why does adding "" to a String makes any difference at all? 但是为什么在String中添加""会有什么不同呢?

"555" + ""

is a compile-time constant , whereas 是一个编译时常量 ,而

giveLiteralString() + ""

isn't. 不是。 Therefore the former compiles into just the string constant "555" and the latter compiles into the actual method invocation and concatenation, resulting in a fresh String instance. 因此,前者只编译为字符串常量“555”,后者编译成实际的方法调用和连接,从而产生一个新的String实例。


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

Strings computed by concatenation at run time are newly created and therefore distinct. 在运行时通过串联计算的字符串是新创建的,因此是不同的。

After decompiling this line 反编译此行后

System.out.println("555" == "555" + "");

I got this bytecode 我有这个字节码

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

which is equivalent to 这相当于

  System.out.println(true);

that means expression "555" == "555" + "" compiles to boolean true . 这意味着表达式"555" == "555" + ""编译为布尔值true

For giveLiteralString() == giveLiteralString() + "" javac built this bytecode 对于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
    ...

which is equivalent to 这相当于

if (giveLiteralString() == new StringBuilder(giveLiteralString()).append("").toString()) {
...

which will always produce false since here we're comparing 2 disctinct objects. 这将永远产生错误,因为在这里我们比较2个disctinct对象。

In the second case the compiler COULD have recognized that + "" is a no-op of sorts, since "" is a compile-time value known to be zero length. 在第二种情况下,编译器COULD已经认识到+ ""是一种无操作,因为""是一个已知为零长度的编译时值。 But the compiler is still required to check the result from giveLiteralString for null (since the null check would occur as a result of the + operation in the non-optimized case), so it's simplest to just not attempt the optimization. 但是编译器仍然需要检查来自giveLiteralString的null的结果(因为在非优化的情况下由于+操作而发生空检查),所以最简单的是不尝试优化。

As a result, the compiler generates code to perform the concatenation, and a new string is created. 结果,编译器生成用于执行串联的代码,并创建新的字符串。

Compile Time Concatenation String computed by constant expression are done at compile time and treated as constants or literals means the value of the string or expression is known or evaluated at compile time hence the compiler can check the same value in string pool and the return the same string object reference. 编译时间连接由常量表达式计算的字符串在编译时完成并视为常量或文字表示字符串或表达式的值在编译时已知或计算,因此编译器可以检查字符串池中的相同值并返回相同的值字符串对象引用。

Runtime Concatenation String expressions whose values are known or can not be evaluated at compile but depends on the input or condition of run-time then the compiler will not know the value of the string and hence always land up using StringBuilder to append the string and always returns a new string . 运行时连接字符串表达式的值已知或无法在编译时计算,但取决于运行时的输入或条件,编译器将不知道字符串的值,因此始终使用StringBuilder附加字符串并始终返回一个新字符串。 I guess this example will clarify it better. 我想这个例子会更好地澄清它。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM