Ответ 1
String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""
Что-то подобное вызовет s == s2
для вычисления false.
Множество порогов кода создайте новый Strings
, не подвергая вызов new String()
.
ИЗМЕНИТЬ Спасибо за быстрые ответы. Посмотрите, каков настоящий вопрос. На этот раз я сделал это смело.
Я понимаю разницу между == и .equals. Итак, это не мой вопрос (я действительно добавил для этого контекст)
Я выполняю валидацию ниже для пустых строк:
if( "" == value ) {
// is empty string
}
В прошлом при извлечении значений из db или десериализации объектов из другого node этот тест не удалось, поскольку два экземпляра строк были действительно различными объектными ссылками, хотя они содержали одни и те же данные.
Таким образом, исправление для этих ситуаций было
if( "".equals( value ) ) {
// which returns true for all the empty strings
}
Я в порядке. Это понятно.
Сегодня это произошло еще раз, но это озадачило меня, потому что на этот раз приложение представляет собой очень маленькое автономное приложение , которое не использует сеть вообще, поэтому никаких новых строка извлекается из базы данных и десеризируется из другого node.
Итак, вопрос:
"" == value // yields false
и
"".equals( value ) // yields true
Для локального автономного приложения?
Я уверен, что новый код String() не используется в коде.
И единственный способ, которым может быть ссылка на строку, - это потому, что ему присваивается "" непосредственно в коде (или том, что я думал), как в:
String a = "";
String b = a;
assert "" == b ; // this is true
Как-то (после прочтения кода больше у меня есть ключ) были созданы две разные ссылки на пустые строковые объекты, я хотел бы знать как
Больше в строке ответа jjnguys:
Byte!
EDIT: Заключение
Я нашел причину.
После предложения jjnguy я смог смотреть с разными глазами на код.
Метод вины: StringBuilder.toString()
Новый объект String выделяется и инициализируется, чтобы содержать последовательность символов, представленную в данный момент этим объектом.
Doh!...
StringBuilder b = new StringBuilder("h");
b.deleteCharAt( 0 );
System.out.println( "" == b.toString() ); // prints false
Тайна решена.
В коде используется StringBuilder для обработки постоянно растущей строки. Оказывается, в какой-то момент кто-то сделал:
public void someAction( String string ) {
if( "" == string ) {
return;
}
deleteBankAccount( string );
}
и используйте
someAction( myBuilder.toString() ); // bug introduced.
p.s. Я читал слишком много CodingHorror в последнее время? Или почему я чувствую необходимость добавить некоторые забавные картины животных здесь?
String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""
Что-то подобное вызовет s == s2
для вычисления false.
Множество порогов кода создайте новый Strings
, не подвергая вызов new String()
.
"" == value // yields false
и
"".equals( value ) // yields true
в любое время, когда значение переменной value
не было интернировано. Это будет иметь место, если значение вычисляется во время выполнения. См. JLS раздел 3.10.5 Литералы строк, например, код, иллюстрирующий это:
Таким образом, тестовая программа, состоящая из единицы компиляции (§7.3):
package testPackage; class Test { public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static String hello = "Hello"; }
и блок компиляции:
package other; public class Other { static String hello = "Hello"; }
выводит результат:
true true true true false true
Этот пример иллюстрирует шесть пунктов:
- Литеральные строки в одном классе (§8) в том же пакете (§7) представляют ссылки на один и тот же объект String (§4.3.1).
- Литеральные строки в разных классах в одном пакете представляют ссылки на один и тот же объект String.
- Литеральные строки в разных классах в разных пакетах также представляют ссылки на один и тот же объект String.
- Строки, вычисленные постоянными выражениями (§15.28), вычисляются во время компиляции, а затем обрабатываются так, как если бы они были литералами.
- Строки, вычисленные во время выполнения, создаются и, следовательно, различны.
- Результатом явного интернирования вычисленной строки является та же строка, что и любая ранее существовавшая буквальная строка с тем же содержимым.
Если вы можете схватить книгу "Яблочные головоломки" Джошуа Блоха и Нила Гафтера и посмотреть на загадку 13 "Ферма животных"... у него есть большой совет по этой проблеме. Я собираюсь скопировать соответствующий текст:
"Вы можете знать, что константы времени компиляции типа String
интернированы [JLS 15.28]. Другими словами, любые два константных выражения типа String
, которые обозначают одну и ту же последовательность символов, представлены идентичными ссылками на объекты... Ваш код должен редко, если вообще когда-либо, зависеть от интернирования строковых констант. Interning был разработан исключительно для уменьшения объема памяти виртуальной машины, а не как инструмента для программистов... При сравнении ссылки на объекты, вы должны использовать метод equals
, предпочитая оператор ==
, если вам не нужно сравнивать идентификатор объекта, а не значение."
Что из приведенной выше ссылки я упомянул... страницы 30 - 31 в моей книге.
Ожидаете ли вы, что "abcde".substring(1,2)
и "zbcdefgh".substring(1,2)
дают тот же объект String?
Оба они дают "равные" подстроки, извлеченные из двух разных строк, но представляется вполне разумным, что tehy - разные объекты, поэтому == рассматривает их как разные.
Теперь рассмотрим, когда подстрока имеет длину 0, substring(1, 1)
. Он дает строку с нулевой длиной, но неудивительно, что "abcde".substring(1,1)
- это другой объект из "zbcdefgh".substring(1,2)
, и, следовательно, по крайней мере один из них является другим объектом из "".
Как я понимаю, при компиляции кода Java в байт-код или во время работы программы одни и те же строки в большинстве случаев будут ссылаться на один и тот же объект для сохранения памяти. Поэтому иногда вы избегаете == сравнения строк. Но это оптимизация компилятора, на которую нельзя положиться.
Но иногда бывает так, что компилятор решает не делать эту оптимизацию, или нет возможности для программы видеть, что строки одинаковы, и из-за внезапной проверки не удается, поскольку вы полагаетесь на некоторую базовую оптимизацию voodoo это зависит от реализации используемого jvm и т.д.
Таким образом, использование equals всегда хорошо. Для пустых строк существуют другие возможности, такие как сравнение с длиной == 0 или если вам не нужна обратная совместимость, существует string.empty().
Вам следует попробовать String.length() == 0
.
Почему бы не использовать:
if (value != null && value.length == 0) {
// do stuff (above could be "== null ||"
}
Вы должны использовать equals()
, потому что ==
для объектов сравнивает ссылки, т.е. являются ли они одним и тем же объектом. Хотя во время компиляции Java находит одинаковые строки и заставляет их использовать одну и ту же ссылку (строки неизменяемы), во время выполнения легко создавать пустые строки, которые имеют разные ссылки, где == не соответствует вашему типичному намерению equals()
.
Проверьте эту ссылку: http://mindprod.com/jgloss/string.html#COMPARISON на отличном Canadian Mind Products Java и интернет-глоссарий. Стоит закладка.
javadoc для String.intern()
имеет хорошие комментарии к ==
vs. .equals()
.
В документации также разъясняется, что каждый строковый литерал intern
'd.
public String intern()
Возвращает каноническое представление для строкового объекта.
Пул строк, первоначально пустой, поддерживается в частном порядке классом String.
При вызове метода intern, если пул уже содержит строку равный этому объекту String как определяемый равными (Object) метод, то строка из пула возвращается. В противном случае эта строка объект добавляется в пул и ссылка на этот объект String вернулся.
Отсюда следует, что для любых двух строк s и t, s.intern() == t.intern() является истинно тогда и только тогда, когда s.равнения (t) правда.
Все литералы и строковые значения постоянные выражения интернированы. Строковые литералы определены в §3.10.5 Спецификации языка Java
Возвращает: строка, которая имеет тот же в качестве этой строки, но гарантированный из пула уникальных строки.
Если вы используете поиск кода Google, вы можете найти множество мест, где люди делают эту же ошибку: google для файла:.java\=\=\\ "\" Конечно, это может быть правильная идиома в тщательно контролируемых обстоятельствах, но обычно это просто ошибка.