Почему intern () не работает с буквальным java?

Я пробовал код ниже:

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"?

попробовал sj.intern () == sc.intern (), он должен вернуть true

Marcos Vasconcelos 27.03.2018 23:11

Вы не сравниваете строки с ==

nicomp 27.03.2018 23:11

Вы игнорируете возвращаемое значение intern(). Перечитайте документы, присвойте возвращаемое значение исходной ссылке, и вы увидите, что все работает так, как вы ожидаете. Также, пожалуйста, никогда не полагайтесь на intern().

Petr Janeček 27.03.2018 23:11
String.intern не является методом void, он возвращает String. Вы игнорируете возвращаемое значение. Прочтите документацию. Не игнорируйте возвращаемые значения.
Boris the Spider 27.03.2018 23:11

Для меня это похоже на опечатку; вы просто никуда не переназначили s1, sj или sj1.

Makoto 27.03.2018 23:12

@nicomp OP хочет сравнить ссылки в этом случае.

GriffeyDog 27.03.2018 23:12

@MarcosVasconcelos: да, это предполагается. Но это не возвращается. Я тоже удивлен.

Abhishek Kumar 27.03.2018 23:13

Насколько я могу судить, ответы и комментарии до сих пор упускают суть вопроса. По крайней мере, очень они почти не объясняют наблюдаемое поведение, которое описывается, и очень удивляет (по крайней мере, для меня)

Marco13 27.03.2018 23:13

@MDSayemAhmed Это потому, что «ABCD» всегда является ссылкой из первого вызова, в то время как последующие new String() всегда возвращают новый объект.

Petr Janeček 27.03.2018 23:30

Ну, люди голосуют, чтобы закрыть этот вопрос, как "... вызвано проблемой, которую невозможно воспроизвести, или простой типографской ошибкой". Эти люди могут просто быть недостаточно ботанистыми для такого рода вопросов :-)

Marco13 27.03.2018 23:46

@AbhishekKumar Петр Янечек, по сути, дал ответ, и я просто «подтвердил» его, насколько это было возможно. (Я просто упоминаю это для случая, когда вы случайно приняли мой ответ, но это, конечно, зависит от вас ...)

Marco13 27.03.2018 23:48

Я думаю, что люди, голосующие за закрытие, просто упустили тот факт, что игнорируемое возвращаемое значение на самом деле является частью вопроса, а не просто ошибкой.

Radiodef 28.03.2018 00:23

Я знаю, что это определенно обман, но его довольно сложно искать ...

shmosel 28.03.2018 00:34
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
12
13
378
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы неправильно используете 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.

MD Sayem Ahmed 27.03.2018 23:26

@MDSayemAhmed, поскольку new String() всегда создает новую ссылку, он никогда не достигает пула констант. Первый вызов - это тот, который сохраняется в пуле констант, другие вызовы создают разные объекты, которые игнорируются методом intern(). С другой стороны, литерал "java" всегда будет возвращать один и тот же объект, насколько мне известно. Хотя это в спецификации? Честно говоря, я не уверен.

Petr Janeček 27.03.2018 23:29

Да, "java" всегда будет возвращать один и тот же интернированный объект, что я могу подтвердить. Спасибо за терпение.

MD Sayem Ahmed 27.03.2018 23:35

@ PetrJaneček Ваша строка: "буквальный" java "существует в пуле константных строк до начала вашей программы - JVM просто имеет его там для некоторого использования" действительно имеет смысл. Я попытался переназначить «sj = sj.intern ()» для «java», и это сработало отлично. Спасибо.

Abhishek Kumar 27.03.2018 23:37

В 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 String
  • строка, возвращаемая String#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», будет интернировано. Есть ли способ проверить, что находится внутри пула строк?

Karol Dowbecki 27.03.2018 23:54

Я пробовал использовать String «false», и он возвращает true, как и ожидалось. @ Marco13 Есть ли у нас какой-либо механизм, чтобы узнать различные строки, которые уже появлялись раньше, когда виртуальная машина загружается?

Abhishek Kumar 28.03.2018 00:00

@KarolDowbecki Опять же, почти невозможно сказать, что JVM делает здесь внутри. Однажды был вопрос о как распечатать весь пул строк, и я попытался ответить на него каким-то авантюрным хаком, но на самом деле посмотреть на внутренний пул JVM со стороны Java почти наверняка невозможно (мне кажется, что это также может быть проблемой безопасности). Предположение дикий: может быть, в Oracle JVM строка появляется только как часть других строк, например "java.lang...", а не как отдельный литерал?

Marco13 28.03.2018 00:00

Другие вопросы по теме