Кастомные исключения

Тема дорожной карты · Java

Пользовательские исключения в Java позволяют разработчикам моделировать специфические для предметной области условия ошибок со значимыми именами и структурированными данными, делая код обработки ошибок более выразительным и удобным для тестирования с JUnit 5. Пользовательское проверяемое исключение создаётся расширением Exception (или проверяемого подкласса вроде IOException), а пользовательское непроверяемое исключение — расширением RuntimeException; выбор следует той же логике, что и для проверяемых/непроверяемых исключений: используйте проверяемые, когда вызывающие реально могут восстановиться, непроверяемые — когда ошибка является программной или невосстановимым состоянием. Лучшая практика — предоставить как минимум два конструктора: один принимающий String message и один принимающий String message плюс Throwable cause, обеспечивая правильное цепочное исключение, чтобы getCause() сохранял исходный стек вызовов при оборачивании низкоуровневых исключений вроде SQLException или IOException. Пользовательские исключения в Java органично интегрируются с механизмами @ExceptionHandler и @ControllerAdvice Spring — ResourceNotFoundException extends RuntimeException, аннотированный @ResponseStatus(HttpStatus.NOT_FOUND), заставит Spring MVC вернуть 404 без какой-либо дополнительной конфигурации. Снабжение пользовательских исключений строго типизированными полями — например, OrderNotFoundException(long orderId), хранящий orderId как поле, — позволяет нижестоящим обработчикам ошибок журналировать структурированные данные и формировать диагностические сообщения, которые можно использовать для действий, вместо разбора произвольных строк.

Как это работает

Кастомные исключения делится на checked (нужно объявить или обработать — IOException, SQLException) и unchecked (RuntimeException — NullPointerException, IllegalArgumentException). try { } catch (...) { } finally { } или try-with-resources (try (var x = ...) { }) для AutoCloseable-типов — рекомендуется для файлов/сокетов/соединений. Multi-catch (catch (IOException | SQLException e)) сокращает дублирование. Всегда сохраняйте cause (throw new MyException(msg, cause)).

Когда применять

Checked exceptions — для восстановимых условий в API библиотек (вызывающий решает); unchecked (RuntimeException) — для программных ошибок. Оборачивайте checked exceptions на границах модулей, чтобы stack trace upstream оставался чистым. try-with-resources — для всего AutoCloseable. Логируйте с throwable (log.error("...", e)) — slf4j отформатирует со stack trace.

Типичные ошибки

Ловушки Кастомные исключения: catch (Exception e) { } тихо съедает каждую ошибку (тихий убийца); checked exceptions для того, что должно быть runtime-ошибкой (многословные declarations повсюду); rewrap exception теряет cause (throw new MyException(msg) без e — stack trace пропал); finally-блок, бросающий exception (оригинальное теряется).

Связанные понятия

Полезные ресурсы