Перечислите методы класса java.lang.Object
Этот вопрос используется, как способ начать разговор – по большинству методов можно уйти в обсуждении далеко вглубь. В первую очередь важно запомнить сигнатуры – не зная ответов на вопросы по этим методам, можно будет хотя бы рассуждать отталкиваясь от них. Также полезно открыть исходник и внимательно прочитать javadoc-документацию. Поступим как на интервью, и далее рассмотрим каждый из методов детально. Их список:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
#МетодыObject
Этот вопрос используется, как способ начать разговор – по большинству методов можно уйти в обсуждении далеко вглубь. В первую очередь важно запомнить сигнатуры – не зная ответов на вопросы по этим методам, можно будет хотя бы рассуждать отталкиваясь от них. Также полезно открыть исходник и внимательно прочитать javadoc-документацию. Поступим как на интервью, и далее рассмотрим каждый из методов детально. Их список:
1.
public final native Class<?> getClass()
2.
public native int hashCode()
3.
public boolean equals(Object obj)
4.
protected native Object clone() throws CloneNotSupportedException
5.
public String toString()
6.
public final native void notify()
7.
public final native void notifyAll()
8.
public final native void wait(long timeout) throws InterruptedException
9.
public final void wait(long timeout, int nanos) throws InterruptedException
10.
public final void wait() throws InterruptedException
11.
protected void finalize() throws Throwable
#МетодыObject
getClass
Возвращает класс этого экземпляра. То есть результатом вызова
Подробнее об этом читайте на хабре.
#МетодыObject
Возвращает класс этого экземпляра. То есть результатом вызова
.getClass()
переменной типа Foo
может быть как Foo.class
, так и .class
любого из его подклассов. Компилятор страхуется от ClassCastException
в рантайме подменой возвращаемого типа метода на Class<? extends Foo>
.Подробнее об этом читайте на хабре.
#МетодыObject
equals, hashCode
Эти два метода придуманы для использования в Java Collections Framework и связаны общим контрактом, для соблюдения которого переопределять их необходимо вместе. Методы обязательно нужно переопределить чтобы эффективно использовать экземпляры как ключи в HashMap или
Контракт:
1. Если объекты
2.
3. Ничто не может быть
4.
По умолчанию
Подробная инструкция по переопределению этих методов описана в Effective Java Item 9 (больше деталей о волшебном числе 31 здесь).
#МетодыObject
Эти два метода придуманы для использования в Java Collections Framework и связаны общим контрактом, для соблюдения которого переопределять их необходимо вместе. Методы обязательно нужно переопределить чтобы эффективно использовать экземпляры как ключи в HashMap или
HashSet
. HashMap
работает тем эффективнее, чем «лучше» распределение хэшей.Контракт:
1. Если объекты
equals
, у них должны быть одинаковые hashCode
(не обязательно наоборот – коллизии допустимы!)2.
equals
должен быть отношением эквивалентности3. Ничто не может быть
equals(null)
4.
equals
и hashCode
должны возвращать одни и те же значения для одного и того же объекта при каждом последующем вызове, даже если состояние объекта изменилось. Это делает реализацию для изменяемых (mutable) объектов крайне сложной. По умолчанию
equals
сравнивает на ==
. С умолчательным hashCode
дела обстоят интереснее: он зависит от реализации JVM, и может быть неожиданным. Например в OpenJDK 7 это случайное число. Подробная инструкция по переопределению этих методов описана в Effective Java Item 9 (больше деталей о волшебном числе 31 здесь).
#МетодыObject
clone
По умолчанию
По контракту клон должен быть другим объектом (
Альтернативы (многие считают что более удобные) метода
#МетодыObject
По умолчанию
protected
– потому что универсальной реализации нет, а вызов приведет к CloneNotSupportedException
. Нужно писать свою реализацию, делать при этом ее public
и добавлять классу интерфейс Cloneable. Подразумевается что этот метод делает «глубокое копирование», то есть поля-ссылки копи будут вести на копии полей оригинала. Это диктуется соглашением, по которому объект-клон не должен зависеть от оригинала. По контракту клон должен быть другим объектом (
!=
оригиналу). Рекомендуется, чтобы все классы иерархии реализовывали Cloneable
, реализация метода начиналась с super.clone()
(если родитель не Object
), а результат был equals
и имел тот же класс что и оригинал.Альтернативы (многие считают что более удобные) метода
clone
- конструктор копирования и паттерн factory method. Всё, что нужно знать о копировании объектов в Java можно найти в Effective Java Item 11.#МетодыObject
toString
Строковое представление экземпляра. По умолчанию возвращает
#МетодыObject
#Строки
Строковое представление экземпляра. По умолчанию возвращает
"ПолноеИмяКласса@хэшВ16тиричномВиде"
(например "java.lang.Object@1a23b4f"
). Часть после @
– не адрес в памяти, так что умолчательная реализация почти не несет практической пользы. Полезно добавлять нормальную реализацию даже если не необходимо в логике программы – поможет в отладке. Готовый вызов x.toString()
с проверкой на null
уже реализован в String.valueOf(x)
.#МетодыObject
#Строки
finalize
Метод придуман для минимизации риска утечки внешних ресурсов. Может быть вызван виртуальной машиной при сборке мусора (добавляя при этом для нее избыточную нагрузку). Это не то же самое, что деструктор в C++.
Есть только гарантии, что метод не будет вызван пока есть ссылки на объект, и что не будет вызван больше одного раза. Даже то, что вызов будет вообще – не факт. Исполнять будет неизвестно какой, но не синхронизированный поток. Исключения проигнорируются.
С давних пор использовать финализаторы не рекомендуется (Effective Java Item 7), а с Java 9 этот метод помечен как deprecated. Вместо финализатора всегда стоит воспользоваться
#МетодыObject
Метод придуман для минимизации риска утечки внешних ресурсов. Может быть вызван виртуальной машиной при сборке мусора (добавляя при этом для нее избыточную нагрузку). Это не то же самое, что деструктор в C++.
Есть только гарантии, что метод не будет вызван пока есть ссылки на объект, и что не будет вызван больше одного раза. Даже то, что вызов будет вообще – не факт. Исполнять будет неизвестно какой, но не синхронизированный поток. Исключения проигнорируются.
С давних пор использовать финализаторы не рекомендуется (Effective Java Item 7), а с Java 9 этот метод помечен как deprecated. Вместо финализатора всегда стоит воспользоваться
try
/finally
, try-with-resource или более специализированными классами пакета java.lang.ref
.#МетодыObject
wait, notify, notifyAll
Часто этот вопрос формулируется как задача Producer-сonsumer. Эту задачу и практические задачи на многопоточность вообще при возможности лучше реализовывать на высокоуровневых примитивах синхронизации. Другой подход – воспользоваться также низкоуровневой, но оптимистической блокировкой на compareAndSet. Но обычно использование
Эти методы вместе с
Чтобы вызывать эти методы у объекта, необходимо чтобы был захвачен его монитор (т.е. нужно быть внутри synchronized-блока на этом объекте). В противном случае будет выброшено
Вызов
В теории, ожидание wait может быть прервано без вызова
Еще два нештатных случая завершения
Различные проблемы реализации блокировок рассмотрены в Java Concurrency in Practice 14.1.3, 14.2. Для желающих разобраться, как блокировки работают в кишках JVM, написана статья на хабре.
#МетодыObject
#Многопоточность
Часто этот вопрос формулируется как задача Producer-сonsumer. Эту задачу и практические задачи на многопоточность вообще при возможности лучше реализовывать на высокоуровневых примитивах синхронизации. Другой подход – воспользоваться также низкоуровневой, но оптимистической блокировкой на compareAndSet. Но обычно использование
notify
/wait
(пессимистическая блокировка) – условие этого задания, то есть требуется реализовать уже существую BlockingQueue.Эти методы вместе с
synchronized
– самый низкий уровень пессимистических блокировок в Java, использующийся внутри реализации примитивов синхронизации. Еще с Java 5 в непосредственном использовании этих методов нет необходимости, но теоретические знания всё еще часто спрашивают на интервью.Чтобы вызывать эти методы у объекта, необходимо чтобы был захвачен его монитор (т.е. нужно быть внутри synchronized-блока на этом объекте). В противном случае будет выброшено
IllegalMonitorStateException
. Так что для полного ответа нужно понимать, как работает monitor lock (блок synchronized
).Вызов
wait
тормозит текущий поток на ожидание на этом объекте и отпускает его монитор. Исполнение продолжится, когда другой поток вызовет notify
и отпустит блокировку монитора. Если на объекте ожидают несколько потоков, notify
разбудит один случайный, notifyAll
- все сразу.В теории, ожидание wait может быть прервано без вызова
notify
, по желанию JVM (spurious wakeup). На практике это бывает крайне редко, но нужно страховаться и после вызова wait
добавлять дополнительную проверку условия завершения ожидания.Еще два нештатных случая завершения
wait
– прерывание потока извне и таймаут ожидания. В случае прерывания выбрасывается InterruptedException
. Для таймаута нужно указать время ожидания параметрами метода wait
. Значение 0 проигнорируется.Различные проблемы реализации блокировок рассмотрены в Java Concurrency in Practice 14.1.3, 14.2. Для желающих разобраться, как блокировки работают в кишках JVM, написана статья на хабре.
#МетодыObject
#Многопоточность