← Назад к вопросам
Какие знаешь способы обработки непроверяемых исключений?
1.8 Middle🔥 191 комментариев
#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы обработки непроверяемых исключений (Unchecked Exceptions)
Непроверяемые исключения (Runtime Exceptions) — это исключения, которые наследуются от RuntimeException и не требуют явной обработки или объявления в сигнатуре метода. Однако правильная их обработка критична для надёжности приложения.
Типы исключений в Java
// Throwable
// ├── Error (не обрабатываем: OutOfMemoryError, StackOverflowError)
// └── Exception
// ├── Checked (CheckedException) - НУЖНО обрабатывать
// │ ├── IOException
// │ ├── SQLException
// │ └── ...
// └── Unchecked (RuntimeException) - обрабатываем опционально
// ├── NullPointerException
// ├── ArrayIndexOutOfBoundsException
// ├── IllegalArgumentException
// └── ...
1. Try-Catch блоки
Базовая обработка:
public double divide(double a, double b) {
try {
return a / b; // Может быть Infinity при b=0.0
} catch (ArithmeticException e) {
System.out.println("Ошибка деления: " + e.getMessage());
return 0; // Значение по умолчанию
}
}
// Специфичная обработка разных типов
public void process(String data) {
try {
int value = Integer.parseInt(data);
System.out.println("Значение: " + value);
} catch (NumberFormatException e) {
System.out.println("Ошибка парсинга числа: " + data);
} catch (NullPointerException e) {
System.out.println("Данные null");
}
}
Multi-catch в Java 7+:
public void readFile(String filename) {
try {
File file = new File(filename);
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();
} catch (FileNotFoundException | IOException e) {
System.out.println("Ошибка файла: " + e.getMessage());
}
}
2. Try-Catch-Finally
Finally для очистки ресурсов:
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
System.out.println("Ошибка ввода: " + e.getMessage());
} finally {
// Выполнится ВСЕГДА, даже при исключении
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. Try-with-resources (Java 7+) - ЛУЧШИЙ СПОСОБ
Автоматическое управление ресурсами:
// Лучше всего для управления ресурсами
public void readFile(String filename) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} // reader.close() вызовется автоматически
}
// С несколькими ресурсами
public void copyFile(String source, String destination) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(source));
BufferedWriter writer = new BufferedWriter(new FileWriter(destination))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} // Оба ресурса закроются автоматически
}
// Работает с любым AutoCloseable
public void withDatabase() {
try (Connection conn = DriverManager.getConnection(url)) {
// работа с БД
} catch (SQLException e) {
e.printStackTrace();
}
}
4. Кастомные исключения
Создание своих исключений:
// Unchecked (наследуем RuntimeException)
public class InsufficientFundsException extends RuntimeException {
private final double required;
private final double available;
public InsufficientFundsException(String message, double required, double available) {
super(message);
this.required = required;
this.available = available;
}
public double getRequired() { return required; }
public double getAvailable() { return available; }
}
// Использование
public void withdraw(double amount) {
if (amount > balance) {
throw new InsufficientFundsException(
"Недостаточно средств",
amount,
balance
);
}
balance -= amount;
}
// Обработка
try {
account.withdraw(1000);
} catch (InsufficientFundsException e) {
System.out.println("Требуется: " + e.getRequired());
System.out.println("Доступно: " + e.getAvailable());
}
5. Логирование исключений
SLF4J + Logback:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User getUserById(Long id) {
try {
if (id == null || id < 0) {
throw new IllegalArgumentException("ID не может быть null или отрицательным");
}
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("Пользователь не найден: " + id));
} catch (IllegalArgumentException e) {
logger.warn("Некорректный ID: {}", id, e);
throw e;
} catch (UserNotFoundException e) {
logger.info("Пользователь не найден: {}", id);
throw e;
} catch (Exception e) {
logger.error("Неожиданная ошибка при получении пользователя", e);
throw new RuntimeException("Ошибка базы данных", e);
}
}
}
6. Обработка на уровне приложения
Global Exception Handler в Spring:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handleNullPointer(NullPointerException e) {
return ResponseEntity.badRequest().body(
new ErrorResponse("Null указатель", e.getMessage())
);
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
return ResponseEntity.badRequest().body(
new ErrorResponse("Некорректный аргумент", e.getMessage())
);
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<ErrorResponse> handleGeneric(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
new ErrorResponse("Внутренняя ошибка сервера", e.getMessage())
);
}
}
public record ErrorResponse(String error, String message) {}
7. Optional вместо null проверок
Исключение: NullPointerException
// ПЛОХО: может быть NPE
public String getUserName(User user) {
return user.getName(); // NPE если user null
}
// ХОРОШО: Optional
public Optional<String> getUserName(User user) {
return Optional.ofNullable(user)
.map(User::getName);
}
// Использование
String name = getUserName(user)
.orElse("Unknown");
// Или с обработкой ошибки
getUserName(user)
.ifPresentOrElse(
name -> System.out.println("Имя: " + name),
() -> System.out.println("Пользователь не найден")
);
8. Функциональный подход - Result типы
Использование Result wrappers:
public class Result<T> {
private final T value;
private final Exception exception;
private final boolean success;
private Result(T value, Exception exception) {
this.success = exception == null;
this.value = value;
this.exception = exception;
}
public static <T> Result<T> success(T value) {
return new Result<>(value, null);
}
public static <T> Result<T> failure(Exception e) {
return new Result<>(null, e);
}
public boolean isSuccess() { return success; }
public T getValue() { return value; }
public Exception getException() { return exception; }
public <U> Result<U> map(Function<T, U> fn) {
if (!success) return failure(exception);
try {
return success(fn.apply(value));
} catch (Exception e) {
return failure(e);
}
}
}
// Использование
Result<User> result = getUserFromDb(id)
.map(User::getName)
.map(String::toUpperCase);
if (result.isSuccess()) {
System.out.println(result.getValue());
} else {
System.out.println("Ошибка: " + result.getException().getMessage());
}
9. Defensive Programming
Проверка аргументов в начале метода:
public class BankAccount {
private final String accountNumber;
private double balance;
public BankAccount(String accountNumber, double initialBalance) {
// Явная проверка и выброс исключения
if (accountNumber == null || accountNumber.trim().isEmpty()) {
throw new IllegalArgumentException("Номер счёта не может быть пустым");
}
if (initialBalance < 0) {
throw new IllegalArgumentException("Начальный баланс не может быть отрицательным");
}
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Сумма должна быть положительной");
}
balance += amount;
}
public void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Сумма должна быть положительной");
}
if (amount > balance) {
throw new InsufficientFundsException("Недостаточно средств");
}
balance -= amount;
}
}
10. Пример в реальной системе
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
private final Logger logger = LoggerFactory.getLogger(OrderService.class);
@Transactional
public Order createOrder(OrderRequest request) {
try {
// Валидация
if (request == null || request.getItems().isEmpty()) {
throw new IllegalArgumentException("Заказ не может быть пустым");
}
// Бизнес логика
Order order = new Order();
order.setItems(request.getItems());
order.setStatus("PENDING");
// Сохранение
Order savedOrder = orderRepository.save(order);
// Оплата
try {
paymentService.processPayment(savedOrder);
savedOrder.setStatus("PAID");
} catch (PaymentException e) {
logger.error("Ошибка платежа для заказа {}", savedOrder.getId(), e);
savedOrder.setStatus("PAYMENT_FAILED");
throw e;
}
return orderRepository.save(savedOrder);
} catch (IllegalArgumentException e) {
logger.warn("Ошибка валидации: {}", e.getMessage());
throw e;
} catch (Exception e) {
logger.error("Неожиданная ошибка при создании заказа", e);
throw new RuntimeException("Ошибка создания заказа", e);
}
}
}
Лучшие практики
- Используй Try-with-resources: для автоматического управления ресурсами
- Специфичная обработка: лови конкретные исключения, не Exception
- Логирование: всегда логируй при перехвате
- Не подавляй исключения: catch (Exception e) {} - плохо
- Бросай ранние: проверяй аргументы в начале метода
- Используй Optional: вместо null проверок
- Собственные исключения: для бизнес логики
- Global handlers: в Spring приложениях
- Документируй: какие исключения может выбросить метод
Правильная обработка исключений — это не просто избежание crashes, это часть контракта и гарантии надёжности вашего кода.