Исключения: try и catch
Исключение (exception) — сигнал об ошибке во время работы программы: деление на ноль, строка вместо числа, нет файла. Если его не обработать, программа останавливается и печатает сообщение об ошибке. Обработать — значит поймать исключение в блоке catch и решить, что делать дальше.
public class Main {
public static void main(String[] args) {
String[] inputs = {"10", "abc", "0"};
for (String s : inputs) {
try {
int n = Integer.parseInt(s);
System.out.println("100 / " + n + " = " + (100 / n));
} catch (NumberFormatException e) {
System.out.println("Не число: " + s);
} catch (ArithmeticException e) {
System.out.println("Делить на ноль нельзя");
} finally {
System.out.println("---");
}
}
}
}100 / 10 = 10
---
Не число: abc
---
Делить на ноль нельзя
---try— блок, где может случиться ошибка. Как только она случилась, остаток блока пропускается.catch (Тип e)— что делать при исключении этого типа. Блоковcatchможет быть несколько, срабатывает первый подходящий. Один блок на несколько типов:catch (NumberFormatException | ArithmeticException e).finally— выполняется всегда: и после успешногоtry, и послеcatch. Там закрывают файлы и соединения.
Целочисленное деление на ноль бросает ArithmeticException, а у дробных чисел ошибки нет: 1.0 / 0 даёт Infinity.
Что внутри исключения
try {
Integer.parseInt("abc");
} catch (NumberFormatException e) {
System.out.println(e.getMessage());
}e.getMessage() возвращает текст ошибки — здесь For input string: "abc". e.printStackTrace() печатает полное сообщение со списком строк, через которые прошла ошибка, — удобно при отладке.
Проверяемые и непроверяемые
- Непроверяемые — наследники
RuntimeException:NullPointerException,ArrayIndexOutOfBoundsException,NumberFormatException,ArithmeticException. Обычно это ошибки в логике программы, перехватывать их не обязательно — лучше исправить причину. - Проверяемые (checked) — например,
IOExceptionпри работе с файлами. Компилятор требует либо поймать их, либо объявить в заголовке метода черезthrows. Иначе ошибка компиляции:unreported exception FileNotFoundException; must be caught or declared to be thrown.
static String firstLine(String path) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
return reader.readLine();
}
}throws IOException передаёт исключение тому, кто вызвал метод. Конструкция try (…) — try с ресурсами: открытое в скобках закроется само, даже при ошибке, и finally для этого не нужен. Нужны импорты java.io.*.
Своё исключение: throw
Метод может сам сообщить об ошибке словом throw:
static void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Возраст не может быть отрицательным: " + age);
}
}IllegalArgumentException — стандартное исключение для неверного аргумента. После throw метод сразу прекращается, а исключение летит к вызвавшему коду — пока его не поймает какой-нибудь catch.
Подводные камни
- Пустой
catch—catch (Exception e) { }прячет ошибку: программа работает дальше с неверными данными, а причину потом не найти. Хотя бы выведите сообщение. - Ловить
Exceptionцеликом — значит поймать и то, чего не ждали, напримерNullPointerExceptionот опечатки. Ловите конкретные типы. - Порядок
catch— сначала более узкие типы, потом общие. Еслиcatch (Exception e)стоит первым, следующие блоки до него не дойдут, и компилятор сообщит об ошибке. - Исключения не для обычного хода программы. Проверка
if (s.isEmpty())понятнее и быстрее, чем ожидание исключения.