интерн() ведет себя по-разному в Java 6 и Java 7
class Test {
public static void main(String...args) {
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1.intern());
String s2 = "Goodmorning";
if (s1 == s2) {
System.out.println("both are equal");
}
}
}
этот код производит различные выходы в Java 6 и Java 7.
В Java 6 и s1==s2
возвращает условие false
а в Java 7 то s1==s2
возвращает true
. Зачем?
почему эта программа производит разные выходные данные в Java 6 и Java 7?
9 ответов:
кажется, что JDK7 процесс стажера по-другому, как и раньше.
Я протестировал его со сборкой 1.7.0-b147 и получил "оба равны", но при выполнении его (тот же байт-код) с 1,6.0_24 я не получаю сообщение.
Это также зависит от того, гдеString b2 =...
строка находится в исходном коде. Следующий код также не выводит сообщение:class Test { public static void main(String... args) { String s1 = "Good"; s1 = s1 + "morning"; String s2 = "Goodmorning"; System.out.println(s1.intern()); //just changed here s1.intern() and the if condition runs true if(s1 == s2) { System.out.println("both are equal"); } //now it works. } }
кажется
intern
после того, как строка не найдена в пуле строк, вставляет фактический экземпляр s1 в поле бассейн. JVM использует этот пул при создании s2, поэтому он получает ту же ссылку, что и s1. С другой стороны, если s2 создается первым, эта ссылка сохраняется в пуле.
Это может быть результатом перемещения интернированных строк из постоянной генерации кучи Java.найти здесь: важные RFEs, адресованные в JDK 7
в JDK 7 интернированные строки больше не выделяются в постоянном поколении Java куча, но вместо этого выделяются в основной части кучи Java (известной как молодое и старое поколения) вместе с другими объектами, созданными приложением. Это изменение приведет к большему количеству данных, находящихся в основной куче Java, и меньшему количеству данных в постоянном поколении, и таким образом может потребовать изменения размеров кучи. Большинство приложений увидят только относительно небольшие различия в использовании кучи из-за этого изменения, но более крупные приложения, которые загружают много классов или интенсивно используют Строка.метод intern () будет видеть более существенные различия.
не уверен, что это ошибка и из какой версии... В JLS 3.10.5 указано
результатом явного интернирования вычисляемой строки является та же строка, что и любая ранее существовавшая литеральная строка с тем же содержимым.
Итак, вопрос в том, как pre-existing интерпретируется, время компиляции или время выполнения: "Goodmorning" уже существует или нет?
Я предпочитаю путь он был реализован до 7...
опустим ненужные детали из примера:
class Test { public static void main(String... args) { String s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6. } }
рассмотрим
String#intern
как черный ящик. Основываясь на нескольких тестовых случаях, я бы сделал вывод, что реализация выглядит следующим образом:Java 6:
если пул содержит объект, равныйthis
, затем возвращает ссылку на этот объект, в противном случае создайте новую строку (равнуюthis
), поместить в пул и вернуть ссылку на этот созданный экземпляр.Java 7:
если пул содержит объект равенthis
, затем возвращает ссылку на этот объект, остальное поставьthis
в бассейн, и вернутьсяthis
.ни Java 6, ни Java 7 не разбивают контракт метода.
похоже, что новое поведение метода стажера было результатом исправления этой ошибки:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962931.
==
сравнивает ссылки. Метод intern гарантирует, что строки с одинаковым значением имеют одинаковую ссылку.javadoc для строку.интерн метод объясняю:
public String intern ()
возвращает каноническое представление для строкового объекта.
пул строк, изначально пустых, поддерживается в частном порядке строка класса.
когда метод стажера вызывается, если пул уже содержит строка, равная этому объекту строки, как определено equals(Object) метод, затем возвращается строка из пула. В противном случае, это Строковый объект добавляется в пул и ссылка на эту строку объект возвращается.
отсюда следует, что для любых двух строк s и t, S. intern() == t. intern () истинно тогда и только тогда, когда s.равно (t) истинно.
все литеральные строки и строковые постоянные выражения являются интернированный. Строковые литералы определены в §3.10.5 языка Java Спецификация
возвращает: строку, которая имеет то же содержимое, что и эта строка, но является гарантированно из пула уникальных строк.
таким образом, без интернирования компилятор смотрит на константы в коде java и строит свой постоянный пул из этого. Существует другой пул, поддерживаемый классом String, и interning проверяет строку, переданную в пул, и гарантирует, что ссылка уникальна (так что == будет работать).
в jdk6:
String s1="Good";
создает строковый объект "хорошо" в постоянном пуле.
s1=s1+"morning";
создает еще один строковый объект "утро" в постоянном пуле, но на этот раз на самом деле JVM do:s1=new StringBuffer().append(s1).append("morning").toString();
.сейчас
new
оператор создает объект в куче, поэтому ссылка вs1
имеет кучу не постоянный пул а тоString s2="Goodmorning";
создает строковый объект "Goodmorning" в постоянном пуле, ссылка на который хранится вs2
.таким образом,
if(s1==s2)
состояние ложный.но что происходит в jdk7?
ПЕРВЫЙ СЛУЧАЙ:
в первом обрезанном коде вы фактически добавляете три строки в пул строк. 1. С1 = "хорошо"
2. s1 = " Доброе утро "(после объединения) 3. С2 = "Goodmorining"при выполнении if (s1==s2), объекты одинаковы, но ссылки как разные, следовательно, это ложь.
ВТОРОЙ СЛУЧАЙ:
в этом случае вы используете s1.intern (), что означает, что если пул уже содержит строку, равную этому строковому объекту как определено методом equals (Object), затем возвращается строка из пула. В противном случае этот строковый объект добавляется в пул и возвращается ссылка на этот строковый объект.
- s1 = "хорошо"
- s1 = " Доброе утро "(после объединения)
- для строки s2= "Goodmorning" новая строка не добавляется в пул, и вы получаете ссылку на существующую для s2. Следовательно, если (s1==s2) возвращает true.
вы должны использовать
s1.equals(s2)
. Используя==
СString
объекты сравнивают сами ссылки на объекты.Edit: когда я запускаю ваш второй фрагмент кода, я не получаю "оба равны" распечатаны.
Edit2: уточняется, что ссылки сравниваются при использовании '=='.
есть в основном 4 способа сравнения строк:
- "== operator": он просто сравнивает ссылочную переменную строкового объекта. Так что может дать вам неожиданные результаты в зависимости от того, как вы создали строку, т. е. используя конструктор класса String или просто с помощью двойных кавычек, как сделать память по-разному(в куче и бассейн соответственно).
- " метод equals (Object)": это метод класса объекта и перегружается строковым классом. Он сравнивает вся строка и чувствительна к регистру.
- " метод equalsIgnoreCase (String)": это метод класса string и сравнивает всю строку и не учитывает регистр.
- "compares (String) method": сравните обе строки по символу и верните их разницу, если возвращаемое значение равно 0, это означает, что строки равны.
всякий раз, когда вы сравниваете между двумя строками, Не используйте
==
и использоватьeqauls()
потому что вы сравниваете объекты не ссылки:string1.equals(string2);
результат кода зависит от времени выполнения:
class Test { public static void main(String... args) { String s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6. } }
Если вы пишете так:
class Test { public static void main(String... args) { String s = "GoodMorning"; String s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints false for both jdk7 and jdk6. } }
причина - 'ldc #N' (строка загрузки из постоянного пула) и строка.intern () оба будут использовать StringTable в hotspot JVM. Для деталей я написал статью на английском языке пула:http://aprilsoft.cn/blog/post/307.html