Расставьте блоки catch, чтобы обработать и отсутствующий файл, и общий сбой ввода-вывода
readConfig открывает файл настроек и должен по-разному реагировать на два сбоя: при отсутствующем файле (FileNotFoundException) вернуть настройки по умолчанию, при любом другом сбое ввода-вывода (IOException) вернуть null.
Ограничения:
- оба блока
catchдолжны присутствовать, и класс должен компилироваться - не объединяйте их в один блок и не ловите
Exception Config.parseсам может броситьIOException
Config readConfig(String path) {
try (var in = new FileInputStream(path)) {
return Config.parse(in);
}
// ваш код здесь
}
Допишите реализацию.
Java просматривает блоки catch сверху вниз и входит в первый, чей тип совместим с брошенным исключением, поэтому подкласс должен идти первым: сначала catch (FileNotFoundException e) и только потом catch (IOException e). В обратном порядке широкий блок уже обрабатывает подкласс, узкий становится недостижимым, и компилятор его прямо отвергает.
- ✗Ставить
catch (Exception e)первым, а более узкий блок ниже него - ✗Считать, что блоки
catchподбираются по лучшему совпадению, как перегрузка - ✗Думать, что недостижимый
catch— предупреждение, а не ошибка компиляции
- →Что меняется, если типы исключений не связаны наследованием, а независимы?
- →Как multi-catch взаимодействует с этими правилами порядка?
Решение
Config readConfig(String path) {
try (var in = new FileInputStream(path)) {
return Config.parse(in);
} catch (FileNotFoundException e) {
return Config.defaults();
} catch (IOException e) {
return null;
}
}
Почему только такой порядок. FileNotFoundException — подкласс IOException. Блоки catch проверяются сверху вниз, и выполняется первый, чей тип совместим с брошенным объектом. Если поставить IOException первым, он перехватит и FileNotFoundException тоже — до второго блока управление не дойдёт никогда.
Это не просто мёртвый код, а ошибка компиляции:
error: exception FileNotFoundException has already been caught
} catch (FileNotFoundException e) {
^
Если типы не связаны наследованием (IOException и SQLException), порядок безразличен — ни один не поглощает другой, и компилятор принимает любой. Правило про «сначала конкретное» действует только вдоль одной ветви иерархии.
Практическое следствие: catch (Exception e) в середине списка обрывает все блоки ниже, поэтому широкий перехват если и нужен, то только последним.