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

Какие знаешь способы обработки исключения?

1.0 Junior🔥 281 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы обработки исключений

Exception Handling — критическая часть надежного кода в Java. Существует несколько стратегий обработки исключений, каждая из которых применяется в зависимости от контекста и требований приложения.

1. Try-Catch блоки (Classic approach)

Базовая структура

try {
    // Код, который может выбросить исключение
    int result = 10 / Integer.parseInt("abc");
    System.out.println("Result: " + result);
} catch (NumberFormatException e) {
    // Обработка конкретного исключения
    System.err.println("Invalid number format: " + e.getMessage());
} catch (ArithmeticException e) {
    // Обработка другого типа исключения
    System.err.println("Division by zero: " + e.getMessage());
} catch (Exception e) {
    // Обработка всех остальных исключений
    System.err.println("Unexpected error: " + e.getMessage());
}

Множественные catch блоки (Multi-catch)

try {
    // Код, который может выбросить исключение
    parseAndProcessData("data.json");
} catch (FileNotFoundException | JsonParseException e) {
    // Обработка нескольких типов исключений одинаково
    logger.error("Failed to process file: " + e.getMessage());
    showErrorToUser("Unable to load data");
} catch (IOException e) {
    logger.error("I/O error: " + e.getMessage());
}

Иерархия обработки (от специфичных к общим)

try {
    database.connect();
    database.query("SELECT * FROM users");
} catch (SQLSyntaxErrorException e) {
    // Специфичное исключение для SQL синтаксиса
    logger.error("SQL syntax error", e);
    notifyDeveloper(e);
} catch (SQLException e) {
    // Более общее исключение для БД
    logger.error("Database error", e);
} catch (Exception e) {
    // Самое общее исключение
    logger.error("Unexpected error", e);
}

2. Try-Catch-Finally блоки

Finally гарантирует выполнение очистки ресурсов

Connection connection = null;
try {
    connection = getConnection();
    connection.setAutoCommit(false);
    
    // Выполнение операций
    updateUser(connection, user);
    connection.commit();
    
    System.out.println("User updated successfully");
} catch (SQLException e) {
    logger.error("Database error", e);
    if (connection != null) {
        try {
            connection.rollback();
        } catch (SQLException rollbackEx) {
            logger.error("Rollback failed", rollbackEx);
        }
    }
    throw new DataAccessException("Failed to update user", e);
} finally {
    // Выполняется ВСЕГДА, даже при исключении или return
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            logger.error("Failed to close connection", e);
        }
    }
}

3. Try-with-resources (Автоматическое управление ресурсами)

Рекомендуемый способ (Java 7+)

// Автоматическое закрытие ресурса
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    logger.error("Failed to read file", e);
}
// reader.close() вызовется автоматически

// Несколько ресурсов
try (
    Connection connection = getConnection();
    PreparedStatement statement = connection.prepareStatement("SELECT * FROM users");
    ResultSet resultSet = statement.executeQuery()
) {
    while (resultSet.next()) {
        System.out.println(resultSet.getString("name"));
    }
} catch (SQLException e) {
    logger.error("Database error", e);
}
// Все три ресурса закроются автоматически

// Custom AutoCloseable ресурс
public class DatabaseConnection implements AutoCloseable {
    @Override
    public void close() throws Exception {
        // Очистка ресурсов
        System.out.println("Connection closed");
    }
}

// Использование
try (DatabaseConnection db = new DatabaseConnection()) {
    db.execute("SELECT * FROM users");
} catch (Exception e) {
    logger.error("Error", e);
}

4. Выброс исключений (Throwing Exceptions)

Пробрасывание исключения вверх

public void validateUser(User user) throws IllegalArgumentException {
    if (user == null) {
        throw new IllegalArgumentException("User cannot be null");
    }
    if (user.getName() == null || user.getName().isEmpty()) {
        throw new IllegalArgumentException("User name cannot be empty");
    }
}

public User getUserById(Long id) throws UserNotFoundException {
    return userRepository.findById(id)
        .orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
}

// Использование
try {
    User user = getUserById(999L);
} catch (UserNotFoundException e) {
    logger.warn("User not found", e);
    return ResponseEntity.notFound().build();
}

Пробрасывание с цепочкой (Exception Chaining)

public void processData(String filePath) throws DataProcessingException {
    try {
        readFile(filePath);
        parseData();
        saveToDatabase();
    } catch (IOException e) {
        // Оборачиваем нижнеуровневое исключение в свое
        throw new DataProcessingException("Failed to process data", e);
    } catch (SQLException e) {
        throw new DataProcessingException("Failed to save data to database", e);
    }
}

// Использование
try {
    processData("data.csv");
} catch (DataProcessingException e) {
    logger.error("Data processing failed: " + e.getMessage());
    logger.debug("Root cause: ", e.getCause());  // Исходное исключение
}

5. Custom исключения

// Checked исключение (наследует Exception)
public class UserNotFoundException extends Exception {
    public UserNotFoundException(String message) {
        super(message);
    }
    
    public UserNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

// Unchecked исключение (наследует RuntimeException) — РЕКОМЕНДУЕТСЯ
public class InsufficientFundsException extends RuntimeException {
    private final BigDecimal required;
    private final BigDecimal available;
    
    public InsufficientFundsException(String message, BigDecimal required, BigDecimal available) {
        super(message);
        this.required = required;
        this.available = available;
    }
    
    public BigDecimal getRequired() { return required; }
    public BigDecimal getAvailable() { return available; }
}

// Использование
public void transfer(Account from, Account to, BigDecimal amount) {
    if (from.getBalance().compareTo(amount) < 0) {
        throw new InsufficientFundsException(
            "Insufficient funds for transfer",
            amount,
            from.getBalance()
        );
    }
    from.debit(amount);
    to.credit(amount);
}

// Обработка
try {
    transfer(fromAccount, toAccount, amount);
} catch (InsufficientFundsException e) {
    logger.warn("Transfer failed: " + e.getMessage());
    response.put("required", e.getRequired());
    response.put("available", e.getAvailable());
}

6. Обработка в Spring (Global Exception Handler)

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(
            "USER_NOT_FOUND",
            ex.getMessage(),
            System.currentTimeMillis()
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
    
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException ex) {
        ErrorResponse error = new ErrorResponse(
            "INVALID_INPUT",
            ex.getMessage(),
            System.currentTimeMillis()
        );
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
        logger.error("Unexpected error", ex);
        ErrorResponse error = new ErrorResponse(
            "INTERNAL_ERROR",
            "An unexpected error occurred",
            System.currentTimeMillis()
        );
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}

// Контроллер
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
        User user = userService.getUserById(id);  // Может выбросить UserNotFoundException
        return ResponseEntity.ok(new UserDto(user));
    }
}

7. Optional для избежания исключений

// Вместо выброса исключения
public User getUserById(Long id) throws UserNotFoundException {
    return userRepository.findById(id)
        .orElseThrow(() -> new UserNotFoundException("User not found"));
}

// Можно вернуть Optional
public Optional<User> findUserById(Long id) {
    return userRepository.findById(id);
}

// Использование
findUserById(1L)
    .ifPresent(user -> System.out.println(user.getName()))
    .ifPresentOrElse(
        user -> processUser(user),
        () -> logger.warn("User not found")
    );

8. Логирование и отладка

public void processOrder(Order order) {
    try {
        validateOrder(order);
        saveOrder(order);
        notifyCustomer(order);
    } catch (OrderValidationException e) {
        logger.error("Order validation failed for order {}: {}", order.getId(), e.getMessage(), e);
        // Отправить сообщение об ошибке пользователю
        throw e;
    } catch (Exception e) {
        logger.error("Unexpected error processing order {}", order.getId(), e);
        // Оборачиваем в свое исключение
        throw new OrderProcessingException("Failed to process order", e);
    }
}

9. Функциональный подход (Java 8+)

// Функция, которая может выбросить исключение
@FunctionalInterface
public interface CheckedFunction<T, R> {
    R apply(T t) throws Exception;
}

// Обработка
public static <T, R> Function<T, R> unchecked(CheckedFunction<T, R> f) {
    return t -> {
        try {
            return f.apply(t);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

// Использование в Stream
List<Integer> numbers = Arrays.asList("1", "2", "abc", "4");
numbers.stream()
    .map(unchecked(Integer::parseInt))
    .forEach(System.out::println);

Best Practices

  1. Используй checked исключения редко — unchecked (RuntimeException) проще
  2. Логируй исключения — важно для отладки
  3. Не игнорируй исключения — пустые catch блоки опасны
  4. Используй try-with-resources — для управления ресурсами
  5. Выбрасывай специфичные исключения — помогает обработке
  6. Оборачивай исключения цепочкой — сохраняй контекст ошибки
  7. Используй Global Exception Handler в Spring — централизованная обработка
  8. Предпочитай Optional — где можно, вместо выброса исключений

Правильная обработка исключений — основа надежного и поддерживаемого Java приложения.