Какие исключения нужно обрабатывать обязательно?
Компилятор Java заставляет обрабатывать checked исключения. Это все наследники Exception, кроме RuntimeException.
Junior Level
Checked исключения — обязательная обработка
Компилятор Java заставляет обрабатывать checked исключения. Это все наследники Exception, кроме RuntimeException.
Checked exceptions – это контракт между методом и его вызывающим. Метод говорит: «Я могу столкнуться с X, и ты должен быть к этому готов.»
// Обязательно: либо try-catch, либо throws
public void readFile() throws IOException {
Files.readAllLines(Paths.get("file.txt"));
}
// Либо обработка на месте
public void safeRead() {
try {
Files.readAllLines(Paths.get("file.txt"));
} catch (IOException e) {
log.error("Failed to read file", e);
}
}
Распространённые исключения для обработки
IOException— работа с файлами, сетьюSQLException— работа с базой данныхClassNotFoundException— загрузка классовInterruptedException— многопоточность
Unchecked исключения — необязательная обработка
NullPointerException, IllegalArgumentException и другие RuntimeException можно не обрабатывать. Они сигнализируют об ошибках в коде.
Базовое правило
- Checked — внешние сбоки (файлы, сеть, БД) → обязаны обработать
- Unchecked — ошибки программирования → должны исправить код
Middle Level
Обязательная vs рекомендуемая обработка
| Тип | Обязанность | Кто требует |
|---|---|---|
| Checked | Обязан обработать или объявить throws | Компилятор |
| Unchecked | Рекомендуется обработать | Архитектура/команда |
| Error | Не должен перехватывать | JVM (восстановление невозможно) |
Компиляторная проверка
На уровне JVM разделения на checked и unchecked не существует. Инструкция athrow просто выбрасывает объект. Обязательность обработки — это только проверка javac.
Атрибут Exceptions в class-файле
При объявлении void method() throws IOException в .class файле появляется атрибут Exceptions. Компилятор использует его для проверки. Другие языки (Kotlin, Groovy) игнорируют этот атрибут.
Sneaky Throws — обход проверки
Можно выбросить checked исключение без throws:
@SuppressWarnings("unchecked")
public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
throw (E) e; // Стирание типов обманывает компилятор
}
// Lombok делает это автоматически
@SneakyThrows
public void readFile() {
// IOException без throws!
Files.readAllLines(Paths.get("file.txt"));
}
Exception Translation
Правильный подход — перехватывать checked исключения на границе слоя и переводить в доменные unchecked:
public interface Storage {
byte[] read(String path); // Не зависит от SQL
}
public class SqlStorage implements Storage {
public byte[] read(String path) {
try {
return jdbcTemplate.queryForObject(...);
} catch (SQLException e) {
throw new StorageException("Failed to read: " + path, e);
}
}
}
Senior Level
Функциональное программирование и checked exceptions
Стандартные интерфейсы (Function, Predicate, Supplier) не поддерживают checked исключения:
// Не скомпилируется
stream.map(path -> Files.readString(path))
// Решение 1 — обёртка
stream.map(path -> {
try { return Files.readString(path); }
catch (IOException e) { throw new UncheckedIOException(e); }
})
// Решение 2 — Sneaky Throws
stream.map(path -> sneakyRead(path))
Производительность
Никогда не используйте checked исключения для управления логикой. Бросание исключения — создание объекта + обход стека — в 100-1000 раз медленнее возврата Optional.
Валидация входных данных
Для невалидных данных всегда используйте unchecked исключения (IllegalArgumentException). Это ошибка программиста, а не внешнего мира.
InterruptedException – исключение, подтверждающее правило. Это не «control flow» в обычном смысле, а механизм кооперативной отмены потоков. Его обработка – часть контракта многопоточности.
UncaughtExceptionHandler
Если checked исключение “просочилось” через sneaky throws и не было поймано, оно дойдёт до обработчика потока. Установите глобальный обработчик:
Thread.setDefaultUncaughtExceptionHandler((thread, e) -> {
log.error("Uncaught exception in thread {}", thread.getName(), e);
});
Empty Catch Blocks — худшее преступление
catch (IOException e) {} // Преступление!
Если обязаны обработать, но не знаете как — логируйте:
catch (IOException e) {
log.error("IO error occurred", e);
throw new RuntimeException(e);
}
Диагностика
- Static Analysis — SonarQube блокирует пустые catch-блоки
- Micrometer — отслеживайте количество исключений через метрики
- Log Correlation — сохраняйте Trace ID для всех типов ошибок
🎯 Шпаргалка для интервью
Обязательно знать:
- Checked исключения — обязан обработать или объявить
throws(компилятор требует) - Unchecked — рекомендуется обработать на уровне архитектуры (не компилятор)
- Error — НЕ должен перехватываться (JVM в нестабильном состоянии)
- На уровне JVM разделения нет — инструкция
athrowвыбрасывает всё одинаково - Sneaky throws — обход проверки компилятора через стирание типов
- Exception translation — перехват checked на границе слоя → перевод в доменное unchecked
- Для невалидных входных данных всегда использовать unchecked (
IllegalArgumentException) - Пустые catch-блоки — худшее преступление: минимум логировать
Частые уточняющие вопросы:
- Как JVM обрабатывает checked vs unchecked? — Одинаково; разделение только в
javac - Что такое sneaky throws? — Обход проверки через
@SneakyThrows(Lombok) или дженерик-стирание - Почему InterruptedException — особое? — Это механизм кооперативной отмены потоков, нужно восстановить статус прерывания
- Что делать, если не знаешь как обработать? — Логировать и пробросить как
RuntimeException
Красные флаги (НЕ говорить):
- “Я оставляю пустой catch-блок, если не знаю что делать” — хотя бы логируйте
- “Я ловлю Error, чтобы приложение не падало” — JVM нестабильна, это бессмысленно
- “Checked исключения — это то же самое, что unchecked” — нет, компилятор проверяет только checked
Связанные темы:
- [[Что такое checked exception и когда его использовать]]
- [[Что такое unchecked exception (Runtime Exception)]]
- [[В чём разница между Error и Exception]]
- [[Почему не стоит глотать исключения (catch empty)]]
- [[Что такое suppressed exceptions]]