Разница в следующих объявлениях [дубликаты]

Я не могу распознать разницу в следующих объявлениях строк в Java.

Предположим, у меня есть две строки

String str1="one";
String str2="two";

В чем разница между

String str3=new String(str1+str2);

а также

String str3=str1+str2;

В обоих приведенных выше объявлениях содержимое str3 будет onetwo.

Предположим, я создаю новую строку

String str4="onetwo";

Тогда ни в одном из приведенных выше объявлений

if(str4==str3) {
    System.out.println("This is not executed");
}

Почему str3 и str4 не относятся к одному и тому же объекту?


person kevin gomes    schedule 22.03.2015    source источник
comment
Не используйте == для сравнения строк   -  person Apurva    schedule 22.03.2015
comment
@Apurva: я хочу сравнивать объекты, а не содержимое строк. Я хочу знать, на что ссылаются str4 и str3   -  person kevin gomes    schedule 22.03.2015
comment
это фактически сравнивает ссылку, то есть если str4 и str3 будут указывать на одно и то же место, тогда оно будет равно тому, что здесь находится в другом месте.... вместо этого используйте равный метод строки   -  person Iftikhar Ali Ansari    schedule 22.03.2015
comment
@Iftikhar Почему и где str3 и str4 относятся к разным местам   -  person kevin gomes    schedule 22.03.2015
comment
вы не можете напечатать адрес объекта в чистой Java. stackoverflow.com/questions/18396927/   -  person Apurva    schedule 22.03.2015
comment
Why and where str3 and str4 are referring to different location Как вы думаете, почему они должны иметь одинаковую ссылку?   -  person Tom    schedule 22.03.2015
comment
@Tom, потому что String str3=str1+str2 должна была быть интернированной строкой, а str4 должна относиться к тому же месту, что и str3   -  person kevin gomes    schedule 22.03.2015
comment
@kevingomes потому что String str3=str1+str2 должен был быть интернированным нет, компилятор не может быть уверен в значениях нефинальных переменных, поэтому он не может конкатенировать их во время компиляции, как это может быть в случае "foo"+"bar" и интернировать его. Он скомпилирует его в new StringBuilder(str1).append(str2).toString(), и этот результат не будет интернирован.   -  person Pshemo    schedule 22.03.2015


Ответы (4)


str1 + str2 для строк, не являющихся константами компиляции, будет скомпилировано в
new StringBuilder(str1).append(str2).toString(). Этот результат не будет помещен или взят из пула строк (куда идут интернированные строки).

Другое дело в случае "foo"+"bar", где компилятор знает, с какими значениями он работает, поэтому он может объединить эту строку один раз, чтобы избежать ее во время выполнения. Такой строковый литерал также будет интернирован.

Итак, String str3 = str1+str2; такой же, как

String str3 = new StringBuilder(str1).append(str2).toString();

и String str3 = new String(str1+str2); такой же, как

String str3 = new String(new StringBuilder(str1).append(str2).toString());

Опять же, строки, созданные в результате метода (например, substring, replace, toString), не интернируются.
Это означает, что вы сравниваете два разных экземпляра (в которых хранятся одни и те же символы), и поэтому == возвращает false.

person Pshemo    schedule 22.03.2015

Java не имеет памяти о том, «как эта переменная получила значение», поэтому действительно не имеет значения, какой метод вы используете, если результат тот же.

Что касается сравнения, если вы сравниваете строки с ==, вы сравниваете адреса объектов в памяти, потому что String не является примитивным типом данных, а не значениями. Вы должны использовать if(str4.equals(str3))

person libik    schedule 22.03.2015
comment
Я хочу сравнить только адреса объектов. У стр3 и стр4 разные ссылки. - person kevin gomes; 22.03.2015
comment
Важно, какой метод мы используем - person frunkad; 22.03.2015
comment
@kevingomes - Google: учебник по Java. На самом деле вы хотите сравнить значения, а не адрес в памяти, в вашем примере. Если ваша жена родит близнецов, они могут выглядеть одинаково, но это два разных экземпляра. Если вы хотите знать, выглядят ли они одинаково, вы используете equals, если вы хотите знать, являются ли они одним и тем же человеком, вы используете ==. - person libik; 22.03.2015

Поскольку String в Java неизменяемы, компилятор будет оптимизировать и повторно использовать литералы String. Таким образом

String s1 = "one";
String s2 = "one";
s1 == s2; //true because the compiler will reuse the same String (with the same memory address) for the same string literal
s1 == "o" + "ne"; //true because "Strings computed by constant expressions are computed at compile time and then treated as if they were literals"
s3 = "o";
s1 == s3 + "ne"; //false because the second string is created a run time and is therefore newly created

для справки см. http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5

person robertjlooby    schedule 22.03.2015
comment
Вы использовали слово immutable - person frunkad; 22.03.2015
comment
may be true as the compiler may ...? Это будет всегда верно. - person Tom; 22.03.2015
comment
Я полагаю, что любой компилятор сделает это, так что, скорее всего, это будет верно на практике. Однако я не думаю, что это поведение на самом деле определено в стандарте языка, поэтому я не хотел говорить, что это всегда будет верно, поскольку допустимый компилятор может иметь другое поведение. - person robertjlooby; 22.03.2015
comment
Interning Strings не имеет ничего общего с компилятором. - person Tom; 22.03.2015
comment
Вы правы, строковые литералы всегда будут интернированы docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5 исправляет это. - person robertjlooby; 22.03.2015

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

Чтобы получить более развернутый ответ и немного развлечься, попробуйте следующее:

String s1= "a" + "b";
String s2= "a" + "b";
String s3=new String("a"+"b");

System.out.println(s1==s2);
System.out.println(s3==s2);

Вы заметите, что s1==s2 из-за усилий компилятора по совместному использованию. Однако s2 != s3, потому что вы явно запросили новую строку. Вы вряд ли сделаете с ним что-то очень умное, потому что оно неизменяемо.

person Pelit Mamani    schedule 22.03.2015