Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Как выбрать и обрабатывать правильные исключения
Обработка исключений - один из самых важных аспектов надёжного кода. Неправильный выбор исключения приводит к сложной отладке и скрытым ошибкам.
1. Иерархия исключений в Java
Throwable
├── Exception
│ ├── Checked Exceptions (IOException, SQLException)
│ └── Runtime Exceptions (NPE, IAE, IllegalStateException)
└── Error (OutOfMemoryError - НЕ ловим обычно)
Checked vs Unchecked
// Checked - ОБЯЗАТЕЛЬНО обработать
File file = new File("data.txt");
try {
FileInputStream fis = new FileInputStream(file); // IOException
} catch (IOException e) {
// ОБЯЗАН обработать
}
// Unchecked - можно не обрабатывать
String[] arr = {"a", "b"};
System.out.println(arr[5]); // ArrayIndexOutOfBoundsException - можно не ловить
2. Определение какое исключение обрабатывать
Шаг 1: Определи возможные ошибки метода
public User findUserById(Long id) {
// Возможные ошибки:
// 1. id = null (NPE)
// 2. БД недоступна (SQLException, DataAccessException)
// 3. Пользователь не найден (может быть вообще не ошибка)
// 4. Сетевая ошибка при запросе к БД
}
Шаг 2: Выбери исключение на основе причины
// ❌ НЕ ДЕЛАЙ ТАК - ловишь все
public User findUserById(Long id) {
try {
// код
} catch (Exception e) { // Слишком общее!
return null;
}
}
// ✅ ДЕЛАЙ ТАК - ловишь специфичное
public Optional<User> findUserById(Long id) {
if (id == null) {
throw new IllegalArgumentException("ID не может быть null");
}
try {
return userRepository.findById(id);
} catch (DataAccessException e) {
// Специфичная обработка ошибки БД
logger.error("Ошибка доступа к БД для ID: " + id, e);
throw new UserServiceException("Не удалось найти пользователя", e);
}
}
3. Типичные сценарии и исключения
Валидация входных данных → IllegalArgumentException
public void createUser(String name, String email) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Имя не может быть пустым");
}
if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
throw new IllegalArgumentException("Email имеет неправильный формат");
}
// Создание пользователя
}
// Использование
try {
createUser("", "test");
} catch (IllegalArgumentException e) {
System.err.println("Ошибка валидации: " + e.getMessage());
// Вернуть 400 Bad Request в REST API
}
NullPointerException → проверь на null ДО
// ❌ НЕ ДЕЛАЙ ТАК
public void processUser(User user) {
try {
String email = user.getEmail();
} catch (NullPointerException e) {
logger.error("User is null");
}
}
// ✅ ДЕЛАЙ ТАК - проверь заранее
public void processUser(User user) {
if (user == null) {
throw new IllegalArgumentException("User cannot be null");
}
String email = user.getEmail();
// processEmail(email);
}
// ИЛИ используй Optional
public void processUser(Optional<User> user) {
user.map(User::getEmail)
.ifPresent(this::processEmail);
}
Состояние объекта → IllegalStateException
public class PaymentService {
private PaymentStatus status;
public void processPayment() {
if (status != PaymentStatus.PENDING) {
throw new IllegalStateException(
"Платеж можно обрабатывать только в статусе PENDING, текущий: " + status
);
}
// Обработка
}
}
// Использование
try {
paymentService.processPayment();
} catch (IllegalStateException e) {
System.err.println("Неправильное состояние: " + e.getMessage());
// Вернуть 409 Conflict
}
Операции с ресурсами → IOException
public String readFile(String path) throws IOException {
try (FileReader reader = new FileReader(path)) {
char[] buffer = new char[1024];
reader.read(buffer);
return new String(buffer);
}
// IOException выбросится автоматически если есть проблемы
}
// Использование
try {
String content = readFile("data.txt");
} catch (IOException e) {
logger.error("Не удалось прочитать файл", e);
// Обработка ошибки чтения
}
Ошибки БД → DataAccessException (Spring) или SQLException
@Repository
public class UserRepository {
@Autowired
private JdbcTemplate jdbc;
public User findById(Long id) {
try {
return jdbc.queryForObject(
"SELECT * FROM users WHERE id = ?",
new Object[]{id},
new UserRowMapper()
);
} catch (EmptyResultDataAccessException e) {
// Пользователь не найден
return null;
} catch (DataAccessException e) {
// Ошибка БД (подключение, синтаксис SQL и т.д.)
logger.error("Database error", e);
throw new UserServiceException("Failed to fetch user", e);
}
}
}
Сетевые ошибки → IOException или похожие
public String fetchDataFromAPI(String url) {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new IOException("API вернул ошибку: " + response.statusCode());
}
return response.body();
} catch (IOException e) {
logger.error("Network error while fetching from API", e);
throw new ExternalServiceException("Failed to fetch data from API", e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ExternalServiceException("Request was interrupted", e);
}
}
4. Иерархия пользовательских исключений
// Base exception для всего приложения
public class ApplicationException extends RuntimeException {
public ApplicationException(String message) {
super(message);
}
public ApplicationException(String message, Throwable cause) {
super(message, cause);
}
}
// Бизнес-исключения
public class UserNotFoundException extends ApplicationException {
public UserNotFoundException(Long userId) {
super("User not found: " + userId);
}
}
public class InvalidUserDataException extends ApplicationException {
public InvalidUserDataException(String message) {
super(message);
}
}
public class PaymentException extends ApplicationException {
public PaymentException(String message, Throwable cause) {
super(message, cause);
}
}
// Использование
public void createUser(String name, String email) {
if (name == null) {
throw new InvalidUserDataException("Name cannot be null");
}
// ...
}
5. Best practices при обработке
✅ Логируй оригинальное исключение (chain)
try {
processPayment(order);
} catch (SQLException e) {
// ✅ ПРАВИЛЬНО - сохраняем context
throw new PaymentException("Failed to process payment for order: " + order.getId(), e);
}
// ❌ НЕПРАВИЛЬНО
catch (SQLException e) {
throw new PaymentException("Payment failed");
}
✅ Не ловь слишком общие исключения
// ❌ Слишком общее
try {
// код
} catch (Exception e) {
logger.error("Some error");
}
// ✅ Специфичное
try {
// код
} catch (IOException e) {
// обработка ошибки файла
} catch (SQLException e) {
// обработка ошибки БД
}
✅ Provide context в сообщении
// ❌ Бесполезно
throw new UserNotFoundException("User not found");
// ✅ Полезно
throw new UserNotFoundException(
"User not found by ID: " + userId + ", department: " + dept
);
✅ Не игнорируй исключения
// ❌ НИКОГДА
try {
processUser(user);
} catch (Exception e) {
// Молча игнорируем
}
// ✅ Всегда логируй
try {
processUser(user);
} catch (Exception e) {
logger.error("Error processing user: " + user.getId(), e);
throw new ApplicationException("User processing failed", e);
}
6. Стратегия выбора исключения
1. Является ли это ошибкой программиста?
→ IllegalArgumentException / IllegalStateException
2. Это ошибка во время выполнения (не предсказуемая)?
→ IOException, SQLException, или свой Checked Exception
3. Это проблема с ресурсами/внешней системой?
→ IOException, DataAccessException, ExternalServiceException
4. Это бизнес-ошибка (валидация, бизнес-правила)?
→ Свое исключение (InvalidUserException, InsufficientBalanceException)
5. Нужен ли мне контроль компилятора над обработкой?
→ Checked Exception (если очень важно чтобы обработали)
→ Unchecked Exception (если достаточно logging)
Вывод: выбирай исключение на основе ПРИЧИНЫ ошибки, не случайно. Специфичные исключения делают код понятнее, а обработку надежнее.