Класс Object в Java и его основные методы
Класс Object (java.lang.Object) — корень иерархии классов в Java: каждый класс неявно наследуется от Object, даже если в объявлении нет extends Object. Object — единственный класс без суперкласса. Поэтому методы класса Object — equals(), hashCode(), toString(), getClass(), clone(), wait(), notify() и другие — доступны у любого объекта, включая массивы. Примитивные типы (int, double и т.д.) объектами не являются и этих методов не имеют.
Все методы класса Object: таблица
В классе Object объявлено 11 публичных и защищённых методов. Часть из них можно переопределять, а методы с модификатором final — нельзя.
| Метод | Модификаторы | Назначение | Можно переопределить? |
|---|---|---|---|
| boolean equals(Object obj) | public | Проверяет, равен ли другой объект текущему | Да, часто |
| int hashCode() | public | Возвращает хэш-код объекта для HashMap, HashSet и других хэш-коллекций | Да, всегда вместе с equals() |
| String toString() | public | Возвращает строковое представление объекта | Да, часто |
| Class<?> getClass() | public final | Возвращает класс объекта во время выполнения (runtime-тип) | Нет |
| Object clone() | protected | Создаёт копию объекта; класс должен реализовать Cloneable, иначе CloneNotSupportedException | Да, редко |
| void finalize() | protected, deprecated | Вызывался сборщиком мусора перед удалением объекта; устарел | Не рекомендуется |
| void notify() | public final | Пробуждает один поток, ожидающий на мониторе этого объекта | Нет |
| void notifyAll() | public final | Пробуждает все потоки, ожидающие на мониторе этого объекта | Нет |
| void wait() | public final | Переводит поток в ожидание, пока другой поток не вызовет notify()/notifyAll() | Нет |
| void wait(long timeout) | public final | То же, но ожидание не дольше timeout миллисекунд | Нет |
| void wait(long timeout, int nanos) | public final | То же, с дополнительной точностью в наносекундах | Нет |
finalize() устарел
Метод finalize() помечен как @Deprecated начиная с Java 9, а с Java 18 (JEP 421) — как подлежащий удалению. Сборщик мусора не гарантирует, когда и будет ли он вызван вообще. Для освобождения ресурсов используйте try-with-resources и интерфейс AutoCloseable, а для крайних случаев — java.lang.ref.Cleaner.
Какие методы Object переопределяют на практике
В повседневной разработке переопределяют в основном три метода класса Object:
- equals() — чтобы сравнивать объекты по содержимому (полям), а не по ссылке;
- hashCode() — обязательно вместе с
equals(), иначе объекты некорректно работают вHashMapиHashSet; - toString() — чтобы получать читаемый вывод в логах, отладчике и при печати.
Методы getClass(), wait(), notify() и notifyAll() объявлены как final, поэтому переопределить их невозможно. clone() переопределяют редко — вместо него чаще пишут конструктор копирования или фабричный метод.
Подробнее: правила сравнения объектов и контракт equals()/hashCode() разобраны в уроке Метод equals() в Java, а форматирование строкового представления — в уроке Метод toString() в Java. Метод clone() рассмотрен в уроке Клонирование объектов в Java.
Как работают equals, hashCode и toString по умолчанию
Если метод не переопределён, используется реализация из класса Object:
equals()сравнивает ссылки, то есть работает как==;hashCode()возвращает число, связанное с конкретным объектом (идентификационный хэш-код), поэтому у двух разных объектов с одинаковыми полями хэш-коды почти всегда разные;toString()возвращаетgetClass().getName() + "@" + Integer.toHexString(hashCode()).
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public static void main(String[] args) {
Point a = new Point(1, 2);
Point b = new Point(1, 2);
System.out.println(a.equals(b)); // false - сравниваются ссылки
System.out.println(a); // Point@1b6d3586 (хэш будет другим)
System.out.println(a.getClass()); // class Point
}
} Переопределим методы, чтобы точки с одинаковыми координатами считались равными:
import java.util.Objects;
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point other = (Point) o;
return x == other.x && y == other.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "Point{x=" + x + ", y=" + y + "}";
}
} Теперь new Point(1, 2).equals(new Point(1, 2)) вернёт true, HashSet найдёт такую точку, а System.out.println выведет Point{x=1, y=2}.
Современная альтернатива
Начиная с Java 16 для простых классов-носителей данных можно объявить record Point(int x, int y) {} — компилятор сам сгенерирует корректные equals(), hashCode() и toString() по всем компонентам.
Методы wait, notify и notifyAll
Методы wait(), notify() и notifyAll() служат для взаимодействия потоков. Они объявлены в Object, потому что монитором (объектом блокировки) в Java может быть любой объект. Вызывать их можно только внутри блока или метода synchronized по этому же объекту — иначе будет выброшено IllegalMonitorStateException.
synchronized (lock) {
while (!ready) {
lock.wait(); // освобождает монитор и ждёт
}
}
// в другом потоке
synchronized (lock) {
ready = true;
lock.notifyAll(); // будит все ожидающие потоки
} Подробно механизм разобран в уроке Межпотоковые коммуникации.
Частые ошибки при работе с методами Object
- Переопределить equals() без hashCode(). Равные объекты попадут в разные корзины хэш-таблицы, и
HashSet.contains()илиHashMap.get()их не найдут. - Перегрузить вместо переопределения. Метод
equals(Point p)— это перегрузка, а не переопределение: коллекции вызываютequals(Object). Аннотация@Overrideловит эту ошибку на этапе компиляции. - Вызывать wait() вне synchronized. Результат —
IllegalMonitorStateExceptionво время выполнения. - Полагаться на finalize() для закрытия ресурсов. Вызов не гарантирован; используйте
try-with-resources. - Вызывать clone() у класса без Cloneable. Реализация Object выбросит
CloneNotSupportedException.
Презентация с видео на Patreon →
Часто задаваемые вопросы
Сколько методов в классе Object в Java?
В классе Object 11 доступных наследникам методов: equals(), hashCode(), toString(), getClass(), clone(), finalize(), notify(), notifyAll() и три перегрузки wait(). Шесть из них (getClass, notify, notifyAll и три метода wait) объявлены final и не переопределяются.
Почему при переопределении equals() нужно переопределять hashCode()?
По контракту равные по equals() объекты обязаны иметь одинаковый hashCode(). Хэш-коллекции сначала ищут корзину по хэш-коду и только потом сравнивают через equals(), поэтому без согласованного hashCode() HashMap и HashSet не найдут равный объект.
Наследуют ли интерфейсы класс Object?
Нет, интерфейс не наследуется от класса. Но по спецификации языка интерфейс без суперинтерфейсов неявно объявляет абстрактные версии public-методов Object, поэтому через переменную интерфейсного типа можно вызвать equals(), hashCode() или toString(). Реализовать их как default-методы в интерфейсе нельзя.
Почему wait() и notify() находятся в Object, а не в Thread?
Потому что в Java монитором может быть любой объект, а ожидание и уведомление привязаны к монитору, а не к потоку. Поток ждёт на конкретном объекте-блокировке, и другой поток будит его через тот же объект.
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии