Object

Object

Является родительским классом для всех остальных классов. Каждый класс напрямую или косвенно наследуется от него, что делает его базовым для всех объектов в Java.

Основные методы
equals

Проверяет равенство двух объектов (эквивалентность).

• Стандартная реализация сравнивает адреса объектов в памяти (содержимое полей не учитывается).

• Равные объекты — это объекты одного класса с одинаковым содержимым полей.

• При переопределении equals нужно переопределить хэшкод.

• Если переопределить equals без hashCode - классы и методы будут некорректно работать (у объекта HashMap пара может быть не найдена).

• Реализация equals должна подчиняться правилам рефлексивности, симметрии, транзитивности и непротиворечивости.

Person person1 = new Person("John", 25);
Person person2 = new Person("John", 25);

if (person1.equals(person2)) {
    System.out.println("Objects are equal");
} else {
    System.out.println("Objects are not equal");
}
hashCode

Возвращает хэш-код объекта (битовая строка фиксированной длины, целочисленный результат работы метода, которому как входной параметр передан объект).

• Вычисление нативное (на C++), используется алгоритм Park Miller, в основе которого random.

• При каждом запуске приложения у объекта будет разный хеш-код (генерируется 1 раз при первом вызове, далее хранится).

• Для одного и того же входного объекта хеш-код всегда будет одинаковым.

• Если хеш-коды разные, то и входные объекты гарантированно разные.

• Если хеш-коды равны, то входные объекты не всегда равны (ограничение числа Int)

• Для генерации хэшкода следует использовать те же поля что и для метода equals.

toString

Возвращает строковое представление объекта.

clone

Создает копию объекта (при имплементации интерфейса Cloneable).

• Метод protected - для использования нужно переопределить.

getClass

Возвращает класс объекта во время выполнения.

finalize

Вызывается перед уничтожением объекта сборщиком мусора.

Методы синхронизации
wait()

Заставляет текущий поток ожидать, пока другой поток не вызовет notify() или notifyAll() на том же объекте. Метод wait вызывается на объекте синхронизации, который должен быть заблокирован (внутри блока synchronized).

// Поток блокируется, пока условие не станет истинным. 
// После вызова wait поток освобождает монитор и переходит в состояние ожидания.
synchronized(object) {
    while (conditionNotMet) {
        object.wait();
    }
    // Продолжение работы после возобновления
}
wait(long timeout)

Заставляет текущий поток ожидать указанное время (в миллисекундах) или до вызова notify().

wait(long timeout, int nanos)

Заставляет поток ждать с учетом миллисекунд и наносекунд.

notify()

Будит один поток, ожидающий на этом объекте. Этот метод не освобождает монитор объекта, на котором он вызывается; поток, вызвавший notify, остаётся владельцем монитора до тех пор, пока не завершит блок synchronized.

// Метод notify пробуждает один из потоков, ожидающих на объекте object, но не освобождает монитор объекта сразу.
synchronized (object) {
    // Изменение состояния
    object.notify();
}
notifyAll()

Будит все потоки, ожидающие на этом объекте. Все пробуждённые потоки будут конкурировать за монитор объекта.

// Все потоки, ожидающие на объекте object, будут пробуждены и конкурировать за получение монитора.
synchronized (object) {
    // Изменение состояния
    object.notifyAll();
}
Клонирование
Shallow Copy

• Создается поверхностная копия объекта, где новый объект имеет ссылки на те же объекты, что и оригинал.

• Изменение внутреннего состояния копии может повлиять на оригинал, если внутренние объекты являются изменяемыми.

• Реализуется с помощью метода clone() из интерфейса Cloneable.

public class Person implements Cloneable {
    private String name;
    private Address address; // объект Address

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // поверхностное клонирование
    }
}
Deep Copy

• Создается полная копия объекта, включая копирование всех объектов, на которые ссылается оригинал.

• Изменение состояния копии не повлияет на оригинал, поскольку все вложенные объекты также клонируются.

• Обычно реализуется вручную путем создания нового объекта и копирования значений всех полей, включая рекурсивное клонирование вложенных объектов.

public class Person implements Cloneable {
    private String name;
    private Address address; // объект Address

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = (Address) address.clone(); // глубокое клонирование
        return cloned;
    }
}
Вопросы на собесе (10)
  1. Основные методы класса Object?

    equals() hashCode() toString() notify() notifyAll() wait() clone() finalize()

  1. Для чего используются методы equals и hashcode у Object?

    Методы equals() и hashCode() используются для сравнения объектов и для оптимизации работы с хэш-структурами, такими как HashMap и HashSet.

  1. Как реализован метод equals?

    Метод equals в классе Object сравнивает ссылки на объекты, чтобы проверить, указывают ли они на один и тот же экземпляр в памяти.

  1. Как реализован метод hashCode?

    Возвращает Int. Разная в зависимости от JVM. В общем случае, хэшкод основывается на внутреннем адресе объекта в памяти или на использовании генератора псевдослучайных чисел (алгоритм park miller).

  1. Почему рекомендуется переопределять оба метода equals и hashcode?

    Рекомендуется переопределять оба метода equals и hashCode, чтобы обеспечить корректное сравнение объектов и правильную работу в коллекциях, таких как HashSet или HashMap. Если equals переопределён, но hashCode нет, то два равных объекта могут иметь разные хэш-коды, что приведет к нарушению контрактов этих коллекций и может вызвать трудности при поиске и удалении объектов. Сравнение объектов может происходить по хэшкодам, что быстрее equals.

  1. Почему при сравнении 2 объектов используется хэш-код, а не только equlas?

    При сравнении двух объектов сначала используют хэш-код, чтобы быстро проверить, могут ли они быть равными. Если хэш-коды разные, объекты точно не равны, и equals не вызывается, что экономит время. Если хэш-коды совпадают, вызывается equals для точного сравнения, чтобы обработать возможные коллизии.

  1. Какие есть правила относительно контракта equals?

    Рефлексивность: Для любого ненулевого референса x, x.equals(x) должно возвращать true.

    Симметричность: Для любых референсов x и y, если x.equals(y) возвращает true, то y.equals(x) также должно возвращать true.

    Транзитивность: Для любых референсов x, y и z, если x.equals(y) возвращает true, а y.equals(z) возвращает true, то x.equals(z) должно возвращать true.

    Согласованность: Для любых референсов x и y, многократные вызовы x.equals(y) должны возвращать одно и то же значение, при условии, что x и y не изменились.

    Неправдивость с null: Для любого ненулевого референса x, x.equals(null) должно возвращать false.

  1. Что произойдет если у всех классов переопределить метод hashCode = 1?

    Это может вызвать проблемы при сравнении объектов, особенно если equals() реализован корректно (сравнивает содержимое объектов). Это сделает объекты технически равными в условиях, где требуется равенство хеш-кодов, что приведет к ложным результатам, а также к трудностям в тестировании и отладке.

  1. Какие виды клонирования есть в Java?

    В Java существуют два основных вида клонирования: Шаринг (Shallow Copy) и Глубокое клонирование (Deep Copy). Шаринг создает поверхностную копию объекта, где новый объект ссылается на те же внутренние объекты, что и оригинал, что может привести к нежелательным изменениям при изменении состояния копии. Глубокое клонирование, напротив, создает полную копию объекта, включая все вложенные объекты, что гарантирует независимость состояния между оригиналом и копией.

  1. Какой метод не входит в класс Object?

    notify()

    notifyAll()

    sleep()

    wait()