1. Множественный catch: обработка разных исключений
В реальных программах один и тот же блок кода может выбрасывать разные типы исключений. Например, при чтении файла может возникнуть как FileNotFoundException, так и IOException, а при парсинге данных — ещё и NumberFormatException. Чтобы корректно обработать каждую ситуацию, в Java можно использовать несколько блоков catch подряд.
Синтаксис
try {
// опасные операции
} catch (FileNotFoundException e) {
System.out.println("Файл не найден: " + e.getMessage());
} catch (IOException e) {
System.out.println("Ошибка ввода-вывода: " + e.getMessage());
} catch (NumberFormatException e) {
System.out.println("Некорректный формат числа: " + e.getMessage());
}
Важный момент:
Блоки catch должны идти от более специфичных к более общим! Если сначала написать catch (Exception e), то все остальные блоки станут недостижимыми, и компилятор выдаст ошибку.
Почему это важно?
- Специфичная обработка: Можно по-разному реагировать на разные ошибки (например, предложить выбрать другой файл или повторить ввод).
- Читаемость: Код становится понятнее — видно, какие ошибки ожидаются и как они обрабатываются.
2. catch с несколькими типами (multi-catch)
Иногда для разных исключений нужна одинаковая обработка. Например, вы хотите просто вывести сообщение об ошибке или залогировать её, независимо от того, была это ошибка чтения файла или преобразования числа.
С Java 7 появился синтаксис multi-catch, который позволяет объединить несколько типов исключений в одном блоке:
try {
// опасная операция
} catch (IOException | NumberFormatException e) {
System.out.println("Ошибка при обработке файла или числа: " + e.getMessage());
}
Как это работает:
- Через вертикальную черту (|) указываются типы исключений.
- Переменная e будет иметь тип самого общего базового класса (обычно Exception).
- Внутри блока нельзя присваивать новое значение переменной e — она автоматически считается final.
Пример:
try {
BufferedReader reader = new BufferedReader(new FileReader("numbers.txt"));
String line = reader.readLine();
int number = Integer.parseInt(line);
System.out.println("Число: " + number);
reader.close();
} catch (IOException | NumberFormatException e) {
System.out.println("Ошибка: " + e.getMessage());
}
3. Лучшие практики обработки исключений
Не подавляйте исключения
Плохо:
try {
// что-то опасное
} catch (Exception e) {
// ничего не делаем!
}
Почему это плохо?
Пустой блок catch "глушит" ошибку — вы теряете информацию о причине сбоя, и программа становится неотлаживаемой.
Лучше: хотя бы выведите сообщение или залогируйте ошибку.
catch (Exception e) {
e.printStackTrace();
}
Если вы не можете обработать исключение — пробросьте его дальше:
catch (IOException e) {
throw e; // или throw new RuntimeException(e);
}
Бросайте наиболее специфичные исключения
Если вы пишете свои методы или API, всегда бросайте максимально конкретные исключения, а не просто Exception или RuntimeException. Это делает ваш код и интерфейс понятнее для других разработчиков.
Пример:
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Недостаточно средств");
}
// ...
}
Не используйте исключения для управления потоком программы
Исключения предназначены для обработки исключительных ситуаций — то есть ошибок, которые не должны происходить в нормальном ходе работы программы. Не используйте их для обычной логики (например, для выхода из цикла или проверки условий).
Плохо:
try {
while (true) {
String line = reader.readLine();
if (line == null) break;
// обработка строки
}
} catch (Exception e) {
// используем исключение для выхода из цикла — плохо!
}
Хорошо:
String line;
while ((line = reader.readLine()) != null) {
// обработка строки
}
4. Практические примеры
Пример: обработка разных ошибок по-разному
try {
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
int number = Integer.parseInt(line);
System.out.println("Число: " + number);
reader.close();
} catch (FileNotFoundException e) {
System.out.println("Файл не найден!");
} catch (IOException e) {
System.out.println("Ошибка чтения файла!");
} catch (NumberFormatException e) {
System.out.println("В файле не число!");
}
Пример: multi-catch для одинаковой обработки
try {
// что-то опасное
} catch (IOException | NumberFormatException e) {
System.out.println("Ошибка: " + e.getMessage());
}
5. Важные нюансы и типичные ошибки
Ошибка №1: Неправильный порядок блоков catch. Сначала должны идти более специфичные исключения, а потом — общие. Иначе до широкого блока выполнение никогда не дойдёт.
Ошибка №2: Использование родственных типов в multi-catch. Например, catch (IOException | Exception e) не скомпилируется, потому что IOException наследуется от Exception.
Ошибка №3: Попытка изменить переменную e в multi-catch. В этом случае e считается final — присвоить ей новое значение нельзя.
Ошибка №4: Пустые catch-блоки. Игнорирование ошибок приводит к "тихим" багам. Минимум — выводите сообщение или логируйте исключение.
Ошибка №5: Ловля всех Exception подряд. Использование catch (Exception e) скрывает реальные проблемы и усложняет отладку. Лучше ловить только ожидаемые исключения.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ