ООП. JAVA Flashcards
Какие виды классов есть в java?
- Top level class (Обычный класс):
- Abstract class (Абстрактный класс);
- Final class (Финализированный класс).
- Interfaces (Интерфейс).
- Enum (Перечисление).
- Nested class (Вложенный класс):
- Static nested class (Статический вложенный класс);
- Member inner class (Простой внутренний класс);
- Local inner class (Локальный класс);
- Anonymous inner class (Анонимный класс).
Расскажите про вложенные классы. В каких случаях они применяются?
Класс называется вложенным (Nested class), если он определен внутри другого класса. Вложенный класс должен создаваться только для того, чтобы обслуживать обрамляющий его класс.
Если вложенный класс оказывается полезен в каком-либо ином контексте, он должен стать классом верхнего уровня.
Вложенные классы имеют доступ ко всем (в том числе приватным) полям и методам внешнего класса, но не наоборот.
-
Статические вложенные классы (Static nested classes)
– Есть возможность обращения к внутренним статическим полям и методам класса обертки. -
Вложенные классы
– Есть возможность обращения к внутренним полям и методам класса обертки.
– Не может иметь статических объявлений.
– Внутри такого класса нельзя объявить перечисления.
– Если нужно явно получить this внешнего класса — OuterClass.this -
Локальный класс
– Видны только в пределах блока, в котором объявлены.
– Не могут быть объявлены как private/public/protected или static (по этой причине интерфейсы нельзя объявить локально).
– Не могут иметь внутри себя статических объявлений (полей, методов, классов), но могут иметь константы (static final)
– Имеют доступ к полям и методам обрамляющего класса.
– Можно обращаться к локальным переменным и параметрам метода, если они объявлены с модификатором final или являются effectively final. -
Анонимные классы
– Локальный класс без имени.
Что такое «локальный класс»? Каковы его особенности?
Local inner class (Локальный класс) - это вложенный класс, созданный внутри тела метода. Только этот метод имеет к ним доступ.
~~~
public class OuterClass {
public void someMethod(){
class LocalClass{
}
}
}
~~~
Локальные классы имеют следующие особенности:
* Видны только в пределах блока, в котором объявлены;
* Не могут быть объявлены как private/public/protected или static;
* Не могут иметь внутри себя статических объявлений (полей, методов, классов);
* Имеют доступ к полям и методам обрамляющего класса;
* Могут обращаться к локальным переменным и параметрам метода, если они объявлены с модификатором final.
* Может наследовать: обычные классы; внутренние классы в OuterClassе и его предках; такие же локальные классы определённые в том же методе.
* Может быть наследован таким же локальным классом определённом в том же методе.
* Может имплементировать интерфейс
Что такое «анонимные классы»? Где они применяются?
Анонимный класс - Это вложенный локальный класс без имени. Класс, определяемый как подкласс некоторого данного класса прямо в процессе задания переменной.
public class OuterClass { public void someMethod(){ Callable callable = new Callable() { @Override public Object call() throws Exception { return null; } }; } }
Анонимные классы имеют несколько ограничений:
- Их использование разрешено только в одном месте программы - месте его создания;
- Применение возможно только в том случае, если после порождения экземпляра нет необходимости на него ссылаться;
- Реализует лишь методы своего интерфейса или суперкласса, т.е. не может объявлять каких-либо новых методов, так как для доступа к ним нет поименованного типа.
- Не может быть наследован
Анонимные классы обычно применяются для:
а) Создания объекта функции (function object), например реализация интерфейса Comparator;
создания объекта процесса (process object), такого как экземпляры классов Thread, Runnable и подобных;
b) В статическом методе генерации;
с) Инициализации открытого статического поля final, которое соответствует сложному перечислению типов, когда для каждого экземпляра в перечислении требуется отдельный подкласс.
Анонимные классы всегда являются конечными классами.
Каждое объявление анонимного класса уникально. Видны только внутри того метода, в котором определены.
В документации Oracle приведена хорошая рекомендация: «Применяйте анонимные классы, если вам нужен локальный класс для одноразового использования».
Каким образом из вложенного класса получить доступ к полю внешнего класса?
1) Статический вложенный класс имеет прямой доступ только к статическим полям обрамляющего класса.
2) Простой вложенный класс, может обратиться к любому полю внешнего класса напрямую.
3) В случае, если у вложенного класса уже существует поле с таким же литералом, то обращаться к внешнему полю следует через имя внешнего класса. Например: Outer.this.field.
Что такое перечисления (enum)?
- Перечисления представляют набор логически связанных констант.
- Перечисление фактически представляет новый класс, поэтому можно определить переменную данного типа и использовать ее.
- Перечисления, как и обычные классы, могут определять конструкторы, поля и методы.
- При этом конструктор по умолчанию приватный. Также можно определять методы для отдельных констант.
- Можно создавать публичные геттеры\сеттеры. Они создаются в момент компиляции.
Методы:
* valueOf() возвращает конкретный элемент;
* ordinal() возвращает порядковый номер определенной константы (нумерация
начинается с 0);
* values() возвращает массив всех констант перечисления;
* name() отличается от toString тем, что второй можно переопределить.
Особенности Enum-классов
- Конструктор всегда private или default.
- Могут имплементировать интерфейсы.
- Не могут наследовать класс.
- Можно переопределить toString().
- Нет public конструктора, поэтому нельзя создать экземпляр вне Enum.
- При equals() выполняется ==.
- ordinal() возвращает порядок элементов.
- Может использоваться в TreeSet и TreeMap, т. к. Enum имплементирует Comparable.
- compareTo() имитирует порядок элементов, предоставляемый ordinal().
- Можно использовать в Switch Case.
- values() возвращает массив всех констант.
- Легко создать потокобезопасный singleton без double check volatile переменных.
Недостатки Enum-классов
- К ним не применимы операторы >, <, >=, <=
- enum также требует больше памяти для хранения чем обычная константа.
Что такое Ромбовидное наследование?
Ромбовидное наследование (англ. diamond inheritance) – ситуация в объектно- ориентированных языках программирования с поддержкой множественного наследования, когда два класса B и C наследуют от A, а класс D наследует от обоих классов B и C.
При этой схеме наследования может возникнуть неоднозначность: если объект класса D вызывает метод, определенный в классе A (и этот метод не был переопределен в классе D), а классы B и C по-своему переопределили этот метод, то от какого класса его наследовать:
B или C?
Как проблема ромбовидного наследования решена в java?
В Java нет поддержки множественного наследования классов.
Предположим, что SuperClass – это абстрактный класс, описывающий некоторый метод, а
классы ClassA и ClassB – обычные классы наследники SuperClass, а класс ClassC
наследуется от ClassA и ClassB одновременно. Вызов метода родительского класса
приведет к неопределенности, так как компилятор не знает о том, метод какого именно
суперкласса должен быть вызван. Это и есть основная причина, почему в Java нет
поддержки множественного наследования классов. Интерфейсы – это только
резервирование/описание метода, а реализация самого метода будет в конкретном классе, реализующем эти интерфейсы, таким образом исключается неопределенность при множественном наследовании интерфейсов. В случае, если вызывается default-метод из интерфейса его обязательно надо будет переопределить.
Проблема ромбовидного наследования в Java решается с помощью интерфейсов или с помощью класса абстрактного суперкласса. Интерфейсы позволяют определять только поведение, а не реализацию, поэтому несколько классов могут реализовать один и тот же интерфейс, исключая возможность ромбовидного наследования. Абстрактный суперкласс может определять общую реализацию для всех подклассов, что также исключает ромбовидное наследование.
Что такое конструктор по умолчанию?
Конструктор - метод, вызываемый при создании экземпляра класса. Конструктор не имеет возвращаемого типа. Назначение - инициализировать поля объекта.
Если в классе конструктор не определен явно, то компилятор создает конструктор по умолчанию (default).
Если же в классе есть явно определенный конструктор, то default конструктор не создается, и, если он необходим, его нужно описывать явно.
Могут ли быть приватные конструкторы? Для чего они нужны?
Приватный (помеченный ключевым словом private, скрытый) конструктор может использоваться публичным статическим методом генерации объектов данного класса.
Также доступ к нему разрешен вложенным классам и может использоваться для их нужд запрещает создание экземпляра класса вне методов самого класса, например, чтобы гарантировать существование только одного объекта определённого класса, предположим какого-то ресурса, например БД.
Нужен для реализации паттернов, например singleton.
Расскажите про классы-загрузчики и про динамическую загрузку классов.
При запуске JVM, используются три загрузчика классов:
- Bootstrap ClassLoader - главный загрузчик - загружает платформенные классы JDK из архива rt.jar
- AppClassLoader - системный загрузчик - загружает классы приложения, определенные в CLASSPATH
- Extension ClassLoader - загрузчик расширений - загружает классы расширений, которые по умолчанию находятся в каталоге jre/lib/ext.
Динамическая загрузка происходит “на лету” в ходе выполнения программы с помощью статического метода класса Class.forName(имя класса).
Для чего нужна динамическая загрузка?
Например мы не знаем какой класс нам понадобится и принимаем решение в ходе выполнения программы передавая имя класса в статический метод forName().
Чем отличаются конструкторы по-умолчанию, конструктор копирования и конструктор с параметрами?
-У конструктора по умолчанию отсутствуют какие-либо аргументы.
-Конструктор копирования принимает в качестве аргумента уже существующий объект класса для последующего создания его клона.
-Конструктор с параметрами имеет в своей сигнатуре аргументы (обычно необходимые для инициализации полей класса).
Какие модификаторы доступа есть в Java? Какие применимы к классам?
private (приватный): члены класса доступны только внутри класса. Для обозначения
используется служебное слово private.
default, package-private, package level (доступ на уровне пакета): видимость класса/членов класса только внутри пакета. Является модификатором доступа по умолчанию – специальное обозначение не требуется.
protected (защищенный): члены класса доступны внутри пакета и в наследниках. Для
обозначения используется служебное слово protected.
public (публичный): класс/члены класса доступны всем. Для обозначения используется служебное слово public.
Последовательность модификаторов по возрастанию уровня закрытости: public-> protected->default->private.
Во время наследования возможно изменения модификаторов доступа в сторону большей видимости (для поддержания соответствия принципу подстановки Барбары Лисков).
Класс может быть объявлен с модификатором public и default.
Может ли объект получить доступ к члену класса объявленному как private?
Если да, то каким образом?
- внутри класса доступ к приватной переменной открыт без ограничений;
- вложенный класс имеет полный доступ ко всем (в том числе и приватным) членам содержащего его класса;
- доступ к приватным переменным извне может быть организован через отличные
от приватных методы, которые предоставлены разработчиком класса. Например:
getX() и setX(). - через механизм рефлексии (Reflection API).
Что означает модификатор static?
Статическая переменная - это переменная, принадлежащая классу, а не объекту.
А статический класс- это вложенный класс, который может обращаться только к статическим полям обертывающего его класса.
Внутри static метода нельзя вызвать не статический метод по имени класса.
К каким конструкциям Java применим модификатор static?
- полям;
- методам;
- вложенным классам;
- членам секции import;
- блокам инициализации.
Может ли статический метод быть переопределён или перегружен?
Нельзя переопределять статические методы.
Если вы объявите такой же метод в классе-наследнике (subclass), т.е. метод с таким же именем и сигнатурой, вы лишь «спрячете» метод суперкласса вместо переопределения. Это явление известно как сокрытие методов (hiding methods).
Дело в том, что Java позволяет вызывать статический метод на экземпляре класса. Однако, по факту реализация метода будет выбрана не из типа объекта, а из типа переменной, которая держит ссылку на этот объект.
Перегружен - да. Всё работает точно так же как и с обычными методами - 2 статических метода могут иметь одинаковое имя, если количество их параметров или типов различается.
Могут ли нестатические методы перегрузить статические?
Да. Это будут просто два разных метода для программы. Статический будет доступен по имени класса.
Можно ли сузить уровень доступа/тип возвращаемого значения при переопределении метода?
При переопределении метода нельзя сузить модификатор доступа к методу (например, с public до private), но можно расширить.
Изменить тип возвращаемого значения нельзя, но можно сузить возвращаемое значение, если они совместимы. Например, если метод возвращает объект класса, а переопределенный метод возвращает класс-наследник.
Что можно изменить в сигнатуре метода при переопределении?
Можно ли менять модификаторы (throws и тп)?
В сигнатуре(имя + параметры) менять ничего нельзя.
Возможно расширение уровня доступа.
Изменять тип возвращаемого значения при переопределении метода разрешено только в сторону сужения типа (вместо родительского класса - наследника).
Секцию throws метода можно не указывать, но стоит помнить, что она остаётся действительной, если уже определена у метода родительского класса.
Так же, возможно добавлять новые исключения, являющиеся наследниками от уже объявленных или исключения RuntimeException. Порядок следования таких элементов при переопределении значения не имеет.