Эккель Брюс
Шрифт:
Мы приходим к ответу на поставленный вопрос: если RuntimeException добирается до main без перехвата, то работа программы завершается с вызовом метода printStackTraceO.
Помните, что только исключения типа RuntimeException (и производных классов) могут игнорироваться во время написания текста программы, в то время как остальные действия компилятор осуществляет в обязательном порядке. Это объясняется тем, что RuntimeException является следствием ошибки программиста, например:
• ошибки, которую невозможно предвидеть (к примеру, получение null-ссылки в вашем методе, переданной снаружи);
• ошибки, которую вы как программист должны были проверить в вашей программе (подобной ArraylndexOutOfBoundsException, с проверкой размера массива). Ошибки первого вида часто становятся причиной ошибок второго вида.
В подобных ситуациях исключения оказывают неоценимую помощь в отладочном процессе.
Назвать механизм исключений Java узкоспециализированным инструментом было бы неверно. Да, он помогает справиться с досадными ошибками на стадии исполнения программы, которые невозможно предусмотреть заранее, но при этом данный механизм помогает выявлять многие ошибки программирования, выходящие за пределы возможностей компилятора.
Завершение с помощью finally
Часто встречается ситуация, когда некоторая часть программы должна выполняться независимо от того, было или нет возбуждено исключение внутри блока try. Обычно это имеет отношение к операциям, не связанным с освобождением памяти (так как это входит в обязанности сборщика мусора). Для достижения желаемой цели необходимо разместить блок finally17 после всех обработчиков исключений. Таким образом, полная конструкция обработки исключения выглядит так:
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?
В языках без сборки мусора и без автоматических вызовов деструкторов18 блок finally гарантирует освобождение ресурсов и памяти независимо от того, что случилось в блоке try. В Java существует сборщик мусора, поэтому с освобождением памяти проблем не бывает. Также нет необходимости вызывать деструкторы, их просто нет. Когда же нужно использовать finally в Java?
Блок finally необходим тогда, когда в исходное состояние вам необходимо вернуть что-то другое, а не память. Это может быть, например, открытый файл или сетевое подключение, часть изображения на экране или даже какой-то физический переключатель, вроде смоделированного в следующем примере:
//: exceptions/Switch.java
import static net mindview.util.Print.*;
class Switch {
private boolean state = false; public boolean readO { return state, } public void on { state = true, print(this); } public void offО { state = false, print(this), } public String toStringO { return state ? "on" • "off"; } } ///.-