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

Можно ли бросить исключение типа Throwable?

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

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

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

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

Можно ли бросить исключение типа Throwable в Java?

Технически можно, но это очень плохая практика и считается антипаттерном. Нужно понимать иерархию исключений и когда можно, а когда нельзя бросать разные типы Throwable.

Иерархия Throwable

Throwable
├── Exception (checked)
│   ├── IOException (checked)
│   ├── SQLException (checked)
│   ├── RuntimeException (unchecked)
│   │   ├── NullPointerException
│   │   ├── IllegalArgumentException
│   │   ├── IndexOutOfBoundsException
│   │   └── ...
│   └── ...
└── Error (не ловятся)
    ├── OutOfMemoryError
    ├── StackOverflowError
    ├── VirtualMachineError
    └── ...

Технически: Да, можно бросить Throwable

public void badMethod() throws Throwable {
    throw new Throwable("Прямой Throwable");  // Компилируется!
}

public static void main(String[] args) throws Throwable {
    try {
        badMethod();
    } catch (Throwable t) {
        System.out.println("Caught: " + t);
    }
}

// Результат:
// Caught: java.lang.Throwable: Прямой Throwable

Код скомпилируется и будет работать. Но это очень плохо!

Почему НЕ нужно бросать Throwable напрямую

Причина 1: Это включает и Error

public void badMethod() throws Throwable {
    // Если бросить OutOfMemoryError, программа может сломаться
    throw new OutOfMemoryError();  // Это Throwable!
}

public static void main(String[] args) throws Throwable {
    try {
        badMethod();
    } catch (Throwable t) {  // Ловишь даже Error - плохо!
        // Пытаешься обработать OutOfMemoryError - невозможно!
        System.out.println("Memory exhausted");
    }
}

Error'ы НЕ должны ловиться. Они означают, что JVM в беде.

Причина 2: Скрывает что ты бросаешь

// ПЛОХО
public void processData(String data) throws Throwable {
    if (data == null) {
        throw new NullPointerException();  // Что именно?
    }
    if (data.length() > 100) {
        throw new IllegalArgumentException();  // Что точно?
    }
    if (!validateData(data)) {
        throw new DataFormatException();  // Это what?
    }
}

// Вызывающий код не знает, что ловить:
try {
    processData("test");
} catch (Throwable t) {  // Too broad!
    // Невозможно правильно обработать
    t.printStackTrace();
}

Хорошо:

public void processData(String data) throws DataFormatException, IllegalArgumentException {
    if (data == null) {
        throw new NullPointerException();  // Специфично
    }
    if (data.length() > 100) {
        throw new IllegalArgumentException("Data too long");  // Ясно
    }
    if (!validateData(data)) {
        throw new DataFormatException("Invalid format");  // Понятно
    }
}

// Вызывающий код знает, что ловить:
try {
    processData("test");
} catch (DataFormatException e) {
    System.out.println("Format error: " + e.getMessage());
} catch (IllegalArgumentException e) {
    System.out.println("Invalid argument: " + e.getMessage());
}

Причина 3: JVM и системные ошибки

public void dangerousMethod() throws Throwable {
    // Если JVM выбросит StackOverflowError, можешь ловить это?
    // Нет! Стек переполнен, можешь не хватить памяти для обработчика!
    throw new StackOverflowError();  // Это тоже Throwable
}

public static void main(String[] args) throws Throwable {
    try {
        dangerousMethod();
    } catch (Throwable t) {  // Ловишь системную ошибку
        // Это может вызвать cascading failures
        t.printStackTrace();  // Может не сработать
    }
}

Правильная иерархия

Что ты ДОЛЖЕН ловить:

try {
    riskyOperation();
} catch (IOException e) {  // Specific
    // Handle IO problem
} catch (SQLException e) {  // Specific
    // Handle database problem
} catch (RuntimeException e) {  // Unchecked
    // Handle runtime issue
} catch (Exception e) {  // General exception
    // Last resort - но всё равно не Exception все
}
// НЕ ловишь Throwable или Error!

Что ты НЕ должен ловить:

// АНТИПАТТЕРН 1: Ловишь Throwable
try {
    something();
} catch (Throwable t) {  // TOO BROAD!
    log.error("Something went wrong", t);
}

// АНТИПАТТЕРН 2: Ловишь Exception как fallback для всего
try {
    something();
} catch (Exception e) {  // Almost as bad
    log.error("Error", e);
}

// АНТИПАТТЕРН 3: Ловишь Error
try {
    something();
} catch (OutOfMemoryError e) {  // NO!
    // Cannot recover from this
}

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

Пример 1: Throwable в throws декларации

// ПЛОХО - слишком широко
public void readFile(String path) throws Throwable {
    // Объявляешь что можешь бросить что угодно
    Files.readAllLines(Paths.get(path));
}

// ХОРОШО - специфично
public void readFile(String path) throws IOException {
    // Только IO ошибки
    Files.readAllLines(Paths.get(path));
}

Пример 2: Бросание Throwable напрямую

// ОЧЕНЬ ПЛОХО
public void process() throws Throwable {
    throw new Throwable("Something wrong");  // Never do this!
}

// ХОРОШО
public void process() throws ProcessingException {
    throw new ProcessingException("Failed to process");  // Создай свой exception
}

// Собственный exception
public class ProcessingException extends Exception {
    public ProcessingException(String message) {
        super(message);
    }
    
    public ProcessingException(String message, Throwable cause) {
        super(message, cause);
    }
}

Пример 3: Правильная обработка иерархии

public class DataProcessor {
    public void process(String data) throws DataValidationException, StorageException {
        try {
            validate(data);  // Throws DataValidationException
            store(data);     // Throws StorageException
        } catch (IOException e) {
            throw new StorageException("Cannot write data", e);
        }
    }
    
    private void validate(String data) throws DataValidationException {
        if (data == null) {
            throw new DataValidationException("Data cannot be null");
        }
    }
    
    private void store(String data) throws IOException {
        Files.write(Paths.get("data.txt"), data.getBytes());
    }
}

// Использование
try {
    processor.process("test");
} catch (DataValidationException e) {
    // Handle validation error
} catch (StorageException e) {
    // Handle storage error
}

Когда Throwable МОЖЕТ быть приемлем

Редкие случаи когда можно использовать Throwable:

// 1. В main методе (как последняя мера)
public static void main(String[] args) throws Throwable {
    // Okayish - просто пропагируешь всё наверх
    application.run(args);
}

// 2. В generic фреймворке, где нужно ловить буквально всё
public interface Task {
    void execute() throws Throwable;  // Framework-level
}

// 3. При рефлексии (очень редко)
public Object invoke() throws Throwable {
    // Рефлексия может бросить что угодно
    return method.invoke(target);
}

// 4. При работе с threads (очень редко)
public void run() throws Throwable {
    // Thread.run() обязан не бросать checked exceptions
    // но может бросить unchecked
}

Даже в этих случаях это не идеально.

Таблица: Что ловить и когда

ИсключениеЛовитьБросатьПримечание
ThrowableНЕТНЕТСлишком широко
ErrorНЕТНЕТСистемная ошибка, не восстанавливается
ExceptionРедкоМожноBroad, но приемлемо как последний фильтр
IOExceptionДАДАChecked, специфично
SQLExceptionДАДАChecked, специфично
RuntimeExceptionДАМожноUnchecked, для программных ошибок
Custom ExceptionДАДАBest practice
NullPointerExceptionНЕТДАНе ловись, предотвращай
IllegalArgumentExceptionРедкоДАValidate input вместо ловли

Лучшая практика

Правило 1: Специфичность

// ДА
try {
    conn = getConnection();
} catch (SQLException e) {
    // Специфичная ошибка
}

// НЕТ
try {
    conn = getConnection();
} catch (Throwable t) {
    // Неправильно
}

Правило 2: Создай свои исключения

public class ApiException extends Exception {}
public class ValidationException extends Exception {}
public class DatabaseException extends Exception {}

// Используй их вместо Throwable

Правило 3: Не ловишь то, что не можешь обработать

// НЕПРАВИЛЬНО
try {
    calculateResult();
} catch (OutOfMemoryError e) {
    // Не сможешь ничего сделать
    e.printStackTrace();
}

// ПРАВИЛЬНО
try {
    calculateResult();
} catch (OutOfMemoryError e) {
    // Not catching - let it crash
}

Вывод

Можно бросить Throwable технически, но:

  1. НЕ ДЕЛАЙ ЭТОГО — это антипаттерн
  2. Используй специфичные исключения — Exception, IOException, etc.
  3. Создавай свои исключения — для своего домена
  4. Никогда не ловишь Throwable — даже если объявлена в throws
  5. Никогда не ловишь Error — это системные ошибки

Тhrowable нужен только для полной иерархии. На практике работаешь с Exception и её подклассами.