Короткий ответ
Оператор == для ссылочных типов сравнивает ссылки: результат true, только если переменные указывают на один и тот же объект в памяти; для примитивов он сравнивает значения. Метод equals() предназначен для сравнения содержимого объектов, но в базовом Object он реализован через ==, поэтому классы, где важно логическое равенство, — прежде всего String — переопределяют его.
Как это работает подробнее
- == для примитивов — сравнение значений, например 5 == 5 верно.
- == для ссылок — reference equality: проверка, что ссылки ведут на один объект; сравнение с null делается именно так.
- equals() — content equality: сравнение содержимого; переопределение требует согласованного hashCode().
- Object.equals() — по умолчанию делегирует ==, поэтому без переопределения разные объекты с одинаковыми данными считаются неравными.
Пример кода
CompareStrings.java
public class CompareStrings {
public static void main(String[] args) {
String a = new String("java");
String b = new String("java");
System.out.println(a == b); // false — разные объекты
System.out.println(a.equals(b)); // true — одинаковое содержимое
}
}Что использовать на практике
- Сравнивай строки и другие объекты по содержимому через equals(), а не через ==.
- == используй для примитивов, проверки на null и случаев, когда важно, что это один и тот же объект.
- Переопределяя equals(), всегда переопределяй hashCode() в соответствии с контрактом.
Подводные камни
- Литералы из пула могут совпадать по ==, а new String() и runtime-конкатенация — нет: == обманывает, equals() корректен всегда.
- Integer в диапазоне -128..127 кэшируется, поэтому == иногда «работает», но полагаться на это нельзя.
- Несогласованный hashCode() ломает работу HashMap, HashSet и других коллекций на основе хеширования.
Как отвечать на собеседовании
- Разграничь: == сравнивает ссылки и значения, equals() — содержимое → отметь, что Object.equals() это просто == и его надо переопределять → приведи пример со строками → сформулируй правило выбора → упомяни контракт с hashCode().