Короткий ответ Оператор == для ссылочных типов сравнивает ссылки: результат true, только если переменные указывают на один и тот же объект в памяти; для примитивов он сравнивает значения. Метод equals() предназначен для сравнения содержимого объектов, но в базовом Object он реализован через ==, поэтому классы, где важно логическое равенство, — прежде всего String — переопределяют его. Как это работает подробнее == для примитивов — сравнение значений, например 5 == 5 верно. == для ссылок — reference equality: проверка, что ссылки ведут на один объект; сравнение с null делается именно так. equals() — content equality: сравнение содержимого; переопределение требует согласованного hashCode(). Object.equals() — по умолчанию делегирует ==, поэтому без переопределения разные объекты с одинаковыми данными считаются неравными. Пример кода Что использовать на практике Сравнивай строки и другие объекты по содержимому через equals(), а не через ==. == используй для примитивов, проверки на null и случаев, когда важно, что