finally
Вернёмся в здание. Кто бы ни уходил с этажа — спокойно после работы, по тревоге или раньше времени, — за ним закрывается дверь. Она закрывается всегда, как бы человек ни вышел.
В программе тоже бывает код, который должен выполниться при любом исходе: закрыть файл, разорвать соединение, напечатать отчёт. Если поставить его в конец try, исключение может его пропустить. Если поставить после catch, его пропустит исключение, которое этот catch не ловит. Для такого кода у try есть третий блок — finally:
public class Main {
public static void main(String[] args) {
int[] boxes = {2, 0};
for (int n : boxes) {
try {
System.out.println(8 / n);
} catch (ArithmeticException e) {
System.out.println("zero");
} finally {
System.out.println("door closed");
}
}
}
}Программа печатает:
4
door closed
zero
door closedТри пути из try
Блок finally пишут после всех catch. Он выполняется последним, как бы ни закончился try. Путей у try три.
Без исключения
try выполняется до конца, catch пропускается, затем выполняется finally. При n, равном 2, программа печатает 4, а потом door closed.
Исключение поймали
try прерывается на строке с исключением, выполняется подходящий catch, а за ним finally. При n, равном 0, печатается zero, а потом снова door closed.
Исключение никто не поймал
Подходящего catch нет — или catch нет вовсе: try можно написать с одним finally. Тогда finally всё равно выполняется, а исключение после него продолжает путь к вызвавшему методу.
finally и return
Дверь закрывается и тогда, когда метод уходит раньше времени через return:
public class Main {
static int share(int n) {
try {
return 10 / n;
} finally {
System.out.println("door closed");
}
}
public static void main(String[] args) {
System.out.println(share(2));
System.out.println(share(0));
}
}door closed
5
door closed
Exception in thread "main" java.lang.ArithmeticException: / by zeroДавайте разберём по шагам:
share(2)вычисляет10 / 2дляreturn. Но прежде чем метод вернёт5, выполняетсяfinallyи печатаетdoor closed.mainполучает5и печатает его.share(0): деление бросает исключение.catchнет, ноfinallyвсё равно печатаетdoor closed.- Исключение уходит из
shareвmain, там его тоже никто не ловит, и программа останавливается.
Это классический вопрос на собеседовании: выполнится ли finally, если в try стоит return? Да. Значение для return уже вычислено, затем выполняется finally, и только потом метод возвращает результат. Не выполнится finally, если остановится сама JVM: например, если выключить компьютер.
Здесь картинка с дверью неточна. В здании дверь закрывается, когда вы уже вышли. А finally выполняется, пока метод ещё не закончился: main ждёт результат 5, а share в это время печатает door closed.
Частая ошибка
Код, который должен выполниться всегда, поставили после try и catch, а не в finally:
String s = "0";
try {
System.out.println(10 / Integer.parseInt(s));
} catch (NumberFormatException e) {
System.out.println("not a number");
}
System.out.println("door closed"); // ✗Exception in thread "main" java.lang.ArithmeticException: / by zeroСтроку "0" прочитать можно, но на ноль делить нельзя. catch ловит только NumberFormatException, а ArithmeticException уходит дальше, и строка после try не выполняется: дверь осталась открытой. Перенесите её в блок finally после catch. Тогда door closed напечатается и здесь, перед тем как программа остановится.
Главное
finallyвыполняется послеtryиcatchпри любом исходе: без исключения, с пойманным и с непойманным.- Если в
tryстоитreturn,finallyвыполняется до того, как метод вернёт значение. - Непойманное исключение проходит через
finallyи идёт дальше, к вызвавшему методу. - Код, который должен выполниться всегда, пишут в
finally, а не послеtry.