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

Ответ

В 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 и другими коллекциями, основанными на хэше.

Ответ 18+ 🔞

А, ну это классика, ёпта! Сейчас объясню, как не наступить на эти грабли, а то потом будешь головой об стенку биться, когда тесты начнут гореть из-за какой-то хуйни.

Смотри, в Java есть две штуки для сравнения: == и equals(). И они, блядь, делают разное, хоть и выглядят похоже. Это как сравнивать двух близнецов: с виду одно и то же, а по факту — два разных человека.

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

  • Для примитивов (int, char, boolean) — сравнивает значения. Всё просто: пять — это пять.
  • Для объектов — сравнивает ссылки в памяти. То есть он проверяет, не одна ли это и та же квартира, а не то, что в обеих живут Ивановы с двумя детьми. Даже если в обеих трёшках живут одинаковые семьи, но это разные квартиры в разных домах — == скажет «нет, нихуя не одно и то же».

equals() (метод)

  • По умолчанию, из коробки, он делает ровно то же самое, что и == — тыкает пальцем в адреса памяти. Его реализация в классе Object — это просто return (this == obj);. Честно, да?
  • Но вся его сила в том, что его можно переопределить. Это как взять стандартный кухонный нож и заточить его под свою, ебать, специфическую задачу — например, чистить рыбу. Многие умные дядьки из Java уже так и сделали для популярных классов вроде String или Integer. В них equals() сравнивает уже не адреса, а содержимое объектов. Две строки "Hello" — равны, даже если они висят в разных углах памяти.

Пример, чтобы вообще всё стало ясно:

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(переменнаяИзТеста). Почему? Да потому что если переменнаяИзТеста внезапно null, то этот вызов просто вернёт false. А если сделать наоборот, переменнаяИзТеста.equals("ожидаемоеЗначение"), то тебе в лоб прилетит NullPointerException, и тест накроется медным тазом. Запомни этот финт ушами.

  2. Проверка на null — только через == или !=. Никогда не пиши obj.equals(null). Это либо бессмысленно (вернёт false), либо, если реализация equals() кривая, опять же приведёт к пиздецу. Для null есть свой, отдельный оператор.

  3. Священный контракт equals() и hashCode(). Вот это, блядь, важно. Если ты в своём классе (например, в классе-модельке для тестовых данных) переопределил equals(), чтобы сравнивать объекты по какому-то полю id, то ты ОБЯЗАН переопределить и hashCode() так, чтобы у равных по equals() объектов хэш-коды тоже были одинаковые. Если этого не сделать, то твои объекты начнут вести себя в HashSet или HashMap как последние распиздяи — ты их положишь в коллекцию, а потом не найдешь, потому что они будут сидеть не в той корзине. Доверия к такому коду — ноль ебать.