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