Странное поведение пула строк

У меня вопрос о странном поведении пула строк. Я использую == для сравнения одинаковых строк, чтобы выяснить, находятся они в пуле или нет.

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

что для меня большой сюрприз. Может ли кто-нибудь объяснить это, пожалуйста? Я думаю, что-то об этом происходит во время компиляции. Но почему добавление "" в строку имеет какое-либо значение?

73 голоса | спросил iozee 8 J000000Monday13 2013, 15:13:29

4 ответа


0
"555" + ""

является константой времени компиляции , в то время как

giveLiteralString() + ""

нет. Поэтому первый компилируется в строковую константу "555", а второй компилируется в фактический вызов метода и конкатенацию, что приводит к свежему экземпляру String.


Также см. JLS §3.10.5 (строковые литералы) :
  

Строки, вычисленные путем конкатенации во время выполнения, создаются заново и   поэтому отличается.

ответил Marko Topolnik 8 J000000Monday13 2013, 15:16:03
0

После декомпиляции этой строки

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

Для 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
    ...

что эквивалентно

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

который всегда будет выдавать false, так как здесь мы сравниваем 2 разных объекта.

ответил Evgeniy Dorofeev 8 J000000Monday13 2013, 15:24:09
0

Во втором случае компилятор МОЖЕТ распознать, что + "" не работает, поскольку "" - это значение времени компиляции, которое известно как нулевая длина. Но компилятору по-прежнему необходимо проверять результат из giveLiteralString на ноль (поскольку проверка на ноль будет происходить в результате + в неоптимизированном случае), поэтому проще всего не пытаться выполнить оптимизацию.

В результате компилятор генерирует код для выполнения конкатенации и создает новую строку.

ответил Hot Licks 8 J000000Monday13 2013, 15:25:30
0

Конкатенация времени компиляции Строка, вычисляемая по константному выражению, выполняется во время компиляции и обрабатывается как константы или литералы. Это означает, что значение строки или выражения известно или оценено во время компиляции, следовательно, компилятор может проверить одно и то же значение в строковом пуле и вернуть ту же ссылку на строковый объект. .

Конкатенация во время выполнения Строковые выражения, значения которых известны или не могут быть оценены при компиляции, но зависят от входных данных или условий времени выполнения, тогда компилятор не будет знать значение строки и, следовательно, всегда получит значение, используя StringBuilder для добавления строки, и всегда возвращает новая строка Я думаю, этот пример прояснит это лучше.

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() + "");
  }
ответил Himansu Meher 12 AMpWed, 12 Apr 2017 08:07:53 +030007Wednesday 2017, 08:07:53

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132