Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 65 66 67 68 69 70 71 72 73 ... 132

Есть целая группа исключений, принадлежащих к этой категории. Они всегда возбуждаются в 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. Обычно это имеет отношение к операциям, не связанным с освобождением памяти (так как это входит в обязанности сборщика мусора). Для достижения желаемой цели необходимо разместить блок 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"; } } ///.-

//. exceptions/OnOffException]..java

public class OnOffExceptionl extends Exception {} lll-

ll . exceptions/0n0ffException2.java

public class 0n0ffException2 extends Exception {} III ~

//• exceptions/OnOffSwitch java 11 Для чего нужно finally?

public class OnOffSwitch {

private static Switch sw = new SwitchO; static void f()

throws OnOffExceptionl, 0n0ffException2 {} public static void main(String[] args) { try {

sw.onO;

// Код, способный возбуждать исключения... f();

sw off(): } catch(OnOffExceptionl e) {

System.out.pri ntin("OnOffExcepti onl"); sw.offO; } catch(OnOffException2 e) {

System.out.pri ntin("OnOffExcepti on2"); sw.offO:

}

}

} /* Output-

on

off

*///:-

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

II: exceptions/WithFinally.java 11 Finally гарантирует выполнение завершающего кода.

public class WithFinally {

static Switch sw = new SwitchO; public static void main(String[] args) { try {

sw.onO;

// Код, способный возбуждать исключения. OnOffSwitch.fO; } catch(OnOffExceptionl e) {

System out.printing"OnOffExceptionl"); } catch(OnOffException2 e) {

System out println( OnOffException2"); } finally {

sw.offO;

}

}

} /* Output:

on

off

*///:-

Здесь вызов метода sw.off() просто перемещен в то место, где он гарантированно будет выполнен.

Даже если исключение не перехватывается в текущем наборе условий catch, блок finally отработает перед тем, как механизм обработки исключений продолжит поиск обработчика на более высоком уровне:

//: exceptions/AlwaysFinally.java

// Finally выполняется всегда

import static net.mindview.util Print.*:

class FourException extends Exception {}

public class AlwaysFinally {

public static void main(String[] args) {

print("Входим в первый блок try"), try {

print("Входим во второй блок try"): try {

throw new FourExceptionO, } finally {

print("finally во втором блоке try"):

}

} catch(FourException e) { System.out.println(

"Перехвачено FourException в первом блоке try"):

} finally {

System.out.println("finally в первом блоке try"):

}

}

} /^Output-

Входим в первый блок try Входим во второй блок try finally во втором блоке try Перехвачено FourException в первом блоке try finally в первом блоке try *///:-

Блок finally также исполняется при использовании команд break и continue. Заметьте, что комбинация finally в сочетании с break и continue с метками снимает в Java всякую необходимость в операторе goto.

Использование finally с return

Поскольку секция finally выполняется всегда, важные завершающие действия будут выполнены даже при возврате из нескольких точек метода:

//• excepti ons/Multi pleReturns java import static net.mindview util Print.*;

public class MultipleReturns {

public static void f(int i) {

pri nt("Инициализация. требующая завершения"), try {

print("Точка 1"), if(i == 1) return, print("Точка 2"); if(i == 2) return, print("Точка 3"), if(i == 3) return, print("Конец"), return; } finally {

ргШС'Завершение"),

}

}

public static void main(String[] args) { for (int i =1, i <=4; i++) f(i).

}

} /* Output;

Инициализация, требующая завершения

Точка 1

Завершение

Инициализация, требующая завершения Точка 1 Точка 2 Завершение

Инициализация, требующая завершения

1 ... 65 66 67 68 69 70 71 72 73 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий