34

34


Спецификации исключений

В языке Java желательно сообщать программисту, вызывающему ваш метод, об исключениях, которые данный метод способен возбуждать. Пользователь, вызывающий метод, сможет написать весь необходимый код для перехвата воз¬можных исключений. Конечно, когда доступен исходный код, программист- клиент может пролистать его в поиске предложений throw, но библиотеки не всегда поставляются с исходными текстами. Чтобы библиотека не превраща¬лась в «черный ящик», в Java добавили синтаксис (обязательный для использо¬вания), при помощи которого вы сообщаете клиенту об исключениях, возбуж¬даемых методом, чтобы он сумел правильно обработать их. Этот синтаксис называется спецификацией исключений (exception specification), входит в объяв¬ление метода и следует сразу за списком аргументов.

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

void f() throws TooBig. TooSmall. DivZero { //...

Однако запись

void f() { // ...

означает, что метод не вырабатывает исключений. (Кроме исключений, произ¬водных от RuntimeException, которые могут быть возбуждены практически в лю¬бом месте — об этом еще будет сказано.)

Обойти спецификацию исключений невозможно — если ваш метод возбуж¬дает исключения и не обрабатывает их, компилятор предложит либо обрабо¬тать исключение, либо включить его в спецификацию. Жестким контролем за соблюдением правил сверху донизу 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() {

Report Page