В чем разница между оператором сравнения `==` и методом `equals()` в Java

«В чем разница между оператором сравнения `==` и методом `equals()` в Java» — вопрос из категории Java, который задают на 24% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Java == и equals() служат для сравнения, но делают это на разных уровнях абстракции.

  • Оператор == (оператор равенства):

    • Для примитивных типов (int, char, boolean и т.д.) сравнивает значения.
    • Для ссылочных типов (объекты) сравнивает ссылки, т.е. проверяет, указывают ли две переменные на один и тот же объект в памяти (на один адрес).
  • Метод equals():

    • Определен в классе Object и по умолчанию также сравнивает ссылки (его реализация по умолчанию: return (this == obj);).
    • Его основное предназначение — быть переопределенным (overridden) в пользовательских классах для сравнения объектов по их содержимому или бизнес-логике (например, два объекта Person равны, если у них одинаковый id).
    • Многие стандартные классы (String, Integer, LocalDate) уже имеют корректно переопределенный equals().

Наглядный пример:

String str1 = new String("Hello");
String str2 = new String("Hello");
String str3 = str1; // str3 ссылается на тот же объект, что и str1

System.out.println("str1 == str2: " + (str1 == str2));      // false (разные объекты в памяти)
System.out.println("str1.equals(str2): " + str1.equals(str2)); // true (одинаковое содержимое)
System.out.println("str1 == str3: " + (str1 == str3));      // true (одна и та же ссылка)

// Сравнение примитивов
int a = 5;
int b = 5;
System.out.println("a == b: " + (a == b)); // true (значения равны)

Критически важные нюансы для тестирования:

  1. Сравнение строк: Всегда используйте "текст".equals(переменная). Это защищает от NullPointerException, если переменная равна null. Обратный порядок (переменная.equals("текст")) выбросит NPE.
  2. Сравнение с null: Для проверки на null всегда используйте == или !=. Выражение obj.equals(null) либо вернет false (если equals корректно реализован), либо выбросит NPE.
  3. Контракт equals() и hashCode(): Если вы переопределяете equals() в своем классе (например, в классе-модели для тестовых данных), вы обязаны переопределить и hashCode(), чтобы объекты, которые равны по equals(), имели одинаковый хэш-код. Нарушение этого контракта приведет к некорректной работе с HashSet, HashMap и другими коллекциями, основанными на хэше.