48

48


Создание собственных исключений
Ваш выбор не ограничивается использованием уже существующих в Java ис¬ключений. Иерархия исключений JDK не может предусмотреть все возможные ошибки, поэтому вы вправе создавать собственные типы исключений для обо¬значения специфических ошибок вашей программы.
Для создания собственного класса исключения вам придется определить его производным от уже существующего типа — желательно наиболее близкого к вашей ситуации (хоть это и не всегда возможно). В простейшем случае созда¬ется класс с конструктором по умолчанию:
//• exceptions/InheritingExceptions java
// Создание собственного исключения
class SimpleException extends Exception {}
public class InheritingExceptions {
public void f() throws SimpleException {
System.out.printin("Возбуждаем SimpleException из f()"). throw new SimpleException();
}
public static void main(String[] args) {
Inherit! ngExcepti ons sed = new InheritingExceptionsO;
try {
sed.fO; } catch(SimpleException e) {
System.out.println( Перехвачено!").
}
}
} /* Output
Возбуждаем SimpleException из f() Перехвачено! */// ~
Компилятор создает конструктор по умолчанию, который автоматически вызывает конструктор базового класса. Конечно, в этом случае вы лишаетесь конструктора вида SimpleException(String), но на практике он не слишком часто используется. Как вы еще увидите, наиболее важно в исключении именно имя класса, так что в основном исключений, похожих на созданное выше, будет дос¬таточно.
В примере результаты работы выводятся на консоль. Впрочем, их также можно направить в стандартный поток ошибок, что достигается использовани¬ем класса System.err. Обычно это правильнее, чем выводить в поток System.out, который может быть перенаправлен. При выводе результатов с помощью System, err пользователь заметит их скорее, чем при выводе в System.out.
Также можно создать класс исключения с конструктором, получающим ар¬гумент String:
//: exceptions/Full Constructors java
class MyException extends Exception { public MyException() {}
public MyException(String msg) { super(msg); }
}
public class Full Constructors {
public static void f() throws MyException {
System.out рппШГВозбуждаем MyException из fO"). throw new MyException();
}
public static void g() throws MyException {
System, out. pri ntl n( "Возбуждаем MyException из g(D; throw new MyException("Создано в g()");
}
public static void main(String[] args) { try {
f();
} catch(MyException e) {
e.printStackTrace(System.err);
}
try {
g();
} catch(MyException e) {
e.pri ntStackTrace(System.err):
}
}
} /* Output:
Возбуждаем MyException из f() продолжение &
MyException
at Ful1 Constructors.f(Ful1 Constructors.java:11) at Full Constructors main(FullConstructors.java-19) Возбуждаем MyException из g() MyException Создано в g()
at Full Constructors g(Ful1 Constructors java:15) at FullConstructors.main(FullConstructors.java 24)
III-
Изменения незначительны — появилось два конструктора, определяющие способ создания объекта MyException. Во втором конструкторе используется конструктор родительского класса с аргументом String, вызываемый ключевым словом super.
В обработчике исключений вызывается метод printStackTrace() класса Throwable (базового для Exception). Этот метод выводит информацию о последо¬вательности вызовов, которая привела к точке возникновения исключения. В нашем примере информация направляется в System.out, но вызов по умолча¬нию направляет информацию в стандартный поток ошибок:
e.printStackTraceO,
Регистрация исключений
Вспомогательное пространство имен java.utiLlogging позволяет зарегистриро¬вать информацию об исключениях в журнале. Базовые средства регистрации достаточно просты:
II exceptions/LoggingExceptions.java // Регистрация исключений с использованием Logger import java.util.logging *; import java.io *;
class LoggingException extends Exception { private static Logger logger =
Logger getLogger("LoggingException"); public LoggingException() {
StringWriter trace = new StringWriter(); printStackTrace(new PrintWriter(trace)), 1ogger.severe(trace.toStri ng()),

public class LoggingExceptions {
public static void main(String[] args) { try {
throw new LoggingException(); } catch(LoggingException e) {
System.err.println("Перехвачено " + e),
}
try {
throw new LoggingExceptionO: } catch(LoggingException e) {
System.err.println("Перехвачено " + e),

} /* Output (85Х match)
Aug 30, 2005 4:02:31 РМ LoggingException <init> SEVERE LoggingException
at LoggingExceptions.main(LoggingExceptions.java:19)
Перехвачено LoggingException
Aug 30, 2005 4.02.31 PM LoggingException <init>
SEVERE LoggingException
at Loggi ngExcepti ons.mai n(Loggi ngExcepti ons.j ava:24)
Перехвачено LoggingException */// -
Статический метод Logger.getLogger() создает объект Logger, ассоциируемый с аргументом String (обычно имя пакета и класса, к которому относятся ошиб¬ки); объект передает свой вывод в System.err. Простейший способ записи ин¬формации в Logger заключается в вызове метода, соответствующего уровню ошибки; в нашем примере используется метод severe(). Нам хотелось бы создать String для регистрируемого сообщения из результатов трассировки стека, но ме¬тод printStackTrace() по умолчанию не создает String. Для получения String необ¬ходимо использовать перегруженную версию printStackTrace() с аргументом java.io.PrintWriter (за подробными объяснениями обращайтесь к главе «Ввод/вы¬вод»). Если передать конструктору PrintWriter объект java.io. StringWriter, для по¬лучения вывода в формате String достаточно вызвать toString().
Подход LoggingException чрезвычайно удобен (вся инфраструктура регистра¬ции встроена в само исключение, и все работает автоматически без вмешательст¬ва со стороны клиента), однако на практике чаще применяется перехват и реги¬страция «сторонних» исключений, поэтому сообщение должно генерироваться в обработчике исключения:
//: exceptions/LoggingExceptions2.java // Регистрация перехваченных исключений, import java.util.logging.*; import java.io.*:
public class LoggingExceptions2 { private static Logger logger =
Logger.getLogger("Loggi ngExcepti ons2"): static void logException(Exception e) {
StringWriter trace = new StringWriter(); e.printStackTrace(new PrintWriter(trace)): 1ogger.severe(trace.toStri ng());
}
public static void main(String[] args) { try {
throw new NullPointerException(); } catch(NullPointerException e) { logException(e):
}
}
} /* Output: (90* match)
Aug 30, 2005 4:07:54 PM LoggingExceptions2 logException
SEVERE: java.lang.NullPointerException продолжение &
at LoggingExceptions2 main(LoggingExceptions2 java:16)
*///.-
На этом процесс создания собственных исключений не заканчивается — ис¬ключение можно снабдить дополнительными конструкторами и элементами:
//: exceptions/ExtraFeatures.java // Дальнейшее расширение классов исключений, import static net.mindview.util.Print.*:
class MyException2 extends Exception { private int x; public MyException2() {} public MyException2(String msg) { super(msg): } public MyException2(String msg, int x) { super(msg): this.x = x:
}
public int valО { return x: } public String getMessageO {
return "Подробное сообщение: "+ x + " " + super getMessageO:

public class ExtraFeatures {
public static void f() throws MyException2 { print("MyException2 в f()"), throw new MyException2():
}
public static void g() throws MyException2 {
System out.println("MyException2 в g()"); throw new MyException2("Возбуждено в g()");
}
public static void h() throws MyException2 {
System out.println("MyException2 в h()"): throw new MyException2("Возбуждено в h()", 47);
}
public static void main(String[] args) { try {
f():
} catch(MyException2 e) {
e.printStackTrace(System.out);
}
try {
g():
} catch(MyException2 e) {
e.printStackTrace(System out):
}
try {
h():
} catch(MyException2 e) {
e.printStackTrace(System.out):
System out.printlnC'e.valO = " + e.valO):
}
}
} /* Output: MyException2 в f()
MyException2: Подробное сообщение: 0 null
at ExtraFeatures.f(ExtraFeatures.java•22)
at ExtraFeatures.main(ExtraFeatures.java.34) MyException2 в g()
MyException2: Подробное сообщение: 0 Возбуждено в g()
at ExtraFeatures.g(ExtraFeatures.java:26) at ExtraFeatures.main(ExtraFeatures.java:39) MyException2: Подробное сообщение: 47 Возбуждено в h() at ExtraFeatures.h(ExtraFeatures.java:30) at ExtraFeatures.main(ExtraFeatures.java:44)
e.valO = 47 *///:-
Было добавлено поле данных х вместе с методом, считывающим его значе¬ние, а также дополнительный конструктор для инициализации х. Переопреде¬ленный метод Throwable.getMessage() выводит более содержательную информа¬цию об исключении. Метод getMessage() для классов исключений — аналог toStringO в обычных классах.
Так как исключение является просто видом объекта, расширение возможно¬стей классов исключений можно продолжить. Однако следует помнить, что все эти программисты, использующие ваши библиотеки, могут попросту проигно¬рировать все «украшения» — нередко программисты ограничиваются провер¬кой типа исключения (как чаще всего бывает со стандартными исключениями Java).
Спецификации исключений
В языке 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:

Report Page