Что почитать: свежие записи из разных блогов

Записи с тэгом #ООП из разных блогов

Widowmaker1984, блог «Java для собеседований»

Оглавление блога со ссылками

Часть 1. Типы данных, переменные, операторы, циклы, массивы
скрытый текстВопрос 1. Сколько ключевых слов зарезервировано языком, что это за слова, какие из них не используются?
Вопрос 2. Из каких символов может состоять имя переменной (корректный идентификатор)?
Вопрос 3. Что значит слово «инициализация»?
Вопрос 4.1. На какие основные группы можно поделить типы данных? (про примитивные типы)
Вопрос 4.2. На какие основные группы можно поделить типы данных? (про составные типы)
Вопрос 5. Какие примитивные типы вы знаете? Назовите размерность в байтах для каждого типа.
Вопрос 6. Что вы знаете о преобразовании примитивных типов данных, есть ли потеря данных, можно ли преобразовать логический тип?
Вопрос 7. Какими значениями инициализируются переменные по умолчанию?
Вопрос 8. Как передается значение переменной (по ссылке/значению)?
Вопрос 9. Что вы знаете о функции main, какие обязательные условия ее определения?
Вопрос 10. Какие логические операции и операторы вы знаете?
Вопрос 11. В чем разница краткой и полной схемы записи логических операторов?
Вопрос 12. Что такое таблица истинности?
Вопрос 13. Что такое тернарный оператор выбора?
Вопрос 14.1. Какие унарные операции вы знаете?
Вопрос 14.2. Про операторы присвоения, отношения и арифметические бинарные операции.
Вопрос 15. Какая арифметическая операция выполняется при побитовом сдвиге вправо/влево?
Вопрос 16. Какова роль и правила написания оператора выбора?
Вопрос 17. Какие циклы вы знаете, в чем их отличия?
Вопрос 18. Что такое «итерация цикла»?
Вопрос 19. Какие параметры имеет цикл for, можно ли их не задать?
Вопрос 20. Какой оператор используется для немедленной остановки цикла?
Вопрос 21. Какой оператор используется для перехода к следующей итерации цикла?
Вопрос 22. Что такое массив?
Вопрос 23. Какие виды массивов вы знаете?
Вопрос 24. Что вы знаете о классах оболочках?
Вопрос 25. Что такое автоупаковка (boxing/unboxing)?

Часть 2. Объектно-ориентированное программирование
скрытый текстВопрос 26. Назовите принципы ООП и расскажите о каждом.
Вопрос 27. Дайте определение понятию «класс»
Вопрос 28. Что такое поле/атрибут класса?
Вопрос 29. Как правильно организовать доступ к полям класса?
Вопрос 30. Дайте определение понятию «конструктор».
Вопрос 31. Чем отличаются конструкторы по-умолчанию, копирования и конструктор с параметрами?
Вопрос 32. Какие модификации уровня доступа вы знаете, расскажите про каждый из них.
Вопрос 33. Расскажите об особенностях класса с единственным закрытым (private) конструктором.
Вопрос 34. О чем говорят ключевые слова «this», «super», где и как их можно использовать?
Вопрос 35. Дайте определение понятию «метод».
Вопрос 36. Что такое сигнатура метода?
Вопрос 37. Какие методы называются перегруженными?
Вопрос 38. Могут ли нестатические методы перегрузить статические?
Вопрос 39. Расскажите о переопределение методов.
Вопрос 40. Может ли метод принимать разное количество параметров (аргументы переменной длины)?
Вопрос 41. Можно ли сузить уровень доступа/тип возвращаемого значения при переопределении метода?
Вопрос 42. Как получить доступ к переопределенным методам родительского класса?
Вопрос 43. Какие преобразования называются нисходящими и восходящими?
Вопрос 44. Чем отличается переопределение от перегрузки?
Вопрос 45. Где можно инициаилизировать статические/нестатические поля?
Вопрос 46. Зачем нужен оператор instanceof?
Вопрос 47. Зачем нужны и какие бывают блоки инициализации?
Вопрос 48. Каков порядок вызова конструкторов и блоков инициализации двух классов: потомка и его предка?
Вопрос 49. Где и для чего используется модификатор abstract?
Вопрос 50. Можно ли объявить метод абстрактным и статическим одновременно?
Вопрос 51. Что означает ключевое поле static?
Вопрос 52. К каким конструкциям Java применим модификатор static?
Вопрос 53. Что будет, если в static блоке кода возникнет исключительная ситуация?
Вопрос 54. Можно ли перегрузить static метод?
Вопрос 55. Что такое статический класс, какие особенности его использования?
Вопрос 56. Какие особенности инициализации final static переменных?
Вопрос 57. Как влияет модификатор static на класс/метод/поле?
Вопрос 58. О чем говорит ключевое слово final?
Вопрос 59. Дайте определение понятию «интерфейс».
Вопрос 60. Какие модификаторы по умолчанию имеют поля и методы интерфейсов?
Вопрос 61. Почему нельзя объявить метод интерфейса с модификатором final или static?
Вопрос 62. Какие типы классов бывают в java (вложенные… и.т.д.)
Вопрос 63. Что вы знаете о вложенных классах, зачем они используются? Классификация, варианты использования, о нарушении инкапсуляции.
Вопрос 64. Каким образом из вложенного класса получить доступ к полю внешнего класса?
Вопрос 65. Какие особенности создания вложенных классов: простых и статических.
Вопрос 66. В чем разница вложенных и внутренних классов?
Вопрос 67. Какие классы называются анонимными?
Вопрос 68. Каким образом можно обратиться к локальной переменной метода из анонимного класса, объявленного в теле этого метода? Есть ли какие-нибудь ограничения для такой переменной?
Вопрос 69. Как связан любой пользовательский класс с классом Object?
Вопрос 70. Расскажите про каждый из методов класса Object.
Вопрос 71. Что такое метод equals(). Чем он отличается от операции ==.
Вопрос 72. Если вы хотите переопределить equals(), какие условия должны удовлетворяться для переопределенного метода?
Вопрос 73. Если equals() переопределен, есть ли какие-либо другие методы, которые следует переопределить?
Вопрос 74. В чем особенность работы методов hashCode и equals? Каким образом реализованы методы hashCode и equals в классе Object? Какие правила и соглашения существуют для реализации этих методов? Когда они применяются?
Вопрос 75. Какой метод возвращает строковое представление объекта?
Вопрос 76. Что будет, если переопределить equals не переопределяя hashCode? Какие могут возникнуть проблемы?
Вопрос 77. Есть ли какие-либо рекомендации о том, какие поля следует использовать при подсчете hashCode?
Вопрос 78. Как вы думаете, будут ли какие-то проблемы, если у объекта, который используется в качестве ключа в hashMap изменится поле, которое участвует в определении hashCode?
Вопрос 79. Чем отличается абстрактный класс о интерфейса, в каких случаях что вы будете использовать?
Вопрос 80. Можно ли получить доступ к private переменным класса и если да, то каким образом?
Вопрос 81. Модификаторы. Назначение и варианты использования.
Вопрос 82. Что такое volatile и transient?
Вопрос 83. Контексты использования модификаторов (класс/поле/метод).
Вопрос 84. Какой из модификаторов более строгий: protected или package-private?
Вопрос 85. Расширение модификаторов при наследовании, переопределение и сокрытие методов. Если у класса-родителя есть метод, объявленный как private, может ли наследник расширить его видимость? А если protected? А сузить видимость?
Вопрос 86. Модификатор abstract и final для классов/методов.
Вопрос 87. Имеет ли смысл объявлять метод private final?
Вопрос 88. Какие особенности инициализации final переменных?
Вопрос 89. Что будет, если единственный конструктор класса объявлен как final?
Вопрос 91. Что такое finalize? Зачем он нужен? Что Вы можете рассказать о сборщике мусора и алгоритмах его работы.
Вопрос 92. Почему метод clone объявлен как protected? Что необходимо для реализации клонирования?

Часть 3. Исключения
скрытый текстВопрос 98. Дайте определение понятию «исключение».
Вопрос 99. Какова иерархия исключений?
Вопрос 100. Можно/нужно ли обрабатывать ошибки JVM?
Вопрос 101. Какие существуют способы обработки исключений?
Вопрос 102. О чем говорит ключевое слово throws?
Вопрос 103. В чем особенность блока finally? Всегда ли он исполняется?
Вопрос 104. Может ли не быть ни одного блока catch при отлавливании исключений?
Вопрос 105. Могли бы вы придумать ситуацию, когда блок finally не будет выполнен?
Вопрос 106. Может ли один блок catch отлавливать несколько исключений (с одной и разных веток наследований)?
Вопрос 107. Что вы знаете об обрабатываемых и не обрабатываемых (catched/uncatched) исключениях?
Вопрос 108. В чем особенность RuntimeException?
Вопрос 109. Как написать собственное («пользовательское») исключение? Какими мотивами вы будете руководствоваться при выборе типа исключения: checked/unchecked?
Вопрос 110. Какой оператор позволяет принудительно выбросить исключение?
Вопрос 111. Есть ли дополнительные условия к методу, который потенциально может выбросить исключение?
Вопрос 112. Может ли метод main выбросить исключение вовне и если да, то где будет происходить обработка данного исключения?
Вопрос 113. Если оператор return содержится и в блоке catch и в finally, какой из них «главнее»?
Вопрос 116. Что такое Error? В каком случае используется Error. Приведите пример Error’а.
Вопрос 117. Какая конструкция используется в Java для обработки исключений?
Вопрос 118. Возможно ли использование блока try-finally (без catch)?
Вопрос 119. Предположим, есть блок try-finally. В блоке try возникло исключение и выполнение переместилось в блок finally. В блоке finally тоже возникло исключение. Какое из двух исключений “выпадет” из блока try-finally? Что случится со вторым исключением?

Widowmaker1984, блог «Java для собеседований»

Вопрос 92 (про клонирование объектов)

"Почему метод clone объявлен как protected? Что необходимо для реализации клонирования?"

1. Иногда необходимо получить копию объекта, которая не зависела бы от оригинала и с которой можно было бы производить манипуляции, при этом не изменяя оригинал. При обыкновенном присваивание объектов (obj1 = obj2;) передаются ссылки на объект. В итоге два экземпляра ссылаются на один объект, и изменение одного приведет к изменению другого. В данном случае, на помощь придет интерфейс Cloneable и метод clone() класса Object.

1.1. Метод clone() класса Object создает и возвращает копию объекта с такими же значениями полей. Если копируемый объект содержит ссылки на другие объекты, то ссылки будут скопированы, но дубликаты тех объектов не создаются. Это называется "поверхностным" (shallow) клонированием. Оно работает корректно (по соглашению, метод clone() должен возвращать объект независимый от клонируемого объекта), если объект содержит только примитивные поля или ссылки на неизменяемые объекты.

1.2. Обратите внимание, что метод clone нативный и делает нечто, что иными способами сделать нельзя, а именно создает копию объекта без вызова конструктора.

2. Метод clone() может выбрасывать исключение CloneNotSupportedException. Данное исключение возникает в случае, когда клонируемый класс не имеет реализации интерфейса Cloneable. Интерфейс Cloneable не реализует ни одного метода. Он является всего лишь маркером, говорящим, что данный класс реализует клонирование объекта.

2.1. Сам Object не реализует Clonable, а метод объявлен, как protected. Поэтому он доступен только при наследовании от объекта. Но это не является проблемой, потому как любой класс, является потомком класса Object. При переопределении метод clone() необходимо расширить до public.

2.2.1. Пример расширения метода clone().
public User clone() throws CloneNotSupportedException {
return (User)super.clone();
}

скрытый текст2.3. Перед дизайнерами языка стояла следующая задача: предоставить функциональность клонирования без вызова конструктора пользователям, но оставить за ними право выбора, можно ли подобную операцию проводить над их классами. Последнее было реализовано с помощью маркерного интерфейса (поскольку аннотаций тогда еще не было), а сам метод решили положить в базовый класс, чтобы при его переопределении автор класса мог в случае необходимости (наличия ссылок на изменяемые объекты) доделать поверхностное копирование до более глубокого.

3. Перед тем как объявлять свой класс как Cloneable, следует внимательно проанализировать его структуру, и определить какие поля нужно проинициализировать после выполнения super.clone().

3.1. Из-за того, что интерфейс Cloneable не имеет публичного метода clone(), к этой операции не получается применить полиморфизм в том смысле, что для массива типа Cloneable нельзя вызвать данный метод. Поэтому по сути использование данного интерфейса ничем не лучше, чем предоставление классу аналогичной функциональности через метод с другим именем и без использования данного интерфейса.

3.2. Пример альтернативного решения — использование конструктора копирования, принимающего как параметр объект копируемого типа. Одно из преимуществ конструктора: возможность сменить тип представления данных (например, скопировать данные из LinkedList в ArrayList).

3.3. Но оба вышеуказанных способа полны потенциальных ошибок. Наиболее удобным и гибким способом клонирования является механизм сериализации. Он заключается в сохранении объекта в поток байтов с последующим восстановлением. Если нет особой необходимости обработки полей во время клонирования объектов, именно сериализация является наиболее предпочтительным вариантом для этих целей.

3.3.1. Пример:
Cat vaska = new Cat("Vaska","Gray",4);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream ous = new ObjectOutputStream(baos);
//сохраняем состояние кота Васьки в поток и закрываем его(поток)
ous.writeObject(vaska);
ous.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
//создаём кота для опытов и инициализируем его состояние Васькиным
Cat cloneVaska = (Cat)ois.readObject();

Widowmaker1984, блог «Java для собеседований»

Вопрос 91 (метод finalize и сборка мусора)

"Что такое finalize? Зачем он нужен? Что Вы можете рассказать о сборщике мусора и алгоритмах его работы."

1. В некоторых объектно-ориентированных языках, в частности в C++, есть явные деструкторы, предназначенные для уничтожения объектов. Основная их задача — освобождение памяти, занятной объектами. Поскольку в языке Java реализован механизм автоматической сборки мусора, освобождать память вручную нет никакой необходимости, поэтому в языке Java деструкторы отсутствуют.

1.1. Разумеется, некоторые объекты используют кроме памяти и другие ресурсы, например файлы, или обрабатывают другие обьекты, которые в свою очередь, обращаются к системным ресурсам. В этом случае очень важно, чтобы ресурсы вовремя освобождались.В любой класс можно добавить метод finalize(). Этот метод будет вызван при первой сборке мусора следующей за моментом когда ваш объект стал недостижим.

скрытый текст2. Не стоит полагаться на finalize для чистки данных. Во-первых, нет гарантии, что он будет вызван, т.к. где-то может остаться ссылка на объект. Во-вторых, нет гарантии, в какое время будет вызван метод. Это связано с тем, что после того, как объект становится доступным для сборки и, если в нем переопределен метод finalize, то он не вызывается сразу, а помещается в очередь, которая обрабатывается специально созданным для этого потоком. Стоит отметить, что в очередь на финализацию попадают только те объекты, в которых метод finalize переопределен.

2.1. Есть вероятность, что данный метод не будет вызван совсем, т.к. программа завершит работу раньше.

2.1.1. Существует метод System.runFinalizerOnExit(true), гарантирующий, что метод finalize() будет вызван до того, как программа корректно завершит свою работу. Однако этот метод крайне ненадежен и не рекомендован к использованию. В качестве альтернативы можно применить метод Runtime.addShutdownHook(). Дополнительную информацию о нем можно найти в документации по API.

2.1.2. Есть другой способ быть уверенным, что finalize-методы были запущены для объектов, доступных для сборки: вызвать System.runFinalization() или Runtime.getRuntime().runFinalization().

2.2. Переопределение метода finalize значительно удлиняет время жизни объекта после смерти, так как он будет удален из памяти не раньше второй сборки мусора. А учитывая два первых пункта, если метод finalize у будет тяжелым иили таких объектов будет очень много, то объекты могут довольно долго висеть в фазе финализации и продолжать занимать место в памяти.

3. Интересной особенностью метода является то, что он может снова сделать объект доступным, присвоив this какой-нибудь переменной, хотя так делать не рекомендуется, т.к. при восстановлении объекта, повторно finalize вызван не будет.

3.1. Поэтому если по каким-то причинам очень надо воскресить данный объект, лучше внутри метода finalize создать его копию.

4. Одна из самых неприятных проблем возникающих при использовании метода finalize — это реордеринг. Представьте, что есть два объекта с переопределенным методом finalize, один из которых ссылается на другой. Так вот, если эти объекты стали недостижимы, то порядок вызова методов финализации произойдет в случайном порядке. Таким образом, у вас будет потенциальная опасность вызвать какой-нибудь метод на уже финализированном объекте из метода finalize другого объекта и получить ошибку.

5. Любые исключения выброшенные в теле метода будут проигнорированы.

5.1. Надо не забыть в конце метода вызвать super.finalize(). А учитывая предыдущий пункт, сделать это необходимо в блоке finally.

6. Согласно всему вышесказанному по возможности следует избегать использование метода finalize, вернее не стоит полагаться на него. Лучше освобождать ресурсы программно, а в методе finalize логировать, если этого почему-то сделано не было, чтобы вовремя найти и починить возникшую проблему. Также можно использовать его как последний шанс закрыть ресурс

6.1. Если ресурс должен быть освобожден сразу после его использования, нужно самостоятельно написать соответствующий код. Добавьте метод dispose() или close(), который нужно явно вызвать для очистки памяти. Если класс имеет такой метод, вы должны вызвать его по завершении работы с объектом этого класса. Недостатком является то, что разработчик должен помнить, что ресурс после использования нужно закрыть.

Widowmaker1984, блог «Java для собеседований»

Вопрос 80-89 (модификаторы)

"Можно ли получить доступ к private переменным класса и если да, то каким образом?"
"Модификаторы. Назначение и варианты использования."
"Что такое volatile и transient?"
"Контексты использования модификаторов (класс/поле/метод)."
"Какой из модификаторов более строгий: protected или package-private?"
"Расширение модификаторов при наследовании, переопределение и сокрытие методов. Если у класса-родителя есть метод, объявленный как private, может ли наследник расширить его видимость? А если protected? А сузить видимость?"
"Модификатор abstract и final для классов/методов."
"Имеет ли смысл объявлять метод private final?"
"Какие особенности инициализации final переменных?"
"Что будет, если единственный конструктор класса объявлен как final?"


1. Модификаторы — ключевые слова, которые добавляются при инициализации для изменения значений. Язык Java имеет широкий спектр модификаторов, основные из них:
- модификаторы доступа;
- модификаторы класса, метода, переменной и потока, используемые не для доступа.

скрытый текст2. В Java существуют следующие модификаторы доступа:
- private: члены класса доступны только внутри класса;
- default (package-private) (модификатор, по-умолчанию): члены класса видны внутри пакета (если класс будет так объявлен он будет доступен только внутри пакета);
- protected: члены класса доступны внутри пакета и в наследниках;
- public: члены класс доступны всем;

2.1. Последовательность модификаторов по убыванию уровня закрытости: private, default, protected, public.

2.2. Доступ к полям модификатором доступа private рекомендуется осуществлять через специальные методы с заголовками, начинающимися с get/set.

2.2.1. С помощью Java Reflection можно получить доступ к части кода, к которой мы не должны получать доступ. Например, мы можем получить доступ к закрытым полям класса и менять их значения. Это может быть серьезной угрозой безопасности.

2.3. Во время наследования возможно изменения модификаторов доступа в сторону большей видимости. Так сделано для того, чтобы не нарушался принцип LSP («подкласс не должен требовать от вызывающего кода больше, чем базовый класс, и не должен предоставлять вызывающему коду меньше, чем базовый класс») для наследуемого класса.

2.3.1. Поэтому методы, объявленные как public в суперклассе, также должны быть public во всех подклассах. Методы, объявленные как protected в суперклассе, должны либо быть либо protected, либо public в подклассах; они не могут быть private. Методы, объявленные как private для всех не наследуются, так что нет никакого правила для них.

3. Java предоставляет ряд модификаторов не для доступа, а для реализации многих других функциональных возможностей:
- модификатор static применяется для создания статических методов и переменных класса;
- модификатор final используется для завершения реализации классов, методов и переменных;
- модификатор abstract необходим для создания абстрактных классов и методов;
- модификаторы synchronized и volatile используются в Java для потоков;
- прочие модификаторы (strictfp, transient, native).

3.1. Модификатор synchronized применяется только к методам и их частям. Указывает, что метод может быть доступен только одному потоку одновременно. В Java модификатор synchronized может быть применен с любым из четырех модификаторов уровня доступа.

3.2. Модификатор transient: переменная экземпляра отмеченная как transient указывает виртуальной машине Java (JVM пропустить эту переменную при сериализации содержащего её объекта.

3.2.1. Применяется только для переменных уровня класса (локальные переменные не могут быть объявлены как transient). Transient переменные могут не быть final или static.

3.3. Модификатор volatile используется, чтобы указать JVM, что при обращении к полю не используется кэш (имеется ввиду область памяти в которой JVM может сохранять локальную копию переменной, чтобы уменьшить время обращения к переменной). Для volatile переменной JVM гарантирует синхронизацию для операций чтения/записи, но не гарантирует для операций изменения значения переменной.

3.3.1. Модификатор применим только к переменным экземпляра, которые имеют тип объект или private. Ссылка на объект с модификатором volatile может быть null.

3.3.2. Может использоваться со static переменными. Не используется с final переменными.

3.4. Модификатор strictfp применяется для методов и классов. Обеспечивает выполнение операций над числами типа float и double (с плавающей запятой) по стандарту IEEE 754.

3.5. Модификатор native используется только для методов. Обозначает, что метод написан на другом языке программирования. Классы в Java используют native методы для повышения производительности и доступа к аппаратным средствам Можно предавать и возвращать Java объекты из native методов. Сигнатура метода должна заканчиваться “;”, фигурные скобки вызовут ошибку компиляции.

4.1. Переменная с final может быть инициализирована только один раз. Они не инициализируются по умолчанию, необходимо явно присвоить значение при объявлении или в конструкторе, иначе – ошибка компиляции. Ссылочная переменная, объявленная как final, никогда не может быть назначена для обозначения другого объекта. Однако поля объекта могут быть изменены. Также это справедливо и для массивов, потому что массивы являются объектами: массив может быть изменен, но переменная всегда будет ссылаться на тот же самый массив.

4.1.1. С переменными модификатор final часто используется совместно со static, чтобы сделать переменную класса константой. Подобные переменные должны быть инициализированы во время объявления или в статическом блоке.

4.2. Метод с final не может быть переопределен. Аргументы методов, обозначенные как final, предназначены только для чтения, при попытке изменения будет ошибка компиляции.

4.2.1. Абстрактный класс не может создать экземпляр. Если класс объявлен как abstract, то единственная цель для него быть расширенным через наследование. Класс не может быть одновременно abstract и final, так как класс с модификатором final не может быть расширенным.

4.3. От класса с final нельзя наследоваться. Поэтому все его методы не могут быть переопределены и неявно также становятся финальными.

4.3.1. Абстрактный метод является методом, объявленным без реализации (без тела метода). Методы abstract никогда не могут быть final.

5. "Суперпункт" для разбора частных вопросов.

5.1. "Имеет ли смысл объявлять метод private final?" Модификатор final позволяет заблокировать метод от переопределения в классах наследниках. Однако private методы и так недоступны в классах наследниках, поэтому перепределить такие методы там нельзя и дополнительный модификатор final не окажет никакого влияния. Можно сказать, что private методы и так неявно являются final методами.

5.2. "Что будет, если единственный конструктор класса объявлен как final?" Конструктор не может иметь подобного модификатора.

6. Таблица с возможностью применить модификаторы в том или ином контексте:
http://www.quizful.net/post/features-of-the-application-of-modifiers-in-java

6.1. Классы (включая любые вложенные классы) могут быть default и strictfp. За исключением анонимных они могут быть abstract или final. Вложенные за исключением локальных (включая анонимные) могут иметь модификатор доступа (верхнего уровня — только public). И только вложенные классы могут быть static.

6.2. Методы в общем случае могут использовать все модификаторы за исключением специально предназначенных для переменных transient и volatile. Конструкторы в отличие от методов не может иметь модификаторов abstract, final, native, static или synchronized.

6.3. Переменные в общем случае могут использовать все модификаторы за исключением не имеющих для них смысла abstract, native и strictfp.

6.4. Логический блок может иметь модификаторы default, static и (как часть метода) synchronized.

Widowmaker1984, блог «Java для собеседований»

Вопросы 69-78 (класс Object и его методы equals, hashCode, toString)

"Как связан любой пользовательский класс с классом Object?"
"Расскажите про каждый из методов класса Object."
"Что такое метод equals(). Чем он отличается от операции ==."
"Если вы хотите переопределить equals(), какие условия должны удовлетворяться для переопределенного метода?"
"Если equals() переопределен, есть ли какие-либо другие методы, которые следует переопределить?"
"В чем особенность работы методов hashCode и equals? Каким образом реализованы методы hashCode и equals в классе Object? Какие правила и соглашения существуют для реализации этих методов? Когда они применяются?"
"Какой метод возвращает строковое представление объекта?"
"Что будет, если переопределить equals не переопределяя hashCode? Какие могут возникнуть проблемы?"
"Есть ли какие-либо рекомендации о том, какие поля следует использовать при подсчете hashCode?"
"Как вы думаете, будут ли какие-то проблемы, если у объекта, который используется в качестве ключа в hashMap изменится поле, которое участвует в определении hashCode?"


1. В Java есть специальный суперкласс Object и все классы являются его подклассами (это не требуется указывать явно). Поэтому ссылочная переменная класса Object может ссылаться на объект любого другого класса.

1.1. Так как массивы являются тоже классами, то переменная класса Object может ссылаться и на любой массив.

2. У класса есть несколько важных методов:

скрытый текстprotected native Object clone() throws CloneNotSupportedException — создаёт новый объект, не отличающий от клонируемого;
public boolean equals(Object obj) — определяет, равен ли один объект другому;
protected void finalize() throws Throwable — вызывается сборщиком мусора, когда он определил, что ссылок на объект больше нет;
public final native Class getClass() — получает класс объекта во время выполнения;
public native int hashCode() — возвращает хеш-код, связанный с вызывающим объектом;
public final native void notify() — возобновляет выполнение одного потока, который ожидает вызывающего объекта;
public final native void notifyAll() — возобновляет выполнение всех потоков, которые ожидают вызывающего объекта;
public String toString() — возвращает строковое представление объекта;
public final void wait() throws InterruptedException — приводит данный поток в ожидание, пока другой поток не вызовет notify() или notifyAll() методы для этого объекта.;
public final native void wait(long timeout) throws InterruptedException --приводит данный поток в ожидание, пока другой поток не вызовет notify() или notifyAll() для этого метода, или пока не истечет указанный промежуток времени.
public final void wait(long timeout, int nanos) throws InterruptedException — приводит данный поток в ожидание, пока другой поток не вызовет notify() или notifyAll() для этого метода, или пока не истечет указанный промежуток времени.

3. Хеш-код - это целочисленный результат работы метода, которому в качестве входного параметра передан объект. Этот метод реализован таким образом, что для одного и того-же входного объекта, хеш-код всегда будет одинаковым. Следует понимать, что множество возможных хеш-кодов ограничено примитивным типом int, а множество объектов ограничено только нашей фантазией. Отсюда следует утверждение: “Множество объектов мощнее множества хеш-кодов”. Из-за этого ограничения, вполне возможна ситуация, что хеш-коды разных объектов могут совпасть.

3.1. Ситуация, когда у разных объектов одинаковые хеш-коды называется коллизией. Вероятность возникновения коллизии зависит от используемого алгоритма генерации хеш-кода.

3.2. У любого объекта имется хеш-код, определяемый по умолчанию, который вычисляется по адресу памяти, занимаемой объектом.

3.2.1. А, например, для вычисления хеш-кода в классе String применяется следующий алгоритм:

int hash = 0;
for(int i = 0; i < length(); i++)
hash = 31 * hash + charAt(i);

3.3. Выводы:
- Для одного и того-же объекта, хеш-код всегда будет одинаковым.
- Если объекты одинаковые (т.е. одного класса с одинаковым содержимым полей.), то и хеш-коды одинаковые.
- Если хеш-коды равны, то входные объекты не всегда равны (коллизия).
- Если хеш-коды разные, то и объекты гарантированно разные.

4. В Java, каждый вызов оператора new порождает в памяти новый объект, при этом их содержимое может быть одинаково, то есть эквивалентно. Для проверки эквивалентности в классе Object существует метод equals(), который сравнивает содержимое объектов и выводит значение true, если содержимое эквивалентно, и false — если нет.

object1.equals(object2);

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

4.2. Но на самом деле по умолчанию код метода equals следующий: return (this == obj);
При сравнение объектов, операция “==” вернет true лишь в одном случае — когда ссылки указывают на один объект. В данном случае не учитывается содержимое полей.

4.2.1. При вычислении хэш-кода для объектов класса Object по умолчанию используется Park-Miller RNG алгоритм. В основу работы данного алгоритма положен генератор случайных чисел. Это означает, что при каждом запуске программы у объекта будет разный хэш-код. Получается, что используя реализацию метода hashCode() от класса Object, мы при каждом создании объекта будем получать разные хеш-коды. Мало того, перезапуская программу, мы будем получать абсолютно разные значения, поскольку это просто случайное число.

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

5.1. Например, в классе String метод equals переопределяется таким образом, что возвращается true, если содержимое двух сравниваемых строк одинаковое. А в классе-обертке Integer метод equal переопределяется для выполнения численного сравнения.

5.2. Если переопределить equals не переопределяя hashCode объекты данного класса могут неправильно хранится в контейнерах, использующих данные методы при вставке и извлечении объектов, таких как HashMap, HashTable или HashSet.

5.2.1. Для правильной работы этих компонентов Java предлагает следующее правило для переопределения указанных методов: эквивалентным называется отношение, которое является симметричным, транзитивным и рефлексивным.

Рефлексивность: для любого ненулевого x, x.equals(x) вернет true;
Транзитивность: для любого ненулевого x, y и z, если x.equals(y) и y.equals(z) вернет true, тогда и x.equals(z) вернет true;
Симметричность: для любого ненулевого x и y, x.equals(y) должно вернуть true, тогда и только тогда, когда y.equals(x) вернет true.

5.2.1.1. Повторный вызов метода equals() должен возвращать одно и тоже значение до тех пор, пока какое-либо значение свойств объекта не будет изменено. То есть, если два объекта равны в Java, то они будут равны пока их свойства остаются неизменными.

5.2.1.2. Также для любого ненулевого x, x.equals(null) должно вернуть false.

5.2.2. Соглашение между equals и hashCode в Java:
1) Если объекты равны по результатам выполнения метода equals, тогда их hashcode должны быть одинаковыми.
2) Если объекты не равны по результатам выполнения метода equals, тогда их hashcode могут быть как одинаковыми, так и разными. Однако для повышения производительности, лучше, чтобы разные объекты возвращали разные коды.

5.3. Рекомендации как переопределять метод equals в Java (сравнение с объектом obj):
- Проверьте, не указывают ли ссылки на один объект.
- Проверьте объект на null, а также проверьте, чтобы объекты были одного типа. Не делайте проверку с помощью instanceof так как такая проверка будет возвращать true для подклассов. Вместо этого можно использовать getClass();
- Объявите переменную типа, который вы сравниваете, и приведите obj к этому типу. Потом сравнивайте каждый атрибут типа начиная с численных атрибутов (если имеются) потому что численные атрибуты проверяются быстрей. Сравнивайте атрибуты с помощью операторов И и ИЛИ для объединения проверок с другими атрибутами.

5.4. При подсчете хеш-кода необходимо использовать уникальные, лучше примитивные поля, такие как id или uuid. Причем, если эти поля задействованы при вычислении hashCode, их задействовать и при выполнении equals. Более общий совет: выбирать поля, которые с большой долью вероятности будут различаться. Хеш-код должен быть равномерно распределен на области возможных принимаемых значений.

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

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

6.1. Очень часто при использовании метода toString() для получения описания объекта можно получить набор бессмысленных символов, например, [I@421199e8. На самом деле в них есть смысл, доступный специалистом. Он сразу может сказать, что мы имеем дело с одномерным массивом (одна квадратная скобка), который имеет тип int (символ I). Остальные символы тоже что-то означают, но вам знать это не обязательно.

6.2. По умолчанию метод работает по следующему алгоритму:
getClass().getName() + '@' + Integer.toHexString(hashCode())

6.2.1. Принято переопределять метод, чтобы он выводил результат в читаемом виде.

Widowmaker1984, блог «Java для собеседований»

Вопросы 55, 62-68 (вложенные классы)

"Что такое статический класс, какие особенности его использования?"
"Какие типы классов бывают в java (вложенные… и.т.д.)"
"Что вы знаете о вложенных классах, зачем они используются? Классификация, варианты использования, о нарушении инкапсуляции."
"Каким образом из вложенного класса получить доступ к полю внешнего класса?"
"Какие особенности создания вложенных классов: простых и статических."
"В чем разница вложенных и внутренних классов?"
"Какие классы называются анонимными?"
"Каким образом можно обратиться к локальной переменной метода из анонимного класса, объявленного в теле этого метода? Есть ли какие-нибудь ограничения для такой переменной?"


1. Кроме "обычных" классов верхнего уровня (top level classes) в Java можно объявлять т.н. вложенные классы. Это классы, который объявлены внутри объявления другого класса.

1.1. Вложенные классы делятся на статические (static nested classes) и нестатические (non-static). Нестатические вложенные классы имеют другое название — внутренние классы (inner classes). Внешний класс (outer class) по отношению к ним можно назвать обрамляющим классом.

1.1.1. Итого, "внутренние" (inner) классы — это подмножество "вложенных" (nested), а именно не-статические "вложенные" классы. Тем не менее часто под "внутренними" классами подразумеваются все "вложенные".

скрытый текст1.2. Использование вложенных классов всегда приводит к некоторому нарушению инкапсуляции — вложенный класс может обращаться к закрытым членам внешнего класса (но не наоборот!). По сути внутренний класс всего лишь является специализированным членом внешнего класса.

2. Статические вложенные классы, не имеют доступа к нестатическим полям и методам обрамляющего класса, в чем аналогичны объявленным внутри класса статическим методам. Доступ к нестатическим полям и методам может осуществляться только через ссылку на экземпляр обрамляющего класса. Но static nested классы имеют доступ к любым статическим методам внешнего класса, в том числе и к приватным.

2.1. Польза данных классов заключается в основном в логической группировке сущностей и улучшении инкапсуляции. Другое интересное использование статических вложенных классов - тестирование приватных статических методов.

2.2. Так же как и классы, интерфейсы могут быть вложенными и иметь модификаторы доступа. Но они не могут быть инстанциированы, соответственно объявить нестатический вложенный интерфейс нельзя, так как не будет объекта для ассоциации с экземпляром внешнего класса.

2.2.1. Поэтому объявление вида public interface ImNonStaticInterface будет интерпретироваться в виде public static interface ImNonStaticInterface. То есть неявно будет добавлен модификатор static.

2.3. Статический класс — это всегда вложенный класс. К классу высшего уровня модификатор static неприменим.

3. Нестатические вложенные классы называют также внутренними классами (inner). Внутренний класс имеет доступ ко всем переменным и методам своего внешнего класса и может непосредственно ссылаться на них. Он ассоциируется не с самим внешним классом, а с его экземпляром.

3.1. Объект внутреннего класса связан с внешним объектом-создателем и может обращаться к его членам без каких-либо дополнительных описаний. При совпадении имен во внутреннем и внешнем классе, доступ к полю или методу экземпляра обрамляющего класса можно получить через следующую конструкцию: [Имя обрамляющего класса].this.[Имя члена класса].

3.2. Внутренние классы в Java делятся на три вида:
- внутренние классы-члены (member inner classes);
- локальные классы (local classes);
- анонимные классы (anonymous classes).

4. Внутренний класс-член (member inner classes) может иметь любой модификатор доступа (private, package—private, protected, public).

4.1. При этом, вложенный внутренний класс не может содержать в себе статических методов или статических полей. Это связано с тем что, внутренний класс неявно связан с объектом своего внешнего класса, поэтому он не может объявлять никаких статических методов внутри себя. Кроме этого, внутри таких классов нельзя объявлять перечисления (enum).

5. Локальные классы (local classes) определяются в блоке кода. На практике чаще всего объявление происходит в методе некоторого другого класса. Хотя объявлять локальный класс можно и внутри статических или нестатических блоков инициализации.

5.1. Как и member классы, локальные классы ассоциируются с экземпляром обрамляющего класса и имеют доступ к его полям и методам. Кроме этого, локальный класс может обращаться к локальным переменным и параметрам метода, если они объявлены с модификатором final.

5.1.1 Основная причина, по которой необходимо объявлять локальную переменную как финальную заключается в том, что локальная переменная живёт в стеке до тех пор, пока метод находится в стеке. А в случае использования внутреннего класса возможна ситуация, когда экземпляр внутреннего класса живёт в куче и после выхода из метода, но ему может быть необходим доступ к переменной, объявленной в методе. Для этого, компилятор может сохранить копию локальной переменной, которая объявлена как финальная в поле внутреннего класса.

5.2. У локальных классов есть множество ограничений:
- они видны только в пределах блока, в котором объявлены;
- они не могут быть объявлены как private, public, protected или static;
- при этом могут быть помечены как abstract или final, но не оба одновременно;
- они не могут иметь внутри себя статических объявлений (полей, методов, классов); исключением являются константы (static final);

6. Анонимный класс (anonymous class) - это локальный класс без имени. Так как анонимный класс является локальным классом, он имеет все те же ограничения, что и локальный класс. Они могут быть созданы двумя путями:
- как наследник определённого класса;
- как реализация определённого интерфейса.

6.1. Основное ограничение при использовании анонимных классов — это невозможность описания конструктора, так как класс не имеет имени.

6.2. Любой анонимный внутренний класс может реализовать только один интерфейс. Так же, за один раз можно либо расширить класс, либо реализовать интерфейс, но не одновременно.

6.3. Использование анонимных классов оправдано во многих случаях, в частности когда:
- тело класса является очень коротким;
- нужен только один экземпляр класса;
- класс используется в месте его создания или сразу после него;
- имя класса не важно и не облегчает понимание кода.

Widowmaker1984, блог «Java для собеседований»

Вопросы 59, 60, 61, 79 (интерфейсы)

"Дайте определение понятию «интерфейс»."
"Какие модификаторы по умолчанию имеют поля и методы интерфейсов?"
"Почему нельзя объявить метод интерфейса с модификатором final или static?"
"Чем отличается абстрактный класс о интерфейса, в каких случаях что вы будете использовать?"


1. Механизм наследования очень удобен, но имеет свои ограничения. В частности мы можем наследовать только от одного класса, в отличие, например, от языка С++, где имеется множественное наследование.

2. В языке Java подобную проблему частично позволяют решить интерфейсы. Они определяют некоторый функционал, не имеющий конкретной реализации, который затем реализуют классы, применяющие эти интерфейсы. При этом один класс может применить множество интерфейсов. Создатель интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений, но не тела методов (в этом смысле можно сказать, что интерфейс — это аналог полностью абстрактного класса).

2.1 Чтобы определить интерфейс, вместо class используется ключевое слово interface.

interface Printable{
void print();
}

скрытый текст2.2. Как и в случае с классами, можно добавить перед словом interface спецификатор доступа public (но только если интерфейс определен в файле, имеющем то же имя) или оставить для него доступ по умолчанию, если интерфейс будет использоваться только в пределах своего пакета. Интерфейс может содержать поля, но они автоматически являются статическими (static) и неизменными (final). Все методы и переменные неявно объявляются как public.

2.2.1. Поскольку такие константы по умолчанию они имеют модификатор доступа public static final, их значение доступно из любого места программы.

2.3. Интерфейсы могут вкладываться в классы и в другие интерфейсы. При применении такого интерфейса нам надо указывать его полное имя вместе с именем класса/интерфейса, в который он вложен.

2.4. Интерфейс может наследоваться от другого интерфейса через ключевое слово extends. В этом случае класс должен будет реализовать как методы реализуемого им интерфейса, так и методы его интерфейса-предка.

3. Класс, который собирается использовать определённый интерфейс, использует ключевое слово implements. Оно указывает, что интерфейс лишь определяет форму, которую нужно наполнить кодом.

3.1. Методы, которые реализуют интерфейс, должны быть объявлены как public. Если класс содержит интерфейс, но не полностью реализует определённые им методы, он должен быть объявлен как abstract.

3.2. Если класс использует несколько интерфейсов, они перечисляются за ключевым словом implements и разделяются запятыми.

4. Интерфейсы — это не классы. С помощью ключевого слова new нельзя создать экземпляр интерфейса.

4.1. Но можно объявлять интерфейсные переменные. При этом интерфейсная переменная должна ссылаться на объект класса, реализующего данный интерфейс. Например, если класс Journal реализует интерфейс Printable, то переменная типа Printable может хранить ссылку на объект типа Journal.

4.2. И также как и в случае с классами, интерфейсы могут использоваться в качестве типа параметров метода или в качестве возвращаемого типа.

5. До JDK 8 при реализации интерфейса нужно были обязательно реализовать все его методы в классе. А сам интерфейс мог содержать только определения методов без конкретной реализации. В JDK 8 были добавлены так называемые "методы по умолчанию". И теперь интерфейсы кроме определения методов могут иметь их реализацию по умолчанию, которая используется, если класс, реализующий данный интерфейс, не реализует метод.

5.1. Метод по умолчанию — это обычный метод без модификаторов, который помечается ключевым словом default. Его не обязательно реализовывать, хотя можно переопределить.

5.2. Также начиная с JDK 8 в интерфейсах доступны статические методы - они аналогичны методам класса. Чтобы обратиться к статическому методу интерфейса также, как и в случае с классами, пишут название интерфейса и метод. У этих методов должна быть реализация.

5.2.1. А вот модификатор final для метода интерфейса не имеет смысла, так как подобный метод нельзя было бы реализовать в применяющем этот интерфейс классе.

5.3. Начиная с Java 9 можно определять в интерфейсе методы с модификатором private. Подобные методы могут использоваться только внутри интерфейса, в котором они определены. То есть когда надо выполнять в интерфейсе некоторые повторяющиеся действия, такие действия можно выделить в приватные методы.

6. Главное отличие класса от интерфейса — в том, что класс состоит из интерфейса и реализации. То есть любой класс всегда неявно определяет свой интерфейс, но также может иметь и его реализацию. Наследники класса наследуют не только интерфейс, но и реализацию. В этом смысле наследование от класса вступает в противоречие с инкапсуляцией: чтобы изменить реализацию в наследнике, нам может потребоваться знать ее детали в суперклассе. Интерфейс подобных проблем лишен.

6.1. Абстрактные классы используются только тогда, когда есть "is a" тип отношений; интерфейсы могут быть реализованы классами которые не связаны друг с другом. Поэтому с абстрактными классами вы теряете индивидуальность класса, наследующего его; с интерфейсами вы просто расширяете функциональность каждого класса. В Java класс может наследоваться (реализовывать) от многих интерфейсов, но только от одного абстрактного класса.

6.2. Абстрактный класс может реализовывать нестатические методы; интерфейс нет (хотя, начиная с Java 8, может содержать реализацию по умолчанию).

Widowmaker1984, блог «Java для собеседований»

Вопросы 49-52, 54, 56-58 (модификаторы abstract, final и static)

"Где и для чего используется модификатор abstract?"
"Можно ли объявить метод абстрактым и статическим одновременно?"
"Что означает ключевое поле static?"
"К каким конструкциям Java применим модификатор static?"
"Можно ли перегрузить static метод?"
"Какие особенности инициализации final static переменных?"
"Как влияет модификатор static на класс/метод/поле?"
"О чем говорит ключевое слово final?"


1. Java предоставляет ряд модификаторов не для доступа, а для реализации многих других функциональных возможностей:
- модификатор static применяется для создания статических методов и переменных класса;
- модификатор final используется для завершения реализации классов, методов и переменных;
- модификатор abstract необходим для создания абстрактных классов и методов;
- модификаторы synchronized и volatile используются в Java для потоков.

скрытый текст2. Модификатор abstract используется для создания абстрактных классов и методов.

2.1. Абстрактный класс не может создать экземпляр. Если класс объявлен как abstract, то единственная цель для него быть расширенным через наследование. Класс не может быть одновременно abstract и final, так как класс с модификатором final не может быть расширенным.

2.1.1. Если класс содержит абстрактные методы, он должен быть объявлен как abstract. В противном случае будет сгенерирована ошибка компиляции. Абстрактный класс не обязан содержать абстрактные методы и может содержать обычные.

2.2. Абстрактный метод является методом, объявленным без реализации (без тела метода). Методы abstract никогда не могут быть final. Абстрактный метод заканчивается точкой с запятой. Пример: public abstract sample();

2.2.1. Любой класс, который расширяет абстрактный класс должен реализовать все абстрактные методы суперкласса, если сам не является абстрактным классом.

3. Модификатор static применяется для создания статических методов и переменных класса которые будут существовать или вызываться независимо от каких-либо его экземпляров. Они доступны с использованием имени класса, за которым следует точка и соответствующее имя. Также static применим к внутренним классам и логическим блокам).

3.1. Статические переменные также известны как переменные класса. Они инициализируются во время его загрузки, имеют одну копию, независимо от количества созданных экземпляров класса. Статические поля не сериализируются (только при реализации интерфейса Serializable). В Java локальные переменные не могут быть объявлены статическими (static).

3.2. Статические методы могут вызывать только другие статические методы и имеют доступ только к статическим переменным. Они не могут ссылаться на экземпляр объекта, используя this или super.

3.2.1. Абстрактные методы не могут быть static, потому что модификатор abstract говорит, что метод будет реализован в другом классе, а static наоборот указывает, что этот метод будет доступен по имени текущего класса. Не static методы не могут быть переопределены как static. Сами же статичные методы нельзя переопределить вовсе (хотя можно перегрузить).

3.3. Статические блоки выполняются во время загрузки класса.

4. Модификатор final используется для завершения реализации классов, методов и переменных (включая локальные).

4.1. Переменная с final может быть инициализирована только один раз. Они не инициализируются по умолчанию, необходимо явно присвоить значение при объявлении или в конструкторе, иначе – ошибка компиляции. Ссылочная переменная, объявленная как final, никогда не может быть назначена для обозначения другого объекта. Однако поля объекта могут быть изменены. Также это справедливо и для массивов, потому что массивы являются объектами: массив может быть изменен, но переменная всегда будет ссылаться на тот же самый массив.

4.1.1. С переменными модификатор final часто используется совместно со static, чтобы сделать переменную класса константой. Подобные переменные должны быть инициализированы во время объявления или в статическом блоке.

4.2. Метод с final не может быть переопределен. Аргументы методов, обозначенные как final, предназначены только для чтения, при попытке изменения будет ошибка компиляции.

4.3. От класса с final нельзя наследоваться. Поэтому все его методы не могут быть переопределены и неявно также становятся финальными.

Widowmaker1984, блог «Java для собеседований»

Вопросы 45, 47, 48, 53 (инициализация, ее блоки и порядок)

"Где можно инициаилизировать статические/нестатические поля?"
"Зачем нужны и какие бывают блоки инициализации?"
"Каков порядок вызова конструкторов и блоков инициализации двух классов: потомка и его предка?"
"Что будет, если в static блоке кода возникнет исключительная ситуация?"


1. Блок инициализации (initialization block) — последовательность команд, выполняемых при создании (загрузке) классов и объектов. Существуют два типа: статический блок инициализации, обычно называемый для краткости статический блок (static block), и динамический блок инициализации (instance block).

1.1. При создании объекта выполняются различные команды, указанные в конструкторе. Иногда возникает необходимость расширения возможностей синтаксиса. Как правило, динамический блок существует только для удобства — обычно он может быть легко заменен путем добавления в код новой функции загрузки, а затем ее вызова в начале каждого конструктора. Однако, статический блок в значительной степени увеличивает функциональность программы.

2. Статический блок — это, в сущности, конструктор для всего класса. Его синтаксис:

static {
// Static block code
}

скрытый текст2.1. Команды в статическом блоке будут выполняется при первой загрузке класса. То есть в одном из двух случаев (том, что наступит раньше):
- При создании первого объекта класса в процессе работы программы, перед запуском конструктора.
- При первом вызове статической функции, перед выполнением.

2.2. Например, нужно создать класс, моделирующий автомобили, произведенные конкретной компанией. Каждый объект — это автомобиль, а в классе есть инициализируемое в статическом блоке статическое поле, которое содержит базу данных всех автомобилей.

2.3. Статический блок удобен, если в результате выполнения инициализирующего выражения происходит проверяемое исключение. Или же, если инициализация производится путем выполнения кода, который не может быть представлен в виде выражения.


2.4. Если в явном виде написать любое исключение в статическом блоке, компилятор не скомпилирует исходники. В остальном, взаимодействие с исключениями такое же как и в любом другом месте. Если в блоке вывалится unchecked исключение, класс не будет инициализирован.

3. Динамический блок представляет собой дополнение к конструкторам. Его синтаксис:
{
// Instance block code
}

скрытый текст3.1. Динамический блок выполняется, как если бы он был расположен в самом начале любого конструктора. Если блоков инициализации несколько, они выполняются в порядке следования в тексте класса. Блок инициализации способен генерировать исключения, если их объявления перечислены в предложениях throws всех конструкторов класса.

3.2. Обычно динамический блок применяется для упрощения написания конструктора и не приносит дополнительную функциональность. Он позволяет сэкономить на создании функции запуска и добавлении ее вызова в начало всех конструкторов.

3.2.1. Динамический блок полезен, если необходимо инициализировать поле анонимного класса (в анонимном классе невозможно объявить конструктор)

4. При разработке языка Java был установлен постоянный порядок действий при загрузке.

4.1. Во время загрузки класса порядок выглядит следующим образом:

- Определения статических полей родительских классов.
- Инициализация статических полей и выполнение статических блоков родительских классов.
- Определения статических полей класса.
- Инициализация статических полей и выполнение статических блоков класса.

4.2. Затем, при создании объекта, порядок выглядит следующим образом:

- Определения полей объекта из родительских классов.
- Инициализация полей и выполнение динамических блоков из родительских классов.
- Выполнение конструкторов из родительских классов.
- Определения полей объекта из его класса.
- Инициализация полей и выполнение динамических блоков из его класса.
- Выполнение конструктора из его класса.

4.3. Итого порядок инициализации таков:

- Статические элементы родителя.
- Статические элементы наследника.
- Поля родителя.
- Конструктор родителя.
- Поля наследника.
- Конструктор наследника.

5. Статические поля можно инициализировать:
- при объявлении;
- в статическом блоке инициализации.

6. Нестатические поля можно инициализировать:
- при объявлении;
- в динамическом блоке инициализации;
- в конструкторе.

Widowmaker1984, блог «Java для собеседований»

Вопросы 43, 46 (преобразования ссылочных типов)

"Какие преобразования называются нисходящими и восходящими?"
"Зачем нужен оператор instanceof?"

1. Приведение типов (преобразование типов) — преобразование значения переменной одного типа в значение другого типа.

1.1. Преобразование от от подкласса внизу к суперклассу вверху иерархии называется восходящим (upcasting). Восходящее преобразование всегда безопасно, так как это переход от конкретного типа к более общему типу. Такое преобразование осуществляется автоматически (потому что объект подкласса также представляет собою объект суперкласса).

Object tom = new Person("Tom");

скрытый текст1.2. Обратное не всегда верно. Поэтому нисходящее преобразование (downcasting) от суперкласса к подклассу автоматически не выполняется. В этом случае надо явно использовать операцию преобразования типов.

// нисходящее преобразование от Object к типу Employee
Employee emp = (Employee)sam;

1.2.1. Но при попытке преобразования к типу Employee мы можем получить ошибку при выполнении, если переменная sam — это не ссылка не объект Employee . Нередко данные приходят извне, и мы можем точно не знать, какой именно объект они представляют. Соответственно, возникает большая вероятная столкнуться с ошибкой. И перед тем, как провести преобразование типов, мы можем проверить, возможно ли приведение с помощью оператора instanceof.

2. Выражение sam instanceof Employee проверяет, является ли переменная sam объектом типа Employee. Общая форма: [CсылкаНаОбъект] instanceof [Тип].

2.1. Здесь CсылкаНаОбъект обозначает ссылку на экземпляр класса, а Тип- конкретный тип этого класса. Если ссылка_на_объект относится к указанному типу или может быть приведена к нему (по сути является ли объект экземпляром указанного класса или его потомком), то вычисление оператора instanceof дает в итоге логическое значение true, иначе - логическое значение false.

2.2. Большинство программ не нуждается в операторе instanceof, поскольку типы объектов обычно известны заранее. Но этот оператор может пригодиться при разработке обобщенных процедур, оперирующих объектами из сложной иерархии классов. Хотя обычно там, где код должен работать с параметрами различного типа, принято использовать дженерики.
Страницы: 1 2 следующая →

Лучшее   Правила сайта   Вход   Регистрация   Восстановление пароля

Материалы сайта предназначены для лиц старше 16 лет (16+)