Подтвердить что ты не робот

"==" в случае конкатенации строк в Java

    String a = "devender";
    String b = "devender";
    String c = "dev";
    String d = "dev" + "ender";
    String e = c + "ender";

    System.out.println(a == b);     //case 1: o/p true

    System.out.println(a == d);     //case 2: o/p true

    System.out.println(a == e);     //case 3: o/p false

a и b оба указывают на тот же строковый литерал в пуле строковых констант. Итак, true в случае 1

String d = "dev" + "ender";

должен быть внутренне использован что-то вроде -

String d = new StringBuilder().append("dev").append("ender").toString();

Как a и d указывают на одну и ту же ссылку, а не a и e?

4b9b3361

Ответ 1

Четыре вещи продолжаются:

  • (Вы точно это знаете, но для lurkers) == проверяет, указывают ли переменные на тот же String объект, а не на эквивалентные строки. Поэтому даже если x равно "foo", а y также "foo", x == y может быть истинным или ложным, в зависимости от того, относятся ли теги x и y к одному и тому же объекту String или к другим, Поэтому для сравнения строк для эквивалентности мы используем equals, а не ==. Все из следующего объясняется тем, почему == иногда истинно, это не предложение использовать == для сравнения строк.: -)

  • Эквивалентные строковые константы (строки, которые знает компилятор, являются константами в соответствии с различными правилами в JLS) в одном классе, ссылаются на одну и ту же строку компилятором (который также перечисляет их в классе "постоянный пул" ). Вот почему a == b истинно.

  • Когда класс загружается, каждая из его строковых констант автоматически interned — пул строк JVM проверяется на эквивалентную строку, и если он найден, используется объект String (если нет, то новый объект String для новой константы добавляется в пул). Поэтому даже если x является строковой константой, инициализированной в классе Foo, а y является строковой константой, инициализированной в классе Bar, они будут == друг друга.

    Баллы 2 и 3 выше частично описаны JLS§3.10.5. (Бит о пуле констант класса - это немного детализация реализации, поэтому ссылка на спецификацию JVM ранее, JLS просто говорит о интернировании.)

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

    String d = "dev" + "ender";
    

    скомпилирован в

    String d = "devender";
    

    и "devender" - это строковая константа, которую компилятор и JVM применяют к пунктам 2 и 3 выше. Например, no StringBuilder используется, конкатенация происходит во время компиляции, а не во время выполнения. Это описано в JLS§15.28 - Константные выражения. Таким образом, a == d истинно по той же причине a == b имеет значение true: они относятся к одной и той же константной строке, поэтому компилятор гарантировал, что они ссылаются на одну и ту же строку в пуле констант класса.

    Компилятор не может этого сделать, если какой-либо из операндов не является константой, поэтому он не может сделать это с помощью

    String e = c + "ender";
    

    ... хотя анализ кода может легко показать, что значение c будет, безусловно, "dev", и, таким образом, e будет определенно "devender". Спецификация содержит только компилятор с конкатенацией с постоянными значениями. Поэтому, поскольку компилятор не может этого сделать, он выдает код StringBuilder, на который вы ссылались, и эта работа выполняется во время выполнения, создавая новый объект String. Эта строка не выполняется автоматически, поэтому e заканчивается ссылкой на другой объект String, чем a, и поэтому a == e является ложным.

    Обратите внимание, что как сказал Vinod, если вы объявили c как final:

    final String c = "dev";
    

    Тогда это будет постоянная переменная (да, их действительно называют), и поэтому применим §15.28, и компилятор будет включить

    String e = c + "ender";
    

    в

    String e = "devender";
    

    и a == e также верны.

Просто повторить: ничто из этого не означает, что мы должны использовать == для сравнения строк для эквивалентности.:-) То, что equals для.

Ответ 2

Компилятор делает большую оптимизацию под капотом.

String d = "dev" + "ender";

Здесь компилятор заменит "dev" + "ender" на "devender" при компиляции программы. Если вы добавляете 2 литерала (это относится как к примитивам, так и к строкам), компилятор делает эту оптимизацию.

Код Java:

String d = "dev" + "ender";

Байт-код:

  0: ldc           #16                 // String devender

Переход к частному случаю:

final String c = "dev"; // mark this as final
String e = c + "ender";

Создание c final сделает String постоянной времени компиляции. Компилятор поймет, что значение c не может измениться и, следовательно, заменит все события c на значение "dev" при компиляции, таким образом e будет разрешено во время самого компиляции.

Ответ 3

Как вы сказали внутренне, последняя конкатенация выполняется с чем-то похожим на

String e = new StringBuilder().append(c).append("ender").toString();

реализация toString() StringBuilder создает новую строку. Вот реализация.

public String toString() {
     // Create a copy, don't share the array
     return new String(value, 0, count);
}

Сравнение строк с использованием == вместо .equals() возвращает true только в том случае, если обе строки одинаковы. В этом случае они не совпадают, поскольку вторая строка создается как новый объект типа String.

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

Ответ 4

"dev" + "ender" - это константное выражение для оценки времени компиляции: оба аргумента являются строковыми литералами. Следовательно, выражение "devender".

То же самое нельзя сказать о c + "ender": некоторые обстоятельства (например, некоторый код, запущенный в другом потоке) могут привести к тому, что значение c будет установлено на другое значение. Квалификация c как final устраняет эту возможность, и в этом случае e также ссылается на тот же объект, что и a.

So a, b и d все относятся к одному и тому же объекту.

Ответ 5

Разница между d и e заключается в том, что при конкатенации строковых литералов конкатенация выполняется во время компиляции. Компилятор Java рассматривает выражение "dev" + "ender" так же, как выражение "devender", создавая один и тот же литерал во время компиляции. Поскольку все литералы String получают интернированный, d, который является результатом "dev" + "ender", также заканчивается ссылкой на тот же объект, что и a и b "devender".

Выражение для e, которое c + "ender", оценивается во время выполнения. Несмотря на то, что он создает ту же строку, этот факт не используется компилятором. Поэтому возникает другой объект String, что приводит к неудачному сравнению с ==.

Ответ 6

Строка d = "dev" + "ender"; константа + константа, d всегда является константой (одна и та же), поэтому (a == d) истинно;

String e = c + "ender"; variable + constant, результат 'e' является переменной, он будет использовать StringBuilder внутри и создать новую ссылку.

Ответ 7

Имейте в виду, что Java содержит пул всех строковых литералов, найденных в программе, используемых для сопоставления целей среди других, поэтому любая связанная строка буквальное приведенная выше приведет к тому же объекту, к тому же строковый литерал. Вы можете проверить эту полезную статью для более подробной информации.

С другой стороны, конкатенация объекта String и литерала (case c + "ender") приведет к созданию объекта as StringBuilder во время выполнения, отличного от литералов, найденных в пуле.