Вопросы по собеседованию в Java Flow Control

Интервью по управлению потоком Java (+ ответы)

1. Вступление

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

В этой статье мы рассмотрим некоторые вопросы собеседования по управлению потоком, которые могут появиться во время собеседования и, при необходимости; мы будем использовать примеры, чтобы лучше понимать их ответы.

2. Вопросы

Q1. Опишите операторыif-then иif-then-else. Какие типы выражений можно использовать в качестве условий?

Оба оператора говорят нашей программе выполнять код внутри них, только если определенное условие оценивается какtrue. Однако операторif-then-else предоставляет дополнительный путь выполнения в случае, если выражение if оценивается какfalse:

if (age >= 21) {
    // ...
} else {
    // ...
}

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

Q2. Опишите выражениеswitch. Какие типы объектов можно использовать в предложенииswitch?

Переключатель позволяет выбирать несколько путей выполнения на основе значения переменных.

Каждый путь помеченcase илиdefault, операторswitch оценивает каждое выражениеcase на предмет соответствия и выполняет все операторы, следующие за меткой соответствия, до тех пор, пока не появитсяbreak заявление найдено. Если совпадение не найдено, вместо него будет выполнен блокdefault:

switch (yearsOfJavaExperience) {
    case 0:
        System.out.println("Student");
        break;
    case 1:
        System.out.println("Junior");
        break;
    case 2:
        System.out.println("Middle");
        break;
    default:
        System.out.println("Senior");
}

Мы можем использоватьbyte,short,char,int, их упакованные версии,enums иStrings в качестве значенийswitch.

Q3. Что произойдет, если мы забудем поместить операторbreak в предложениеcase вswitch?

Утверждениеswitch проваливается. Это означает, что он будет продолжать выполнение всех метокcase до тех пор, пока if не найдет операторbreak, даже если эти метки не соответствуют значению выражения.

Вот пример, чтобы продемонстрировать это:

int operation = 2;
int number = 10;

switch (operation) {
    case 1:
        number = number + 10;
        break;
    case 2:
        number = number - 4;
    case 3:
        number = number / 3;
    case 4:
        number = number * 10;
        break;
}

После запуска кодаnumber сохраняет значение 20 вместо 6. Это может быть полезно в ситуациях, когда мы хотим связать одно и то же действие с несколькими случаями.

Q4. Когда предпочтительнее использоватьswitch вместо выраженияif-then-else и наоборот?

Операторswitch лучше подходит при тестировании одной переменной с множеством отдельных значений или когда несколько значений будут выполнять один и тот же код:

switch (month) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        days = 31;
        break;
case 2:
    days = 28;
    break;
default:
    days = 30;
}

Операторif-then-else предпочтительнее, когда нам нужно проверить диапазоны значений или несколько условий:

if (aPassword == null || aPassword.isEmpty()) {
    // empty password
} else if (aPassword.length() < 8 || aPassword.equals("12345678")) {
    // weak password
} else {
    // good password
}

Q5. Какие типы циклов поддерживает Java?

Java предлагает три различных типа циклов:for,while иdo-while.

Циклfor позволяет перебирать диапазон значений. Это наиболее полезно, когда мы заранее знаем, сколько раз задача будет повторяться:

for (int i = 0; i < 10; i++) {
     // ...
}

Циклwhile может выполнять блок операторов, в то время как конкретное условиеtrue:

while (iterator.hasNext()) {
    // ...
}

do-while - это вариант оператораwhile, в котором оценка выраженияboolean находится в конце цикла. Это гарантирует, что код будет выполнен хотя бы один раз:

do {
    // ...
} while (choice != -1);

Q6. Что такое петляenhanced for?

Другой синтаксис оператораfor, предназначенный для перебора всех элементов коллекции, массива, перечисления или любого объекта, реализующего интерфейсIterable:

for (String aString : arrayOfStrings) {
    // ...
}

Q7. Как можно заранее выйти из цикла?

Используя операторbreak, мы можем немедленно завершить выполнение цикла:

for (int i = 0; ; i++) {
    if (i > 10) {
        break;
    }
}

Q8. В чем разница между операторомbreak без метки и оператором с меткой?

Операторbreak без метки завершает самый внутренний операторswitch,for,while илиdo-while, тогда как помеченныйbreak завершает выполнение внешнего заявление.

Давайте создадим пример, чтобы продемонстрировать это:

int[][] table = { { 1, 2, 3 }, { 25, 37, 49 }, { 55, 68, 93 } };
boolean found = false;
int loopCycles = 0;

outer: for (int[] rows : table) {
    for (int row : rows) {
        loopCycles++;
        if (row == 37) {
            found = true;
            break outer;
        }
    }
}

Когда найдено число 37, помеченный операторbreak завершает самый внешний циклfor, и больше циклы не выполняются. Таким образом,loopCycles заканчивается значением 5.

Однако непомеченныйbreak завершает только самый внутренний оператор, возвращая поток управления самому внешнемуfor, который продолжает цикл до следующегоrow в переменнойtable, делая loopCycles оканчивается значением 8.

Q9. В чем разница между операторомcontinue без метки и оператором с меткой?

Операторcontinue без метки пропускает до конца текущей итерации в самом внутреннем циклеfor,while илиdo-while, тогда как помеченныйcontinue переходит к внешний контур отмечен данной меткой.

Вот пример, демонстрирующий это:

int[][] table = { { 1, 15, 3 }, { 25, 15, 49 }, { 15, 68, 93 } };
int loopCycles = 0;

outer: for (int[] rows : table) {
    for (int row : rows) {
        loopCycles++;
        if (row == 15) {
            continue outer;
        }
    }
}

Аргументация та же, что и в предыдущем вопросе. Оператор с меткойcontinue завершает самый внешний циклfor.

Таким образом,loopCycles завершает сохранение значения 5, тогда как версия без метки завершает только самый внутренний оператор, в результате чегоloopCycles заканчивается значением 9.

Q10. Опишите поток выполнения внутри конструкцииtry-catch-finally.

Когда программа вошла в блокtry и в ней возникло исключение, выполнение блокаtry прерывается, и поток управления продолжается блокомcatch, который может обработать возникшее исключение.

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

Блокfinally выполняется всегда, независимо от того, было ли создано исключение внутри тела блокаtry.

Q11. В каких ситуациях блокfinally не может быть выполнен?

Когда JVM завершается во время выполнения блоковtry илиcatch, например, путем вызоваSystem.exit(), или когда выполняющийся поток прерывается или завершается, то блок finally не выполняется.

Q12. Каков результат выполнения следующего кода?

public static int assignment() {
    int number = 1;
    try {
        number = 3;
        if (true) {
            throw new Exception("Test Exception");
        }
        number = 2;
    } catch (Exception ex) {
        return number;
    } finally {
        number = 4;
    }
    return number;
}

System.out.println(assignment());

Код выводит число 3. Несмотря на то, что блокfinally выполняется всегда, это происходит только после выхода из блокаtry.

В этом примере операторreturn выполняется до завершения блокаtry-catch. Таким образом, присвоениеnumber в блокеfinally не имеет никакого эффекта, поскольку переменная уже возвращена в вызывающий код методаtestAssignment.

Q13. В каких ситуациях может использоваться блокtry-finally, даже если исключения не могут быть сгенерированы?

Этот блок полезен, когда мы хотим убедиться, что мы случайно не обойдем очистку ресурсов, используемых в коде, встретив операторbreak,continue илиreturn:

HeavyProcess heavyProcess = new HeavyProcess();
try {
    // ...
    return heavyProcess.heavyTask();
} finally {
    heavyProcess.doCleanUp();
}

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

public void doDangerousTask(Task task) throws ComplicatedException {
    try {
        // ...
        task.gatherResources();
        if (task.isComplicated()) {
            throw new ComplicatedException("Too difficult");
        }
        // ...
    } finally {
        task.freeResources();
    }
}

Q14. Как работаетtry-with-resources?

Операторtry-with-resources объявляет и инициализирует один или несколько ресурсов перед выполнением блокаtry и автоматически закрывает их в конце оператора независимо от того, завершился ли блок нормально или внезапно. В качестве ресурса можно использовать любой объект, реализующий интерфейсыAutoCloseable илиCloseable:

try (StringWriter writer = new StringWriter()) {
    writer.write("Hello world!");
}

3. Заключение

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

Удачи в вашем интервью.