Проверяемые и непроверяемые исключения в Java: в чем разница и как их использовать?
Ключевое отличие: контроль компилятора
Главное различие между checked и unchecked исключениями в Java — это то, как с ними работает компилятор. Checked исключения (наследники Exception, но не RuntimeException) компилятор заставляет вас обрабатывать — либо через try-catch, либо объявляя throws. Unchecked исключения (наследники RuntimeException и Error) компилятор не проверяет — вы можете их игнорировать, хотя программа может упасть во время выполнения.
Философия дизайна
Checked исключения созданы для восстановимых ошибок, которые возникают не по вине программы, а из-за внешних факторов: файл не найден, сеть недоступна, база данных отключена. Разработчик может и должен что-то сделать — показать сообщение пользователю, повторить операцию, использовать резервный сервер.
Unchecked исключения — это ошибки программирования: деление на ноль, выход за границы массива, вызов метода на null. Их причина — баг в коде. Обрабатывать их на каждый чих бессмысленно, нужно исправлять код. Или использовать их для ошибок, которые невозможно предвидеть (например, неверные аргументы, переданные извне).
Практические правила
- Checked (проверяемые):
IOException,SQLException,FileNotFoundException. Используйте, когда вызывающий код может разумно восстановиться. - Unchecked (непроверяемые):
NullPointerException,ArithmeticException,IllegalArgumentException. Используйте для ошибок, которые указывают на баг или нарушение контракта.
Примеры кода
Checked исключение — обрабатываем обязательно
public void readFile(String path) {
try {
FileReader file = new FileReader(path);
BufferedReader reader = new BufferedReader(file);
String line = reader.readLine();
System.out.println(line);
} catch (FileNotFoundException e) {
System.err.println("Файл не найден: " + path);
// Можно создать файл по умолчанию или запросить другой путь
} catch (IOException e) {
System.err.println("Ошибка чтения файла: " + e.getMessage());
}
}
Или можно "прокинуть" исключение выше:
public void readFile(String path) throws IOException {
FileReader file = new FileReader(path);
BufferedReader reader = new BufferedReader(file);
String line = reader.readLine();
System.out.println(line);
}
Unchecked исключение — компилятор молчит, но код может упасть
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Деление на ноль недопустимо");
}
return a / b;
}
Компилятор не заставляет обрабатывать IllegalArgumentException — это ваша ответственность проверять аргументы перед вызовом.
Как создавать свои исключения
- Наследуйте от
Exception, если ошибка может быть исправлена вызывающим кодом. Например,AccountNotFoundException— можно попросить ввести другой номер счёта. - Наследуйте от
RuntimeException, если ошибка — следствие бага или нарушения контракта. Например,InvalidEmailFormatException— вызывающий код должен был проверить формат до вызова.
Золотое правило (из Effective Java, Джошуа Блох)
Используйте checked исключения для восстановимых условий и unchecked — для ошибок программирования. Не злоупотребляйте checked исключениями — если вызывающий код не может ничего сделать с ошибкой, сделайте её unchecked. Иначе вы заставите каждого клиента вашего метода писать пустые try-catch, что ухудшает читаемость.
Дополнение: когда checked исключения — это зло
На практике многие Java-разработчики (включая авторов популярных фреймворков) считают, что checked исключения в современной разработке приносят больше проблем, чем пользы. Например, в Java 8+ с лямбдами и Stream API checked исключения очень неудобны — вы не можете просто выбросить IOException внутри лямбды, не оборачивая его в unchecked.
Поэтому часто используют такой подход: checked исключения перехватывают на нижнем уровне и оборачивают в unchecked (например, в RuntimeException), а обрабатывают уже в верхнем слое (контроллер, UI). Это упрощает код и делает его более гибким.
Пример:
public String readFileContent(String path) {
try {
return Files.readString(Path.of(path));
} catch (IOException e) {
throw new RuntimeException("Ошибка чтения файла: " + path, e);
}
}
Такой подход называется "unchecked wrapping" и используется в Spring, Hibernate и многих других библиотеках. Главное — не терять оригинальное исключение (передавайте его в конструктор как cause).
В итоге: если вы пишете библиотеку для широкого круга пользователей — используйте checked исключения для важных восстановимых ошибок. Если пишете приложение — чаще используйте unchecked, чтобы не загромождать код.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги