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

Разрешено ли в Java бросать исключения типа Throwable

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

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

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

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

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

Технически разрешено, но это ОЧЕНЬ плохая практика. Хотя компилятор допустит throw new Throwable(), это нарушает все принципы хорошей архитектуры.

Технический аспект: компилятор разрешает

public class BadPracticeExample {
    
    // Это скомпилируется без ошибок
    public static void throwThrowable() throws Throwable {
        throw new Throwable("I'm a Throwable");
    }
    
    // Это тоже скомпилируется
    public static void throwError() throws Throwable {
        throw new Error("I'm an Error");
    }
    
    public static void main(String[] args) throws Throwable {
        throwThrowable(); // Компилятор это позволит
    }
}

Компилятор скажет "Окей, ты выбросил Throwable, и это допустимо".

Почему это плохая идея

1. Ловля Throwable ловит буквально всё

public class Main {
    public static void main(String[] args) {
        try {
            dangerousMethod();
        } catch (Throwable e) {  // Этот блок поймает абсолютно всё
            // Включая:
            // - Exception
            // - Error (OutOfMemoryError, StackOverflowError)
            // - VirtualMachineError
            // - ThreadDeath
            System.out.println("Caught: " + e);
        }
    }
    
    private static void dangerousMethod() throws Throwable {
        // Может выбросить буквально что угодно
        throw new OutOfMemoryError("Not enough memory");
    }
}

Проблема: если поймаешь OutOfMemoryError, приложение может продолжить работу в бракованном состоянии. Память же ещё не освободилась!

2. Error'ы не предназначены для обработки

// ПЛОХО: ловим Error
try {
    // Какой-то код
} catch (OutOfMemoryError e) {  // ОПАСНО!
    // Пытаемся что-то сделать, но памяти нет
    System.out.println("No memory");
    // Сам вывод может выбросить OutOfMemoryError
}

// ПЛОХО: ловим StackOverflowError
try {
    recursiveMethod();
} catch (StackOverflowError e) {  // ОПАСНО!
    // Стек переполнен, новые вызовы могут выбросить ошибку
    log.error("Stack overflow");  // Сама логирование может выбросить ошибку
}

Error'ы означают, что JVM находится в критическом состоянии. Их не нужно обрабатывать, нужно позволить приложению упасть и перезагрузиться.

3. Нарушается принцип наименьших привилегий

// ПЛОХО: слишком широкий спектр
public void processOrder() throws Throwable {
    // Может выбросить что угодно
    // Кто будет это ловить?
    // Что будет ловить? Exception? Error? Throwable?
}

// ХОРОШО: конкретные исключения
public void processOrder() throws OrderProcessingException, PaymentException {
    // Ясно, какие проблемы могут быть
    // Код вызывающей функции точно знает, что ловить
}

4. Проблемы с логированием

public class Logger {
    
    // ПЛОХО: может выбросить Throwable при логировании Error'а
    public void log(Throwable e) throws Throwable {
        System.out.println("Error: " + e.getMessage());
        // Если e - OutOfMemoryError, toString() может выбросить ошибку
    }
    
    // ХОРОШО: ловим и обрабатываем
    public void logSafely(Throwable e) {
        try {
            System.out.println("Error: " + e.getMessage());
        } catch (Exception ex) {
            System.err.println("Failed to log error");
            // Не выбрасываем дальше
        }
    }
}

Иерархия и правила

Throwable (не выбрасывай) содержит:
  • Error (не лови) с OutOfMemoryError, StackOverflowError
  • Exception (выбрасывай) с Checked и Runtime Exceptions

Правильный способ работать с исключениями

1. Создавай свои исключения

// Хорошее checked исключение
public class PaymentProcessingException extends Exception {
    private double failedAmount;
    
    public PaymentProcessingException(String message, double failedAmount) {
        super(message);
        this.failedAmount = failedAmount;
    }
    
    public double getFailedAmount() {
        return failedAmount;
    }
}

// Хорошее unchecked исключение
public class InvalidUserDataException extends RuntimeException {
    public InvalidUserDataException(String message) {
        super(message);
    }
}

2. Выбрасывай конкретные исключения

public class PaymentService {
    
    // ХОРОШО: конкретное исключение
    public void processPayment(Payment payment) throws PaymentProcessingException {
        if (payment.getAmount() < 0) {
            throw new PaymentProcessingException("Amount cannot be negative", 0);
        }
        
        if (payment.getAmount() > 10000) {
            throw new PaymentProcessingException("Amount too large", payment.getAmount());
        }
    }
}

3. Лови конкретные исключения

public class OrderController {
    
    @PostMapping("/orders")
    public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {
        try {
            Order order = paymentService.processPayment(request.getPayment());
            return ResponseEntity.ok(order);
        } catch (PaymentProcessingException e) {
            return ResponseEntity.badRequest()
                .body("Payment failed: " + e.getMessage());
        } catch (InvalidUserDataException e) {
            return ResponseEntity.badRequest()
                .body("Invalid data: " + e.getMessage());
        }
    }
}

4. Не лови Error'ы

public class GoodExample {
    
    public static void main(String[] args) {
        try {
            // Код, который может выбросить Exception
            doSomething();
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
        }
        // Error пройдёт дальше, приложение упадёт (что правильно)
    }
    
    private static void doSomething() throws Exception {
        // код
    }
}

Случай когда можно ловить Throwable

Единственный разумный случай — это на самом верхнем уровне для логирования перед аварийным завершением:

public class Main {
    
    public static void main(String[] args) {
        try {
            // Весь код приложения
            runApplication();
        } catch (Throwable e) {
            // Логируем для диагностики
            logger.fatal("Application crashed", e);
            
            // Отправляем в систему мониторинга
            reportingService.sendCrashReport(e);
            
            // Завершаем с кодом ошибки
            System.exit(1);
        }
    }
    
    private static void runApplication() {
        // весь код
    }
}

Итог

Выброс Throwable в методе — это сигнал плохой архитектуры.

Правильно: public void process() throws ProcessingException { ... }

Неправильно: public void process() throws Throwable { ... }

Всегда используй конкретные типы исключений и позволь Error'ам завершить приложение.