Когда "== s является ложным, но" ".equals(s) истинно

ИЗМЕНИТЬ Спасибо за быстрые ответы. Посмотрите, каков настоящий вопрос. На этот раз я сделал это смело.

Я понимаю разницу между == и .equals. Итак, это не мой вопрос (я действительно добавил для этого контекст)


Я выполняю валидацию ниже для пустых строк:

if( "" == value ) { 
    // is empty string 
} 

В прошлом при извлечении значений из db или десериализации объектов из другого node этот тест не удалось, поскольку два экземпляра строк были действительно различными объектными ссылками, хотя они содержали одни и те же данные.

Таким образом, исправление для этих ситуаций было

if( "".equals( value ) ) {
   // which returns true for all the empty strings
}

Я в порядке. Это понятно.

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

Итак, вопрос:


В условиях OTHER:

"" == 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 в последнее время? Или почему я чувствую необходимость добавить некоторые забавные картины животных здесь?

Ответ 1

String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""

Что-то подобное вызовет s == s2 для вычисления false.

Множество порогов кода создайте новый Strings, не подвергая вызов new String().

Ответ 2

"" == 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), вычисляются во время компиляции, а затем обрабатываются так, как если бы они были литералами.
  • Строки, вычисленные во время выполнения, создаются и, следовательно, различны.
  • Результатом явного интернирования вычисленной строки является та же строка, что и любая ранее существовавшая буквальная строка с тем же содержимым.

Ответ 3

Если вы можете схватить книгу "Яблочные головоломки" Джошуа Блоха и Нила Гафтера и посмотреть на загадку 13 "Ферма животных"... у него есть большой совет по этой проблеме. Я собираюсь скопировать соответствующий текст:

"Вы можете знать, что константы времени компиляции типа String интернированы [JLS 15.28]. Другими словами, любые два константных выражения типа String, которые обозначают одну и ту же последовательность символов, представлены идентичными ссылками на объекты... Ваш код должен редко, если вообще когда-либо, зависеть от интернирования строковых констант. Interning был разработан исключительно для уменьшения объема памяти виртуальной машины, а не как инструмента для программистов... При сравнении ссылки на объекты, вы должны использовать метод equals, предпочитая оператор ==, если вам не нужно сравнивать идентификатор объекта, а не значение."

Что из приведенной выше ссылки я упомянул... страницы 30 - 31 в моей книге.

Ответ 4

Ожидаете ли вы, что "abcde".substring(1,2) и "zbcdefgh".substring(1,2) дают тот же объект String?

Оба они дают "равные" подстроки, извлеченные из двух разных строк, но представляется вполне разумным, что tehy - разные объекты, поэтому == рассматривает их как разные.

Теперь рассмотрим, когда подстрока имеет длину 0, substring(1, 1). Он дает строку с нулевой длиной, но неудивительно, что "abcde".substring(1,1) - это другой объект из "zbcdefgh".substring(1,2), и, следовательно, по крайней мере один из них является другим объектом из "".

Ответ 5

Как я понимаю, при компиляции кода Java в байт-код или во время работы программы одни и те же строки в большинстве случаев будут ссылаться на один и тот же объект для сохранения памяти. Поэтому иногда вы избегаете == сравнения строк. Но это оптимизация компилятора, на которую нельзя положиться.

Но иногда бывает так, что компилятор решает не делать эту оптимизацию, или нет возможности для программы видеть, что строки одинаковы, и из-за внезапной проверки не удается, поскольку вы полагаетесь на некоторую базовую оптимизацию voodoo это зависит от реализации используемого jvm и т.д.

Таким образом, использование equals всегда хорошо. Для пустых строк существуют другие возможности, такие как сравнение с длиной == 0 или если вам не нужна обратная совместимость, существует string.empty().

Ответ 6

Вам следует попробовать String.length() == 0.

Ответ 7

Почему бы не использовать:

if (value != null && value.length == 0) {
    // do stuff (above could be "== null ||"
}

Вы должны использовать equals(), потому что == для объектов сравнивает ссылки, т.е. являются ли они одним и тем же объектом. Хотя во время компиляции Java находит одинаковые строки и заставляет их использовать одну и ту же ссылку (строки неизменяемы), во время выполнения легко создавать пустые строки, которые имеют разные ссылки, где == не соответствует вашему типичному намерению equals().

Ответ 9

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

Возвращает: строка, которая имеет тот же в качестве этой строки, но гарантированный из пула уникальных строки.

Ответ 10

Если вы используете поиск кода Google, вы можете найти множество мест, где люди делают эту же ошибку: google для файла:.java\=\=\\ "\" Конечно, это может быть правильная идиома в тщательно контролируемых обстоятельствах, но обычно это просто ошибка.