«1. Обзор
В этом уроке мы рассмотрим последствия перехвата Throwable.
2. Класс Throwable
В документации по Java класс Throwable определяется как «надкласс всех ошибок и исключений в языке Java».
Давайте посмотрим на иерархию класса Throwable:
Класс Throwable имеет два прямых подкласса, а именно классы Error и Exception.
Error и его подклассы являются непроверяемыми исключениями, в то время как подклассы Exception могут быть либо проверенными, либо непроверенными исключениями.
Давайте рассмотрим типы ситуаций, в которых программа может столкнуться при сбое.
3. Восстанавливаемые ситуации
Существуют ситуации, когда восстановление вообще возможно и может быть обработано как проверенными, так и непроверенными подклассами класса Exception.
Например, программа может захотеть использовать файл, который не существует в указанном месте, в результате чего будет выдано проверенное исключение FileNotFoundException.
Другой пример: программа пытается получить доступ к системному ресурсу, не имея на это разрешения, что приводит к возникновению непроверенного исключения AccessControlException.
Согласно документации Java, класс Exception «указывает на условия, которые разумное приложение может захотеть перехватить».
4. Неисправимые ситуации
Бывают случаи, когда программа может оказаться в состоянии, когда восстановление невозможно в случае сбоя. Типичными примерами этого являются переполнение стека или нехватка памяти JVM.
В этих ситуациях JVM выдает соответственно StackOverflowError и OutOfMemoryError. Как следует из их названий, это подклассы класса Error.
Согласно документации Java, класс Error \»указывает на серьезные проблемы, которые разумное приложение не должно пытаться отловить\».
5. Пример восстановимых и невосстановимых ситуаций
Предположим, что у нас есть API, который позволяет вызывающим сторонам добавлять уникальные идентификаторы к некоторому хранилищу с помощью метода addIDsToStorage:
class StorageAPI {
public void addIDsToStorage(int capacity, Set<String> storage) throws CapacityException {
if (capacity < 1) {
throw new CapacityException("Capacity of less than 1 is not allowed");
}
int count = 0;
while (count < capacity) {
storage.add(UUID.randomUUID().toString());
count++;
}
}
// other methods go here ...
}
Несколько потенциальных точек отказа могут возникнуть, когда вызов addIDsToStorage:
-
CapacityException — проверенный подкласс Exception при передаче значения емкости менее 1. NullPointerException — непроверенный подкласс Exception, если вместо экземпляра Set указано нулевое значение хранилища. \u003cString\u003e OutOfMemoryError — непроверенный подкласс Error, если JVM исчерпывает память до выхода из цикла while
Ситуации CapacityException и NullPointerException — это сбои, от которых программа может восстановиться, но OutOfMemoryError является неустранимой.
6. Перехват Throwable
Предположим, что пользователь API перехватывает Throwable только в try-catch при вызове addIDsToStorage:
public void add(StorageAPI api, int capacity, Set<String> storage) {
try {
api.addIDsToStorage(capacity, storage);
} catch (Throwable throwable) {
// do something here
}
}
Это означает, что вызывающий код реагирует на исправимые и неисправимые ситуации в так же.
Общее правило обработки исключений состоит в том, что блок try-catch должен быть как можно более конкретным при перехвате исключений. То есть, следует избегать всеобъемлющего сценария.
Перехват Throwable в нашем случае нарушает это общее правило. Чтобы реагировать на восстанавливаемые и неисправимые ситуации отдельно, вызывающий код должен проверять экземпляр объекта Throwable внутри блока catch.
Лучше использовать особый подход к обработке исключений и избегать попыток справиться с неисправимыми ситуациями.
7. Заключение
В этой статье мы рассмотрели последствия перехвата Throwable в блоке try-catch.
Как всегда, полный исходный код примера доступен на Github.