← Назад к вопросам

Как понять какое исключение нужно обрабатывать

2.0 Middle🔥 121 комментариев
#Другое

Комментарии (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)

Вывод: выбирай исключение на основе ПРИЧИНЫ ошибки, не случайно. Специфичные исключения делают код понятнее, а обработку надежнее.

Как понять какое исключение нужно обрабатывать | PrepBro