JavaRush /Java-Blog /Random-DE /Разбор вопросов и ответов с собеседований на Java-разрабо...

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

Veröffentlicht in der Gruppe Random-DE
Hallo! Знания — сила. Чем больше знаний будет у вас к первому вашему собеседованию, тем увереннее на нем будете себя чувствовать. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 12 - 1С хорошим багажом знаний вас будет сложно сбить с толку, и в то же время вы сможете приятно удивить вашего интервьюера. Поэтому сегодня мы без лишних предисловий продолжим усoderвать вашу теоретическую базу, разбирая 250+ вопросов для Java-разработчика. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 12 - 2

103. Какие есть правила для проверки исключений при наследовании?

Если я правильно понимаю вопрос, тут спрашивают про правила работы с исключениями при наследовании, и они таковы:
  • Переопределяемый oder реализуемый метод в наследнике / реализации не может выбрасывать контролируемые исключения, которые выше по иерархии чем исключения в методе суперкласса / интерфейса.
То есть если у нас есть некий интерфейс Animal с методом, бросающим IOException:

public  interface Animal {
   void voice() throws IOException;
}
В реализации данного интерфейса мы не можем выставить более общее бросаемое исключение (например, Exception, Throwable), но можем заменить его на исключение наследника, Wie, например, на FileNotFoundException:

public class Cat implements Animal {
   @Override
   public void voice() throws FileNotFoundException {
// некоторая реализация
   }
}
  • Конструктор подкласса должен включить в свой блок throws все классы исключений пробрасываемым конструктором суперкласса, к которому идет обращение при создании ein Objektа.
Предположим, что конструктор класса Animal бросает множество исключений:

 public class Animal {
   public Animal() throws ArithmeticException, NullPointerException, IOException {
   }
Тогда наследник класса в конструкторе также обязан их указать:

public class Cat extends Animal {
   public Cat() throws ArithmeticException, NullPointerException, IOException {
       super();
   }
Либо, Wie и в случае с методами — можно указать не те же исключения, а более общие. В нашем случае достаточно будет указать более общее исключение — Exception, так Wie это общий предок всех рассмотренных трех исключений:

public class Cat extends Animal {
   public Cat() throws Exception {
       super();
   }

104. Могли бы вы написать Code, когда блок finally не будет выполнен?

Для начала вспомним, что же такое finally. Ранее мы рассматривали механизм перехвата исключений: блок try очерчивает зону перехвата, в то же время блок (блоки) catch — это Code, который сработает при падении определенного исключения. Finally — третий блок Codeа после слова finally, который является взаимозаменяемым с catch, но не взаимоисключающим. Суть данного блока в том, что Code в нем срабатывает всегда, независимо от результата срабатывания try oder catch (независимо от того упало ли исключение oder нет). Случаи его несрабатывания весьма редки и они являются нештатными. Самый простой случай несрабатывания — это когда в Codeе выше будет вызван метод System.exit(0), который завершает программу (тушит ее):

try {
   throw new IOException();
} catch (IOException e) {
   System.exit(0);
} finally {
   System.out.println("Данное сообщение не будет выведенно в консоль");
}
Также есть некоторые другие ситуации, в которых finally не сработает:
  • Аварийное завершение программы, вызванное критическими неполадками системы, либо падение некоторой Error, которая “уронит” приложение (примером error может стать та же StackOwerflowError возникающая при переполнении памяти стека).
  • Когда через блок ry…finally проходит deamon-поток и параллельно с этим программа завершается. Ведь deamon-поток — это поток для фоновых действий, то есть он не является приоритетным и обязательным и приложение не будет ждать окончания его работы.
  • Самый банальный бесконечный цикл, в try oder catch, попав в который поток там останется навсегда:

    
    try {
       while (true) {
       }
    } finally {
       System.out.println("Данное сообщение не будет выведенно в консоль");
    }

Данный вопрос весьма популярен на собеседованиях новичков, поэтому парочку этих исключительных ситуаций стоит помнить. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 12 - 3

105. Напишите пример обработки нескольких исключений в одном блоке catch

1) Возможно вопрос был задан некорректно. Насколько я понимаю в данном вопросе имеется в виду множество catch для одного блока try:

try {
  throw new FileNotFoundException();
} catch (FileNotFoundException e) {
   System.out.print("Упс, у вас упало исключение - " + e);
} catch (IOException e) {
   System.out.print("Упс, у вас упало исключение - " + e);
} catch (Exception e) {
   System.out.print("Упс, у вас упало исключение - " + e);
}
Если в блоке try падает исключение, то блоки catch поочередно сверху вниз пытаются его перехватить, если у некоторого блока catch это получается, он получает право на обработку исключения, в то время когда остальные блоки ниже уже не смогут попытаться перехватить и по своему обработать его. Поэтому, более узкие исключения ставятся в цепочке catch блоков выше, а более широкие — пониже. К примеру, если у нас в первом catch блоке отлавливается исключение класса Exception, то в остальные блоки проверяемые исключения попасть ниWie не смогут (остальные блоки с наследниками Exception будут абсолютно бесполезны). 2) Вопрос был задан корректно В таком случае наша обработка будет иметь вид вроде следующего:

try {
  throw new NullPointerException();
} catch (Exception e) {
   if (e instanceof FileNotFoundException) {
       // некоторая обработка с сужением типа (FileNotFoundException)e
   } else if (e instanceof ArithmeticException) {
       // некоторая обработка с сужением типа (ArithmeticException)e
   } else if(e instanceof NullPointerException) {
       // некоторая обработка с сужением типа (NullPointerException)e
   }
Словив исключение через catch, мы пытаемся выяснить его конкретный тип через метод instanceof, который используется для проверки — принадлежит ли ein Objekt определенному типу, чтобы потом без негативных последствий можно было сузить его до этого типа. Оба рассмотренных подхода могут применяться в одной и той же ситуации, но про некорректность вопроса я сказал из-за того, что второй вариант я бы не назвал хорошим и в своей практике ни разу не встречал, в то же время первый способ с мультикетчами получил широкое распространение. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 12 - 4

106. Какой оператор позволяет принудительно выбросить исключение? Напишите пример

Выше я уже неоднократно использовал, тем не менее повторю это ключевое слово — throw. Пример использования (принудительная выброска исключения):

throw new NullPointerException();

107. Может метод main выбросить throws-исключение? Если да, то куда передаст?

В первую очередь хочу отметить, то что main это не более чем обычный метод, и да, он вызывается виртуальной Maschine для начала выполнения программы, но кроме этого он может быть вызван и из любого другого Codeа. То есть на него также распространяются обычные правила указания выбрасываемых проверяемых исключений (checked exceptions) после throws:

public static void main(String[] args) throws IOException {
Соответственно в нем тоже могут падать исключения. Если main не был вызван в Wieом-то методе, а был запущен Wie точка запуска программы, то выброшенное им исключение будет обрабатываться перехватчиком .UncaughtExceptionHandler. Данный обработчик один на свой поток (то есть по одному обработчику в каждом потоке). При надобности можно создать свой обработчик и задать его с помощью метода setDefaultUncaughtExceptionHandler, вызванного у ein Objektа Thread.

Многопоточность

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

108. Какие средства для работы с многопоточностью знаете?

Основные/базовые средства для использования многопоточности в Java:
  • Synchronized — механизм закрытия (блокирования) метода / блока при вхождении в него потока, от других потоков.
  • Volatile — механизм обеспечения согласованного доступа к переменной разными потокам, то есть с наличием данного модификатора у переменной все операции присвоения и чтения ее должны быть атомарными. Другими словами потоки не будут копировать эту переменную в свою локальную память и изменять ее, а будут изменять ее изначальное Bedeutung.
Подробнее про volatile читайте вот тут.
  • Runnable — интерфейс, реализовав который (в частности его метод run) в некотором классе:

public class CustomRunnable implements Runnable {
   @Override
   public void run() {
       // некоторая логика
   }
}
И создав ein Objekt этого класса, можно запустить новый поток, задав этот ein Objekt в конструктор нового ein Objektа Thread, и вызвав у него метод start():

Runnable runnable = new CustomRunnable();
new Thread(runnable).start();
Метод start запускает реализованный метод run() в отдельном потоке.
  • Thread — класс, наследовав от которого (при этом переопределив метод run):

public class CustomThread extends Thread {
   @Override
   public void run() {
       // некоторая логика
   }
}
И создав ein Objekt данного класса и запустив его с помощью метода start(), запустим тем самым новый поток:

new CustomThread().start();
  • Concurrency — пакет с инструментами для работы в многопоточной среде.
Он состоит из:
  • Concurrent Collections — набор коллекций, специализированных под работу в многопоточной среде.
  • Queues — специализированные очереди для многопоточной среды (блокирующие и неблокирующие).
  • Synchronisers — специализированные утoderты для работы в многопоточной среде.
  • Executors — механизмы для создания пулов потоков.
  • Locks — механизмы синхронизации потоков (более гибкие нежели стандартные — synchronized, wait, notify, notifyAll).
  • Atomics — классы оптимизированные под многопоточное выполнение, именно у них каждая операция — атомарная.
Подробнее о concurrent пакете читайте вот тут.

109. Расскажите о синхронизации между потоками. Для чего используют методы wait (), notify () — notifyAll () join ()?

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

synchronized (Main.class) {
   // некоторая логика
}
Либо непосредственно в сигнатуре метода:

public synchronized void move() {
   // некоторая логика}
Как я и сказал ранее, synhronized это механизм, позволяющий закрывать блок / метод от других потоков, когда в него уже зашел один поток. Представим блок / метод Wie комнату. Некоторый поток, придя к ней, зайдет в нее и закроет ее на ключ, другие потоки, придя к комнате и увидев, что она закрыта, будут ждать возле нее пока она не освободится. Сделав свои дела, первый поток выходит из комнаты и освобождает ключ. И я не зря постоянно говорил о ключе, ведь он действительно существует. Это специальный ein Objekt, который имеет состояние занято / свободно. Этот ein Objekt прикреплен к каждому ein Objektу Java, поэтому при использовании synhronized блока нам нужно в скобках указывать ein Objekt, на чей мьютекс мы хотим закрывать дверь:

Cat cat = new Cat();
synchronized (cat) {
   // некоторая логика
}
Также можно использовать мьютекс класса, Wie я это сделал в первом примере (Main.class). Когда мы используем synhronized на методе, мы ведь не указываем ein Objekt на которым хотим закрыть верно? В таком случае для нестатического метода будет закрываться на мьютекс ein Objektа this, то есть текущий ein Objekt данного класса. Статический будет закрываться на мьютекс текущего класса (this.getClass();). Подробнее про мьютекс можно почитать вот тут. Ну а про synhronized читать вот тут. Wait() — метод, который освобождает мьютекс и переводит текущий поток в режим ожидания Wie бы прикрепляясь к текущему монитору (что-то вроде якоря). Из-за этого данный метод может быть вызван только из synhronized блока oder метода (иначе что ему освобождать и чего ему ожидать). Также отмечу, что это метод класса Object. Точнее не один, а даже три:
  • Wait() — переводит текущий поток в режим ожидания пока другой поток не вызовет метод notify() oder notifyAll() для этого ein Objektа (об этих методах поговорим дальше).

  • Wait (long timeout) — переводит текущий поток в режим ожидания пока другой поток не вызовет метод notify() oder notifyAll() для этого ein Objektа oder не истечет указанное время — timeout.

  • Wait (long timeout, int nanos) — аналогичен предыдущему, только nanos позволяет указать наносекунды (более точная настройка времени).

  • Notify() — метод позволяющий пробудить один рандомный поток текущего блока синхронизации. Опять же, может быть вызван только в synhronized блоке oder методе (ведь в других местах ему не будет кого размораживать).

  • NotifyAll() — метод, пробуждающий все ожидающие потоки на текущем мониторе (тоже используется только в synhronized блоке oder методе).

110. Как остановить поток?

Первое, что нужно сказать — это то, что при полном выполнении метода run() поток автоматически уничтожается. Но иногда нужно убить его досрочно, до завершения данного метода. И что тогда делать? Возможно, у ein Objektа Thread использовать метод stop()? Как бы не так! Данный метод считается устаревшим и может привести к сбоям системы. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 12 - 6Ну а что тогда? Есть два способа сделать это: Первый — это использование своего внутреннего логического флага. Давайте рассмотрим на примере. У нас есть своя реализация потока, который должен выводить на экран некую фразу до его полной остановки:

public class CustomThread extends Thread {
private boolean isActive;
 
   public CustomThread() {
       this.isActive = true;
   }
 
   @Override
   public void run() {
       {
           while (isActive) {
               System.out.println("Поток выполняет некую логику...");
           }
           System.out.println("Поток остановлен!");
       }
   }
 
   public void stopRunningThread() {
       isActive = false;
   }
}
При использовании метода stopRunning() внутренний флаг становится false и метод run прекращает свою работу. Давайте запустим его в main:

System.out.println("Начало выполнения программы");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// пока наш основной поток спит, вспомогательный  CustomThread работает и выводит в коноль своё сообщение
thread.stopRunningThread();
System.out.println("Конец выполнения программы");
В итоге в консоли мы увидим что-то вроде этого:
Начало выполнения программы Поток выполняет некую логику... Поток выполняет некую логику... Поток выполняет некую логику... Поток выполняет некую логику... Поток выполняет некую логику... Поток выполняет некую логику... Конец выполнения программы Поток остановлен!
А это значит, что наш поток отработал, вывел некоторое количество сообщений в консоль и был успешно остановлен. Отмечу, что количество выведенных сообщений от запуска к запуску будет разным, иногда дополнительный поток даже и не выводил ничего. Как я заметил, это зависит от времени сна основного потока, чем оно больше, тем меньше шанс что дополнительный поток ничего не выведет. При времени сна 1мс вывода сообщений почти никогда не происходит, но если выставить 20мс, почти всегда срабатывает. Возможно, при малом времени поток просто не успевает запуститься и начать свою работу, а его сразу же останавливают. Второй способ заключается в использовании на ein Objektе Thread метода interrupted(), который возвращает Bedeutung внутреннего флага прерывания (по умолчанию этот флаг false) и другого его метода interrupt() — ставящего этот флаг в true (когда этот флаг true поток должен остановить свою работу). Смотрим пример:

public class CustomThread extends Thread {
 
   @Override
   public void run() {
       {
           while (!Thread.interrupted()) {
               System.out.println("Поток выполняет некую логику...");
           }
           System.out.println("Поток остановлен!");
       }
   }
}
Запуск в main:

System.out.println("Начало выполнения программы");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Конец выполнения программы");
Итог выполнения будет таким же, Wie и в первом случае, но данный подход мне нравится больше: мы пишем меньше Codeа и больше используем уже готовый, стандатный функционал. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 12 - 7На этом сегодня и остановимся.Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 12 - 8
Другие материалы серии:
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION