Почему для сравнения строк в Java нужно использовать метод equals(), а не оператор ==?

«Почему для сравнения строк в Java нужно использовать метод equals(), а не оператор ==?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Оператор == в Java сравнивает ссылки на объекты (т.е. адреса в памяти), а метод String.equals() сравнивает последовательность символов (содержимое) строк.

Ключевое различие:

  • ==: Проверяет, указывают ли две переменные на один и тот же объект в памяти.
  • equals(): Проверяет, имеют ли два разных объекта одинаковое содержимое.

Почему это важно? В Java строки могут быть представлены разными объектами даже с одинаковым текстом.

Пример, демонстрирующий проблему:

public class StringComparison {
    public static void main(String[] args) {
        // Строка-литерал попадает в пул строк (String Pool)
        String literal1 = "hello";
        String literal2 = "hello";

        // Создание нового объекта String в куче (не в пуле)
        String object1 = new String("hello");
        String object2 = new String("hello");

        // Сравнение через == (ссылки)
        System.out.println("literal1 == literal2: " + (literal1 == literal2)); // true (один объект в пуле)
        System.out.println("object1 == object2:   " + (object1 == object2));   // false (два разных объекта)
        System.out.println("literal1 == object1:  " + (literal1 == object1));  // false (объект в пуле vs объект в куче)

        // Сравнение через equals() (содержимое)
        System.out.println("nliteral1.equals(literal2): " + literal1.equals(literal2)); // true
        System.out.println("object1.equals(object2):   " + object1.equals(object2));     // true
        System.out.println("literal1.equals(object1):  " + literal1.equals(object1));    // true
    }
}

Вывод:

literal1 == literal2: true
object1 == object2:   false
literal1 == object1:  false

literal1.equals(literal2): true
object1.equals(object2):   true
literal1.equals(object1):  true

Практические рекомендации:

  1. Всегда используйте equals() для сравнения строк по значению.
  2. Для сравнения без учета регистра используйте equalsIgnoreCase().
  3. Интернирование строк (String.intern()) может перевести строку в пул, но это редко нужно и может иметь побочные эффекты на производительность.
  4. Помните о проверке на null перед вызовом equals(), чтобы избежать NullPointerException. Безопасный паттерн:
    "expected".equals(variable) // Этот вызов безопасен, даже если variable == null
    // Или, если переменная может быть null:
    Objects.equals(str1, str2); // С Java 7

    Использование == для строк — частая ошибка новичков, ведущая к трудноуловимым логическим багам.