Я пробовал код ниже:
public class TestIntern {
public static void main(String[] args) {
char[] c1 = {'a','b','h','i'};
String s1 = new String(c1);
s1.intern();
String s2 = "abhi";
System.out.println(s1==s2);//true
char[] c2 = {'j','a','v','a'};
String sj1 = new String(c2);
sj1.intern();
String sj2 = "java";
System.out.println(sj1==sj2);//false
char[] c3 = {'J','A','V','A'};
String tj1 = new String(c3);
tj1.intern();
String tj2 = "JAVA";
System.out.println(tj1==tj2);//true
}
}
Я перепробовал много разных литералов.
Может ли кто-нибудь объяснить, почему intern() не работает должным образом с буквальным "java"? Почему приведенные выше сравнения эталонов оцениваются как true, Кроме, когда литерал - "java"?
Вы не сравниваете строки с ==
Вы игнорируете возвращаемое значение intern(). Перечитайте документы, присвойте возвращаемое значение исходной ссылке, и вы увидите, что все работает так, как вы ожидаете. Также, пожалуйста, никогда не полагайтесь на intern().
String.intern не является методом void, он возвращает String. Вы игнорируете возвращаемое значение. Прочтите документацию. Не игнорируйте возвращаемые значения.
Для меня это похоже на опечатку; вы просто никуда не переназначили s1, sj или sj1.
@nicomp OP хочет сравнить ссылки в этом случае.
@MarcosVasconcelos: да, это предполагается. Но это не возвращается. Я тоже удивлен.
Насколько я могу судить, ответы и комментарии до сих пор упускают суть вопроса. По крайней мере, очень они почти не объясняют наблюдаемое поведение, которое описывается, и очень удивляет (по крайней мере, для меня)
@MDSayemAhmed Это потому, что «ABCD» всегда является ссылкой из первого вызова, в то время как последующие new String() всегда возвращают новый объект.
Ну, люди голосуют, чтобы закрыть этот вопрос, как "... вызвано проблемой, которую невозможно воспроизвести, или простой типографской ошибкой". Эти люди могут просто быть недостаточно ботанистыми для такого рода вопросов :-)
@AbhishekKumar Петр Янечек, по сути, дал ответ, и я просто «подтвердил» его, насколько это было возможно. (Я просто упоминаю это для случая, когда вы случайно приняли мой ответ, но это, конечно, зависит от вас ...)
Я думаю, что люди, голосующие за закрытие, просто упустили тот факт, что игнорируемое возвращаемое значение на самом деле является частью вопроса, а не просто ошибкой.
Я знаю, что это определенно обман, но его довольно сложно искать ...




Вы неправильно используете intern. intern не изменяет строковый объект, о котором он называется (строки в любом случае неизменяемы), но возвращается - каноническое представление этой строки, которое вы просто отбрасываете. Вместо этого вы должны присвоить его переменной и использовать эту переменную в своих проверках. Например.:
sj1 = sj1.intern();
Когда JVM впервые встречает строку new String(new char[] {'a', 'b', 'h', 'i'}) и вы вызываете для нее intern(), только что созданная ссылка становится канонической и сохраняется в пуле строковых констант. Затем "abhi" извлекается из пула констант - ваш канонический экземпляр был повторно использован.
Ваша проблема в том, что буквальный "java" существует в пуле константных строк до начала вашей программы - JVM просто имеет его там для некоторого использования. Следовательно, вызов intern() на new String(new char[] {'j', 'a', 'v', 'a'}) делает нет интернированием вашей ссылки. Вместо этого он возвращает уже существующее каноническое значение из пула констант, а вы с радостью игнорируете возвращаемое значение.
Не игнорируйте возвращаемое значение, а используйте его. Вы никогда не узнаете, не находилась ли ваша «определенно оригинальная» строка в постоянном пуле с момента запуска JVM. В любом случае, все это зависит от реализации, вы должны либо всегда использовать ссылки, возвращаемые методом intern(), либо никогда. Не смешивайте их.
Тогда почему эта программа печатает true в первой строке, а false в остальном: ideone.com/SQiiRf? Мало того, при всех последующих вызовах callMethod он будет печатать false.
@MDSayemAhmed, поскольку new String() всегда создает новую ссылку, он никогда не достигает пула констант. Первый вызов - это тот, который сохраняется в пуле констант, другие вызовы создают разные объекты, которые игнорируются методом intern(). С другой стороны, литерал "java" всегда будет возвращать один и тот же объект, насколько мне известно. Хотя это в спецификации? Честно говоря, я не уверен.
Да, "java" всегда будет возвращать один и тот же интернированный объект, что я могу подтвердить. Спасибо за терпение.
@ PetrJaneček Ваша строка: "буквальный" java "существует в пуле константных строк до начала вашей программы - JVM просто имеет его там для некоторого использования" действительно имеет смысл. Я попытался переназначить «sj = sj.intern ()» для «java», и это сработало отлично. Спасибо.
В OpenJDK 1.8.0u151 и OpenJDK 9.0.4
char[] cj = {'j','a','v','a'};
String sj = new String(cj);
sj.intern();
String sc = "java";
System.out.println(sj == sc);
печатает true. Однако эта проверка == зависит от того, какие String были интернированы в пул строк перед выполнением String sc = "java". Поскольку во время компиляции константы String интернированы компилятором Java, ссылка sc теперь указывает на "java" в пуле строк, который был помещен туда с sj.intern() с использованием ссылки s1.
Если вы попытаетесь выделить String "java" раньше, например:
String before = "java"; // interned before by compiler
char[] cj = {'j','a','v','a'};
String sj = new String(cj);
sj.intern();
String sc = "java";
System.out.println(sj == sc);
код теперь будет печатать false, поскольку sj.intern() теперь не будет иметь побочных эффектов, поскольку "java" String был интернирован ранее.
Чтобы отладить проблему, проверьте, что находится внутри интернированного пула строк, прежде чем вы дойдете до неудачной проверки. Это может зависеть от поставщика или версии вашей JVM.
Кто-то может возразить, что вызывать intern() только из-за побочного эффекта добавления значения в пул строк бессмысленно. Запись sj = sj.intern() - правильный способ интернировать String.
ответ Петра Янечека почти наверняка правильный (+1 там).
На самом деле доказывая это сложно, потому что большая часть пула строк находится в самой JVM, и вряд ли можно было бы получить к нему доступ без настроенной виртуальной машины.
Но вот еще несколько доказательств:
public class TestInternEx
{
public static void main(String[] args)
{
char[] c1 = { 'a', 'b', 'h', 'i' };
String s1 = new String(c1);
String s1i = s1.intern();
String s1s = "abhi";
System.out.println(System.identityHashCode(s1));
System.out.println(System.identityHashCode(s1i));
System.out.println(System.identityHashCode(s1s));
System.out.println(s1 == s1s);// true
char[] cj =
{ 'j', 'a', 'v', 'a' };
String sj = new String(cj);
String sji = sj.intern();
String sjs = "java";
System.out.println(System.identityHashCode(sj));
System.out.println(System.identityHashCode(sji));
System.out.println(System.identityHashCode(sjs));
System.out.println(sj == sjs);// false
char[] Cj = { 'J', 'A', 'V', 'A' };
String Sj = new String(Cj);
String Sji = Sj.intern();
String Sjs = "JAVA";
System.out.println(System.identityHashCode(Sj));
System.out.println(System.identityHashCode(Sji));
System.out.println(System.identityHashCode(Sjs));
System.out.println(Sj == Sjs);// true
char[] ct =
{ 't', 'r', 'u', 'e' };
String st = new String(ct);
String sti = st.intern();
String sts = "true";
System.out.println(System.identityHashCode(st));
System.out.println(System.identityHashCode(sti));
System.out.println(System.identityHashCode(sts));
System.out.println(st == sts);// false
}
}
Программа выводит для каждой строки идентификационный хэш-код
new StringString#internРезультат примерно такой:
366712642
366712642
366712642
true
1829164700
2018699554
2018699554
false
1311053135
1311053135
1311053135
true
118352462
1550089733
1550089733
false
Можно видеть, что для строки "java" хэш-код new String равен разные от хеш-кода строкового литерала, но последний является одно и тоже как результат вызова String#intern - это означает, что String#intern действительно вернул строку, которая глубоко идентичен самому буквальному.
Я также добавил String "true" в качестве еще одного тестового примера. Он показывает такое же поведение, потому что можно предположить, что строка true уже появилась раньше при загрузке виртуальной машины.
Вы знаете, почему OpenJDK в Linux печатает true, true, true, false? Я думал, что такое обычное слово, как «java», будет интернировано. Есть ли способ проверить, что находится внутри пула строк?
Я пробовал использовать String «false», и он возвращает true, как и ожидалось. @ Marco13 Есть ли у нас какой-либо механизм, чтобы узнать различные строки, которые уже появлялись раньше, когда виртуальная машина загружается?
@KarolDowbecki Опять же, почти невозможно сказать, что JVM делает здесь внутри. Однажды был вопрос о как распечатать весь пул строк, и я попытался ответить на него каким-то авантюрным хаком, но на самом деле посмотреть на внутренний пул JVM со стороны Java почти наверняка невозможно (мне кажется, что это также может быть проблемой безопасности). Предположение дикий: может быть, в Oracle JVM строка появляется только как часть других строк, например "java.lang...", а не как отдельный литерал?
попробовал sj.intern () == sc.intern (), он должен вернуть true