Выбрать главу

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

Впрочем, «обмануть» компилятор все же можно: вы вправе объявить о воз¬буждении исключения, которого на самом деле нет. Компилятор верит вам на слово и заставляет пользователей метода поступать так, как будто им и в са¬мом деле необходимо перехватывать исключение. Таким образом можно «заре¬зервировать» исключение на будущее и уже потом возбуждать его, не изменяя описания готовой программы. Такая возможность может пригодиться и для создания абстрактных базовых классов и интерфейсов, в производных классах которых может возникнуть необходимость в возбуждении исключений.

Исключения, которые проверяются и навязываются еще на этапе компиля¬ции программы, называют контролируемыми (checked).

Перехват произвольных исключений

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

catch(Exception е) {

System.out рппШСПерехвачено исключение");

}

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

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

• String getMessage(), String getLocalizedMessage() возвращают подробное со¬общение об ошибке (или сообщение, локализованное для текущего контекста);

• String toString() возвращает короткое описание объекта Throwable, вклю¬чая подробное сообщение, если оно присутствует;

• void printStackTrace(), void printStackTrace(PrintStream), void printStack- Trace(java.io.PrintWriter) выводят информацию об объекте Throwable и трассировку стека вызовов для этого объекта. Трассировка стека вызо¬вов показывает последовательность вызова методов, которая привела к точке возникновения исключения. Первый вариант отправляет ин¬формацию в стандартный поток ошибок, второй и третий — в поток по

вашему выбору (в главе «Ввод/вывод» вы поймете, почему типов пото¬ков два);

• Throwable fiUInStackTrace() записывает в объект Throwable информацию о текущем состоянии стека. Метод используется при повторном возбужде¬нии ошибок или исключений.

Вдобавок в вашем распоряжении находятся методы типа Object, базового для Throwable (и для всех остальных классов). При использовании исключений мо¬жет пригодиться метод getClass(), который возвращает информацию о классе объекта. Эта информация заключена в объекте типа Class. Например, вы можете узнать имя класса вместе с информацией о пакете методом getName() или полу¬чить только имя класса методом getSimpleName().

Рассмотрим пример с использованием основных методов класса Exception:

//. exceptions/ExceptionMethods.java // Демонстрация методов класса Exception, import static net.mindview.util.Print.*;

public class ExceptionMethods {

public static void main(String[] args) { try {

throw new Exception("Мое исключение"); } catch(Exception e) {

print("Перехвачено"). print("getMessage():" + e.getMessageO); print("getLocalizedMessage()." +

e.getLocali zedMessage());

print ("toStringO." + e); print("printStackTrace():"); e.printStackTrace(System.out);

}

}

} /* Output. Перехвачено

getMessageO :Moe исключение

getLocalizedMessage().Мое исключение

toStringO.java.lang.Exception: Мое исключение

printStackTraceO:

java lang Exception: Мое исключение

at ExceptionMethods main(ExceptionMethods.java 8)

*///:-

Как видите, методы последовательно расширяют объем выдаваемой инфор¬мации — всякий последующий фактически является продолжением предыду¬щего.

Трассировка стека

Информацию, предоставляемую методом printStackTrace(), также можно полу¬чить напрямую вызовом getStackTrace(). Метод возвращает массив элементов трассировки, каждый из которых представляет один кадр стека. Нулевой эле¬мент представляет вершину стека, то есть последний вызванный метод после¬довательности (точка, в которой был создан и инициирован объект Throwable).

Соответственно, последний элемент массива представляет «низ» стека, то есть первый вызванный элемент последовательности. Рассмотрим простой пример:

//: exceptions/WhoCalled.java

// Программный доступ к данным трассировки стека

public class WhoCalled { static void f() {

// Выдача исключения для заполнения трассировочных данных try {

throw new ExceptionO; } catch (Exception e) {

for(StackTraceElement ste : e.getStackTraceO)

System.out.pri nt1n(ste.getMethodName()):

}

}

static void g() { f(): } static void h() { g(); } public static void main(String[] args) { f():

System.out.printlnC ");

g():

System, out. printlnC'-- "):

h();

}

} /* Output: f

main

f g

main

f g

h

main *///:-

Повторное возбуждение исключения

В некоторых ситуациях требуется заново возбудить уже перехваченное исклю¬чение; чаще всего это происходит при использовании Exception для перехвата всех исключений. Так как ссылка на текущее исключение уже имеется, вы по¬просту возбуждаете исключение по этой ссылке:

catch(Exception е) {

System, out. pri nti пСБыло возбуждено исключение"): throw e:

}

При повторном возбуждении исключение передается в распоряжение обра¬ботчика более высокого уровня. Все остальные предложения catch текущего блока try игнорируются. Вся информация из объекта, представляющего исклю¬чение, сохраняется, и обработчик более высокого уровня, перехватывающий подобные исключения, сможет ее извлечь.

Если вы просто заново возбуждаете исключение, информация о нем, выво¬димая методом printStackTrace(), будет по-прежнему относиться к месту воз¬никновения исключения, но не к месту его повторного возбуждения. Если вам понадобится использовать новую трассировку стека, вызовите метод fi LLI n S ta с kT г а с e (), который возвращает исключение (объект Throwable), созданное на базе старого с помещением туда текущей информации о стеке. Вот как это выглядит:

// exceptions/Rethrowing.java // Демонстрация метода fillInStackTraceO

public class Rethrowing {

public static void f() throws Exception {

System.out.рппШССоздание исключения в f(D; throw new Exception ("возбуждено из f(D;

}

public static void g() throws Exception { try {

f().

} catch(Exception e) {

System, out. pri ntl n("B методе g(), e printStackTraceO"). e printStackTrace(System.out); throw e,

}

}

public static void h() throws Exception { try {

f():

} catch(Exception e) {

System out.printlnC'B методе h(), e.printStackTrace()"),

e printStackTrace(System.out),

throw (Exception)e fill InStackTraceO,

}

}

public static void main(String[] args) { try {

go.

} catch(Exception e) {

System, out pri ntl n( "main- printStackTraceO"). e.printStackTrace(System out);

}

try {

hO.

} catch(Exception e) {

System out.printlnCmairr printStackTraceO"); e printStackTrace(System out);