37

37


Особый случай: RuntimeException

Вспомним первый пример в этой главе:

if(t == null)

throw new NullPointerExceptionO;

Только представьте, как ужасно было бы проверять таким образом каждую ссылку, переданную вашему методу. К счастью, делать это не нужно — такая проверка автоматически выполняется во время исполнения Java-программы, и при попытке использования null-ссылок автоматически возбуждается Null- PointerException. Таким образом, использованная в примере конструкция избы¬точна.

Есть целая группа исключений, принадлежащих к этой категории. Они все¬гда возбуждаются в Java автоматически, и вам не придется включать их в спе¬цификацию исключений. Все они унаследованы от одного базового класса RuntimeException, что дает нам идеальный пример наследования: семейство классов, имеющих общие характеристики и поведение. Вам также не придется создавать спецификацию исключений, указывающую на возбуждение методом RuntimeException (или любого унаследованного от него исключения), так как эти исключения относятся к неконтролируемым (unchecked). Такие исключения означают ошибки в программе, и фактически вам никогда не придется перехва¬тывать их — это делается автоматически. Проверка RuntimeException привела бы к излишнему загромождению программы. И хотя вам обычно не требуется пе¬рехватывать RuntimeException, возможно, вы посчитаете нужным возбуждать не¬которые из них в своих собственных библиотеках программ.

Что же происходит, когда подобные исключения не перехватываются? Так как компилятор не заставляет перечислять такие исключения в спецификациях, можно предположить, что исключение RuntimeException проникнет прямо в ме¬тод mainQ, и не будет перехвачено. Чтобы увидеть все в действии, испытайте следующий цример:

//: exceptions/NeverCaught java

// Игнорирование RuntimeExceptions.

// {ThrowsException}

public class NeverCaught {

static void f() {

throw new RuntimeException("H3 f(D;

}

static void g() { f();

}

public static void mainCString[] args) {

g():

}

} ///.-

Можно сразу заметить, что RuntimeException (и все от него унаследованное) является специальным случаем, так как компилятор не требует для него специ¬фикации исключения. Выходные данные выводятся в System.err:

Exception in thread "main" java.lang.RuntimeException- Из f() at NeverCaught.f(NeverCaught.java:7) at NeverCaught.g(NeverCaught.java:10) at NeverCaught.main(NeverCaught.java-13)

Мы приходим к ответу на поставленный вопрос: если RuntimeException доби¬рается до main() без перехвата, то работа программы завершается с вызовом ме¬тода printStackTraceO.

Помните, что только исключения типа RuntimeException (и производных классов) могут игнорироваться во время написания текста программы, в то вре¬мя как остальные действия компилятор осуществляет в обязательном порядке. Это объясняется тем, что RuntimeException является следствием ошибки про¬граммиста, например:

• ошибки, которую невозможно предвидеть (к примеру, получение null- ссылки в вашем методе, переданной снаружи);

• ошибки, которую вы как программист должны были проверить в вашей программе (подобной ArraylndexOutOfBoundsException, с проверкой разме¬ра массива). Ошибки первого вида часто становятся причиной ошибок второго вида.

В подобных ситуациях исключения оказывают неоценимую помощь в отла¬дочном процессе.

Назвать механизм исключений Java узкоспециализированным инструмен¬том было бы неверно. Да, он помогает справиться с досадными ошибками на стадии исполнения программы, которые невозможно предусмотреть заранее, но при этом данный механизм помогает выявлять многие ошибки программи¬рования, выходящие за пределы возможностей компилятора.

Завершение с помощью finally

Часто встречается ситуация, когда некоторая часть программы должна выполнять¬ся независимо от того, было или нет возбуждено исключение внутри блока try. Обычно это имеет отношение к операциям, не связанным с освобождением памя¬ти (так как это входит в обязанности сборщика мусора). Для достижения желае¬мой цели необходимо разместить блок finally  после всех обработчиков исключе¬ний. Таким образом, полная конструкция обработки исключения выглядит так:

try {

// Защищенная секция: рискованные операции, // которые могут породить исключения А, В. или С } catch(A al) {

// Обработчик для ситуации А } catch(B Ы) {

// Обработчик для ситуации В } catch(C cl) {

// Обработчик для ситуации С } finally {

// Действия, производимые в любом случае

}

Чтобы продемонстрировать, что блок finally выполняется всегда, рассмотрим следующую программу:

//: exceptions/FinallyWorks.java // Блок finally выполняется всегда

class ThreeException extends Exception {}

public class FinallyWorks { static int count = 0; public static void main(String[] args) { while(true) { try {

// Операция постфиксного приращения, в первый раз 0: if(count++ == 0)

throw new ThreeExceptionO; System.out.println("Нет исключения"); } catch(ThreeException e) {

System.out.pri ntln("ThreeExcepti on"); } finally {

System.out.println("B блоке finally"); if(count == 2) break; // вне цикла "while"

}

}

}

} /* Output: ThreeException В блоке finally Нет исключения В блоке finally *///-

Результат работы программы показывает, что вне зависимости от того, было ли возбуждено исключение, предложение finally выполняется всегда.

Данный пример также подсказывает, как справиться с тем фактом, что Java не позволяет вернуться к месту возникновения исключения, о чем говорилось ранее. Если расположить блок try в цикле, можно также определить условие, на основании которого будет решено, должна ли программа продолжаться. Так¬же можно добавить статический счетчик или иной механизм для проверки не¬скольких разных решений, прежде чем отказаться от попыток восстановления. Это один из способов обеспечения повышенной отказоустойчивости программ.

Для чего нужен блок finally?

В языках без сборки мусора и без автоматических вызовов деструкторов  блок finally гарантирует освобождение ресурсов и памяти независимо от того, что случилось в блоке try. В Java существует сборщик мусора, поэтому с освобожде¬нием памяти проблем не бывает. Также нет необходимости вызывать деструк¬торы, их просто нет. Когда же нужно использовать finally в Java?

Report Page