JavaRush /Java блог /Random /Разбор вопросов и ответов с собеседований на Java-разрабо...
Константин
36 уровень

Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4

Статья из группы Random
Всем привет, сегодня я продолжаю разбор 250+ вопросов с собеседований на Java-разработчика.Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 1Предыдущие части разбора: первая, вторая, третья. Итак, продолжим.

29. Можно ли в конструкторе использовать return?

Можно, но без возвращаемого значения справа от return. То есть можно использовать return; как вспомогательную конструкцию при вычислениях в конструкторе, чтобы срочно закончить (прервать) выполнение дальнейшего кода и завершить инициализацию объекта. Например, у нас есть класс Cat, и если Cat бездомный — isHomeless = true, нам нужно закончить инициализацию и не заполнять другие поля (ведь они нам неизвестны, так как котик бездомный):

public Cat(int age, String name, boolean isHomeless) {
   if (isHomeless){
       this.isHomeless = isHomeless;
       return;
   }
   this.isHomeless = isHomeless;
   this.age = age;
   this.name = name;
}
Но если говорить о конкретных значениях, конструктор не может использовать return для возврата какого-то значения, потому что:
  • при объявлении конструктора у вас не будет ничего похожего на возвращаемый тип;
  • как правило, конструктор неявно вызывается во время создания экземпляра;
  • конструктор — это не метод: это отдельный механизм, единственная цель которого — инициализировать переменные экземпляра, а именно созданием объекта занимается оператор new.
Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 2

30. Можно ли из конструктора бросить исключение?

С исключениями конструкторы работают точно так же, как и методы. И если методы позволяют нам пробрасывать исключения, прописывая в заголовке метода throws <ТипИсключения>, то и конструктор позволяет нам это делать, и так же при наследовании и определении конструктора наследника мы можем расширять тип исключения. Например, IOException -> Exception (но не наоборот). В качестве примера для пробрасывания конструктором исключения возьмем класс Cat. Допустим, при его создании мы хотим вводить имя и возраст с консоли:

public Cat() throws IOException {
   BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
   this.name = reader.readLine();
   this.age = Integer.parseInt(reader.readLine());
}
Так как reader.readLine() бросает исключение IOException, в заголовке мы его прописываем как возможное выбрасываемое исключение.

31. Из каких элементов состоит заголовок класса? Напишите пример

Говоря об элементах, составляющих заголовок класса, рассмотрим небольшую схему:
  • обязательные составляющие будут в скобках <>
  • необязательные — в {}
{модификатор доступа класса}{статичность класса}{финальность класса}{абстрактность класса}<имя класса>{наследование от класса Родителя} {реализация интерфейсов} Итак, что мы имеем: {модификатор доступа класса} — для класса доступны лишь модификаторы public и отсутствующий модификатор доступа, то есть default. {статичность класса}static — модификатор, который указывает, что данный класс статичен, применим только к внутренним классам (классам внутри других классов). {финальность класса} — как мы помним, это модификатор final, при наличии которого класс стает не наследуемым (пример из коробки — String). {абстрактность класса} — модификатор — abstract, который указывает на то, что данный класс может иметь нереализованные методы. Этот модификатор конфликтует с модификатором final, то есть в заголовке класса может быть только один из них, так как модификатор abstract подразумевает, что данный класс будет унаследован и будут реализованы его абстрактные части. А final указывает на то, что это финальная (окончательная) версия класса, и унаследован он быть не может. Собственно, одновременное использование обоих модификаторов будет абсурдным, и компилятор не даст нам этого сделать. <class> — ключевое обязательное слово, которое указывает на объявление класса. <имя класса> — простое имя класса, которое является идентификатором конкретного Java класса. Полное имя класса состоит из полного составного имени пакета + . + простое имя класса. {наследование от класса Родителя} — указание класса родителя (если таковой имеется) с помощью ключевого слова extends. Например, .. extends ParentClass. {реализация интерфейсов} — указание интерфейсов, которые данный класс реализует (если они имеются) с помощью ключевого слова implements. Например: … implements FirstInterface, SecondInterface… Ну и в качестве примера заголовка класса рассмотрим заголовок класса Lion, который наследуется от Cat и реализует интерфейс WildAnimal:

public final class Lion extends Cat implements WildAnimal
Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 3

32. Из каких элементов состоит заголовок метода? Напишите пример

Опять же, при рассмотрении элементов, из которых состоит заголовок метода, рассмотрим небольшую схему, в которой:
  • обязательные составляющие —в скобках <>
  • необязательные — в {}
{модификатор доступа}{статичность метода}{абстрактность метода}{финальность метода}{модификатор синхронизации} {модификатор native}<возвращаемое значение><имя метода> <(> {аргументы метода} <)>{бросаемые исключения} {модификатор доступа} — для метода доступны все модификаторы доступа — public, protected, default, private. {статичность метода}static — модификатор, который указывает, что данный метод статичен, то есть привязан не к объекту, а к классу. {абстрактность метода} — модификатор abstract, который указывает на то, что реализация (тело) метода отсутствует. Для корректной работы также нужен модификатор abstract у класса, в котором приведен метод. Как и в заголовке класса, данный модификатор конфликтует с модификатором final, но помимо него конфликтует и с модификатором static, т.к. абстрактный метод подразумевает переопределение метода в наследнике, а статические методы не переопределяются. {финальность метода}final — модификатор, указывающий на то, что данный метод нельзя переопределить. {модификатор синхронизации}synchronized — модификатор, который означает, что данный метод защищен от одновременного доступа к нему из разных потоков. Если метод не статический, он закрывается на мьютекc this объекта. Если метод статический, он закрывается на мьютекс текущего класса. {модификатор native}native — данный модификатор указывает на то, что метод написан на другом языке программирования. <возвращаемое значение> — тип значения, который должен вернуть метод. Если он не должен ничего возвращать — void. <имя метода> — имя метода, его идентификатор его в системе. {аргументы метода} — аргументы (параметры), которые принимает метод: они необходимы для реализации его функционала. {бросаемые исключения}throws ТипИсключения — перечисление проверяемых исключений, которые может бросать данный метод. И в качестве примера заголовка метода приведу этот:

public static void main(String[] args) throws IOException

33. Создайте в объекте-наследнике конструктор по умолчанию, если в базовом он не определен (но определен другой конструктор)

Я не до конца понимаю сам вопрос, но возможно имеется в виду, что, к примеру, в родителе у нас есть некоторый кастомный конструктор:

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
Поэтому в классе предке нам обязательно нужно определить конструктор, который будет заполнять (вызывать) родительский конструктор:

public  class Lion extends Cat {
 
   public Lion(int age, String name) {
       super(age, name);
   }
}
Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 4

34. Когда применяется ключевое слово this?

В Java this используется в двух разных значениях. 1. Как ссылка на текущий объект, типа this.age = 9. То есть this ссылается на объект, в котором была вызвана и к которому относится код, использующий this. Главная функция — повысить читабельность кода и избежать неоднозначности. Например, при одинаковом имени внутреннего поля класса и аргумента метода:

public void setName(String name) {
   this.name = name;
}
То есть, this.name — поле объекта name — аргумент метода Ссылка this не может использоваться в статических методах. 2. this можно применять в конструкторе в форме вызова метода, типа this(value). В таком случае это будет вызов другого конструктора этого же класса. Словом, можно вызвать сразу два конструктора при создании объекта:

public Cat(int age, String name) {
   this(name);
   this.age = age;
}
 
public Cat(String name) {
   this.name = name;
}
При создании объекта Cat и вызове первого конструктора будут вызваны оба поля объекта и успешно проинициализированы. Есть пара нюансов:
  1. this() работает только в конструкторе.
  2. Ссылка на другой конструктор должна находиться в первой строке блока (тела) конструктора. Поэтому в одном конструкторе более одного (другого) конструктора данного класса вызвать нельзя.
Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 5Больше примеров — в этой статье.

35. Что такое инициализатор?

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

class Cat {
   private int age = 9;
   private  String name = "Tom";
Или задавать извне через конструктор:

class Cat {
   private int age;
   private  String name;
 
   public Cat(int age, String name) {
       this.age = age;
       this.name = name;
   }
Но есть и ещё один способ: задавать внутреннюю переменную объекта через блок инициализации, который имеет вид фигурных скобок { } внутри класса, без имени (как у метода или конструктора):

class Cat {
   private int age;
   private  String name;
 
   {
       age = 10;
       name = "Tom";
   }
То есть блок инициализации — это часть кода, которая загружается при создании объекта. Как правило такие блоки используются для выполнения некоторых сложных вычислений, которые необходимы при загрузке класса. Результаты этих вычислений можно задавать как значения для переменных. Также помимо обычных блоков инициализации существуют статические, которые выглядят так же, но перед фигурной скобкой имеют ключевое слово static:

class Cat {
   private static int age;
   private static String name;
 
   static{
       age = 10;
       name = "Tom";
   }
Этот блок в точности такой же, как и предыдущий. Но если обычный срабатывает при инициализации каждого объекта, то статический отработает лишь однажды, при загрузке класса. В таком блоке, как правило, тоже производят некоторые сложные вычисления для последующей инициализации статических переменных класса. Для статического блока действуют те же ограничения, что и для статических методов: в нем нельзя использовать не статические данные, как и ссылку на текущий объект — this.Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 6Далее мы с вами можем увидеть порядок инициализации класса (вместе с его предком) для лучшего понимания момента, когда срабатывают блоки инициализации.

36. Для наследования класса public class Child extends Parent напишите порядок инициализации объекта

При загрузке класса Child будет следующий порядок инициализации:
  1. Статические поля класса Parent.
  2. Статический блок инициализации класса Parent.
  3. Статические поля класса Сhild.
  4. Статический блок инициализации класса Child.
  5. Не статические поля класса Parent.
  6. Не статический блок инициализации класса Parent.
  7. Конструктор класса Parent.
  8. Не статические поля класса Сhild.
  9. Не статический блок инициализации класса Сhild.
  10. Конструктор класса Сhild.
Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 7Вот небольшая статья, которая на практике объясняет порядок инициализации.

37. Какие вы знаете отношения между классами (объектами)?

Между классами в Java есть два вида отношений:
  • отношения IS-A
Принцип IS-A в ООП основан на наследовании классов или реализации интерфейсов. К примеру, если класс Lion наследует Cat, мы говорим, что Lion является Cat:

Lion IS-A Cat
(но не всякий Cat является Lion-ом) Точно такая же ситуация с интерфейсами. Если класс Lion реализует интерфейс WildAnimal, то они также находятся в отношении:

Lion IS-A WildAnimal
  • отношения HAS-A
Данный тип отношений основан на использовании классов другими классами, ещё называемый “ассоциация”. Ассоциация — это один класс ссылается на другой класс (или даже друг на друга). Например, класс Car может ссылаться на класс Passenger, и это будет отношение:

Car HAS-A Passenger
И наоборот: если Passenger имеет ссылку на Car, то это будет отношение:

Passenger HAS-A Car

38. Какие ассоциативные связи между объектами вы знаете?

Агрегация и композиция — не что иное, как частные случаи ассоциации. Агрегация — отношение, когда один объект является частью другого. Например, пассажир может находиться в машине. Также пассажиров может быть несколько или не быть вовсе (если мы говорим про теслу, то и водитель не обязателен). Например:

public class Car {
   private List passengers = new ArrayList<>();
 
 void setPassenger(Passenger passenger) {
     passengers.add(passenger);
 }
 
   void move() {
       for (Passenger passenger : passengers) {
           System.out.println("Перевозка пассажира - " + passenger.toString());
       }
       passengers.clear();
   }
}
То есть нам не важно количество пассажиров (и есть ли они вообще): от этого функционал класса Car не зависит. Также агрегация подразумевает, что при использовании объекта другим объектом первый можно использовать еще в других объектах. Например, один и тот же студент может входить и в кружок вязания, и в музыкальную группу рокеров, и при этом ходить в группу изучающих английских. Как вы поняли, агрегация — это более свободные ассоциативные отношения классов.Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 8Композиция — еще более жесткое отношение, когда объект не только является частью другого объекта, но и работа другого объекта очень зависит от первого. Например, двигатель у машины. Хоть двигатель и может быть без машины, но вне ее он бесполезен. Ну и машина не может работать без двигателя:

public class Car {
   private Engine engine;
 
   public Car(Engine engine) {
       this.engine = engine;
   }
 
   void startMoving() {
       engine.start();
           ...
   }
Также композиция подразумевает, что при использовании объекта другим объектом первый не может принадлежать кому-либо другому. Если вернуться к нашему примеру, двигатель может принадлежать только одной машине, но никак не двум или более одновременно. На этом сегодня, пожалуй, и сделаем остановочку.Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 4 - 9
Другие материалы серии:
Комментарии (7)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Иван Уровень 34
11 декабря 2022
В вопросе 31: "{модификатор доступа класса} — для класса доступны лишь модификаторы public и отсутствующий модификатор доступа, то есть default." Стоит добавить что для внутренних классов доступны все модификаторы доступа, включая private и package.
Dima Mikhalishchin Уровень 1
17 июля 2022
По поводу вот этого: Между классами в Java есть два вида отношений: отношения IS-A отношения HAS-A В умных книжках пишут, что это называется видами не отношений, а иерархией Основными видами иерархических структур применительно к сложным системам являются: •структура классов (иерархия "is-a") •структура объектов (иерархия "part of"). http://www.serg-dobrinin.narod.ru/tutorial/oop.htm#_Toc182894156
Alexander Elias Уровень 15
1 июня 2021
В 33 вопросе нужно определить конструктор по умолчанию, поэтому конструктор Lion должен быть без параметров.
13 апреля 2021
В 33 вопросе опечатка :) Поэтому в классе предке классе-наследнике нам обязательно нужно определить конструктор, который будет заполнять (вызывать) родительский конструктор.
Илья Уровень 30
3 апреля 2021
Спасибо! Читаю каждую статью с удовольствием!
Е К Уровень 41
31 марта 2021
Спасибо за годный материал! Сохранил для обязательного прочтения
Justinian Уровень 41 Master
30 марта 2021
Внесу уточнения, если что, поправьте меня:

36. Для наследования класса public class Child extends Parent напишите порядок инициализации объекта
должен выглядеть так: 1. Статические поля и статические блоки инициализации класса Parent в текстовом порядке их объявления. 2 Статические поля и статические блоки инициализации класса Сhild в текстовом порядке их объявления. 3. Не статические поля класса и не статические блоки инициализации класса Parent в текстовом порядке их объявления. 4. Конструктор класса Parent. 5. Не статические поля и не статические блоки инициализации класса Сhild в текстовом порядке их объявления. 6. Конструктор класса Сhild. При инициализации, разницы между переменными и блоками кода нету, они инициализируются/отрабатывают в текстовом порядке их объявления. Значение имеет статические они или нестатические Пример кода, который можно запустить для наглядности очередности инициализации: https://pastebin.com/W0PKtVsc Можно запускать и смотреть.