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

Можно ли в одном Catch перехватить несколько исключений?

2.0 Middle🔥 111 комментариев
#Docker, Kubernetes и DevOps#Основы Java

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

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

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

Перехват нескольких исключений в одном Catch

Да, в Java можно перехватить несколько исключений в одном catch блоке несколькими способами, в зависимости от версии Java и типа исключений.

Способ 1: Multi-catch (Java 7+) — рекомендуется

Multi-catch позволяет перехватить несколько типов исключений через оператор |:

// ✅ Java 7+ : Multi-catch
try {
    // Код, который может выбросить несколько исключений
    readFile("data.txt");
    parseJSON(fileContent);
    
} catch (FileNotFoundException | JsonSyntaxException e) {
    // Обработка обоих исключений одинаково
    logger.error("Error reading or parsing file", e);
    // e — это effectively final переменная типа FileNotFoundException | JsonSyntaxException
}

Это эквивалентно:

// ❌ Старый способ (Java 1.0+): много кода
try {
    readFile("data.txt");
    parseJSON(fileContent);
    
} catch (FileNotFoundException e) {
    logger.error("Error reading or parsing file", e);
} catch (JsonSyntaxException e) {
    logger.error("Error reading or parsing file", e);
}

Преимущества multi-catch:

  • Меньше кода (DRY)
  • Одна переменная e для обработки
  • Переменная effectively final

Пример с несколькими исключениями

public class OrderProcessingService {
    
    public void processOrder(Order order) {
        try {
            // Операции которые могут выбросить разные исключения
            validateOrder(order);        // может выбросить OrderValidationException
            checkInventory(order);       // может выбросить InventoryException
            processPayment(order);       // может выбросить PaymentException
            saveOrder(order);            // может выбросить DatabaseException
            
        } catch (OrderValidationException | InventoryException | PaymentException e) {
            // Все три исключения обрабатываем одинаково
            logger.error("Order processing failed: {}", e.getMessage());
            notifyCustomer(order, "Sorry, we cannot process your order right now");
            
        } catch (DatabaseException e) {
            // Специальная обработка для БД ошибок
            logger.error("Critical: Database error", e);
            alertMonitoring("Database connection lost");
        }
    }
}

Способ 2: Иерархия исключений (наследование)

Если исключения связаны через наследование, можно перехватить родительский класс:

// Иерархия исключений
public class OrderException extends Exception {}
public class PaymentException extends OrderException {}
public class InventoryException extends OrderException {}
public class NotificationException extends OrderException {}

// Обработка
try {
    processOrder(order);
    
} catch (OrderException e) {
    // Перехватит PaymentException, InventoryException, NotificationException
    logger.error("Order failed", e);
    
    // Но лучше обработать разные исключения отдельно
    if (e instanceof PaymentException) {
        handlePaymentError((PaymentException) e);
    } else if (e instanceof InventoryException) {
        handleInventoryError((InventoryException) e);
    }
}

Способ 3: Ловля Exception (осторожно!)

// ❌ Не рекомендуется: перехватываешь ВСЕ исключения
try {
    riskyOperation();
} catch (Exception e) {
    // Это перехватит FileNotFoundException, NullPointerException и др.
    logger.error("Something went wrong", e);
}

// ✅ Лучше: только нужные исключения
try {
    riskyOperation();
} catch (SpecificException e) {
    logger.error("Expected error", e);
} catch (RuntimeException e) {
    logger.error("Unexpected runtime error", e);
    throw e; // Пробросить дальше
}

Практические примеры из IKEA

Пример 1: Интеграция с внешними API

public class ExternalApiIntegration {
    
    public PaymentResult verifyPayment(PaymentInfo payment) {
        try {
            // Вызовы к внешним системам могут выбросить разные исключения
            PaymentGatewayResponse response = paymentGateway.verify(payment);
            AnalyticsEvent event = createEvent(response);
            analyticsService.track(event);
            
            return PaymentResult.success(response);
            
        } catch (PaymentGatewayException | AnalyticsServiceException | 
                 JsonProcessingException e) {
            
            // Все три исключения обрабатываем как критичные
            logger.error("Payment verification failed", e);
            Sentry.captureException(e); // Отправляем в мониторинг
            
            return PaymentResult.failure(
                "Payment verification unavailable. Please try again later."
            );
        }
    }
}

Пример 2: Работа с файлами

public class FileProcessing {
    
    public void processProductCatalog(String filePath) {
        try {
            byte[] fileContent = readFile(filePath);
            List<Product> products = parseCSV(fileContent);
            saveProducts(products);
            
        } catch (FileNotFoundException | FileReadException | 
                 IOException | ParseException e) {
            
            // Общая обработка для всех ошибок чтения/парсинга
            logger.error("Failed to process catalog from {}", filePath, e);
            notifyAdmin("Catalog processing failed: " + e.getMessage());
            
            // Возможно, откатимся к cached версии
            useCachedCatalog();
            
        } catch (DatabaseException e) {
            // Отдельная обработка для БД
            logger.error("Cannot save products to database", e);
            rollback();
            throw new CatalogProcessingException(e);
        }
    }
}

Пример 3: REST контроллер

@RestController
public class OrderController {
    
    @PostMapping("/orders")
    public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {
        try {
            Order order = orderService.create(request);
            return ResponseEntity.ok(order);
            
        } catch (InvalidOrderException | OutOfStockException | 
                 PaymentFailedException e) {
            
            // Клиентские ошибки — вернём 400
            ErrorResponse error = new ErrorResponse(
                e.getMessage(),
                e.getClass().getSimpleName()
            );
            return ResponseEntity.badRequest().body(error);
            
        } catch (ServiceException | DatabaseException e) {
            // Серверные ошибки — вернём 500
            logger.error("Internal server error", e);
            return ResponseEntity.status(500).body(
                new ErrorResponse("Internal server error", "500")
            );
        }
    }
}

Важные правила Multi-catch

1. Исключения не должны быть в отношении наследования

// ❌ Ошибка: IOException — родитель для FileNotFoundException
try {
    readFile();
} catch (IOException | FileNotFoundException e) {
    // Compilation error: Alternatives in a multi-catch statement cannot be related by subtyping
}

// ✅ Правильно
try {
    readFile();
} catch (IOException e) {
    // FileNotFoundException будет перехвачена как IOException
}

2. Переменная в multi-catch — это эффективно final

try {
    doSomething();
} catch (ExceptionA | ExceptionB e) {
    e = new ExceptionA(); // ❌ Ошибка: cannot assign to effectively final
    
    // Но можешь использовать её
    logger.error(e.getMessage());
}

3. Общий тип переменной e — это общий родитель

try {
    operation();
} catch (FileNotFoundException | JsonSyntaxException e) {
    // e имеет тип: FileNotFoundException | JsonSyntaxException
    // Доступны только методы из Exception
    e.getMessage();        // ✅ OK
    e.printStackTrace();    // ✅ OK
    
    // Но не доступны специфичные методы
    e.getPath();           // ❌ Error: FileNotFoundException не в области видимости
    
    // Нужно приводить типы
    if (e instanceof FileNotFoundException) {
        FileNotFoundException fnf = (FileNotFoundException) e;
        logger.error("File not found: {}", fnf.getPath());
    }
}

Паттерны для обработки разных исключений

Паттерн 1: Группируем по серьёзности

try {
    process();
} catch (ValidationException | BusinessRuleException e) {
    // Expected exceptions — клиент сделал что-то неправильно
    logger.warn("Business error: {}", e.getMessage());
    return error(e.getMessage());
} catch (SQLException | NetworkException e) {
    // Infrastructure exceptions — проблема с системой
    logger.error("System error", e);
    alertOps(e);
    return error("System is temporarily unavailable");
}

Паттерн 2: Используем interface

// Общий интерфейс для исключений которые хочешь перехватить
public interface RetryableException {
    boolean isRetryable();
}

public class TemporaryConnectionException extends RuntimeException 
    implements RetryableException {
    public boolean isRetryable() { return true; }
}

public class TimeoutException extends RuntimeException 
    implements RetryableException {
    public boolean isRetryable() { return true; }
}

// Обработка
try {
    apiCall();
} catch (TemporaryConnectionException | TimeoutException e) {
    if (((RetryableException) e).isRetryable()) {
        retryWithBackoff();
    }
}

Общие рекомендации

  1. Используй multi-catch для связанных ошибок

    catch (FileNotFoundException | IOException e) ✅
    
  2. Не используй catch (Exception e)

    catch (Exception e)  ❌ Слишком общее
    
  3. Специфичные исключения обрабатывай отдельно

    catch (CriticalException e) {
        // Специальная обработка
    } catch (OtherException | AnotherException e) {
        // Общая обработка
    }
    
  4. Проверь иерархию исключений

    // Если IOException родитель FileNotFoundException,
    // не пытайся перехватить оба в multi-catch
    

Заключение

Multi-catch (Java 7+) — это удобный способ перехватить несколько исключений в одном блоке:

catch (ExceptionA | ExceptionB | ExceptionC e) {
    // Обработка для всех трёх
}

Это делает код:

  • Чище — нет дублирования
  • Понятнее — явно видно какие исключения обрабатываются
  • Проще для поддержки — одна логика обработки