Создание ваших собственных исключений
Вы не ограничены в использовании существующих Java исключений. Это очень важно, потому что часто вам будет нужно создавать свои собственные исключения, чтобы объявить специальную ошибку, которую способна создавать ваша библиотека, но это не могли предвидеть, когда создавалась иерархия исключений Java.
Для создания вашего собственного класса исключения вы обязаны наследовать его от исключения существующего типа, предпочтительно от того, которое наиболее близко подходит для вашего нового исключения (однако, часто это невозможно). Наиболее простой способ создать новый тип исключения - это просто создать конструктор по умолчанию для вас, так чтобы он совсем не требовал кода:
//: c10:SimpleExceptionDemo.java
// Наследование вашего собственного исключения.
class SimpleException extends Exception {}
public class SimpleExceptionDemo { public void f() throws SimpleException { System.out.println( "Throwing SimpleException from f()"); throw new SimpleException (); } public static void main(String[] args) { SimpleExceptionDemo sed = new SimpleExceptionDemo(); try { sed.f(); } catch(SimpleException e) { System.err.println("Caught it!"); } } } ///:~
Когда компилятор создает конструктор по умолчанию, он автоматически (и невидимо) вызывает конструктор по умолчанию базового класса. Конечно, в этом случае у вас нет конструктора SimpleException(String), но на практике он не используется часто. Как вы увидите, наиболее важная вещь в использовании исключений - это имя класса, так что чаще всего подходят такие исключения, как показаны выше.
Вот результат, который печатается на консоль стандартной ошибки - поток для записи в System.err. Чаще всего это лучшее место для направления информации об ошибках, чем System.out, который может быть перенаправлен. Если вы посылаете вывод в System.err, он не может быть перенаправлен, в отличие от System.out, так что пользователю легче заметить его.
Создание класса исключения, который также имеет конструктор, принимающий String, также достаточно просто:
//: c10:FullConstructors.java
// Наследование вашего собственного исключения.
class MyException extends Exception { public MyException() {} public MyException(String msg) { super(msg); } }
public class FullConstructors { public static void f() throws MyException { System.out.println( "Throwing MyException from f()"); throw new MyException(); } public static void g() throws MyException { System.out.println( "Throwing MyException from g()"); throw new MyException("Originated in g()"); } public static void main(String[] args) { try { f(); } catch(MyException e) { e.printStackTrace(System.err); } try { g(); } catch(MyException e) { e.printStackTrace(System.err); } } } ///:~
Дополнительный код достаточно мал — добавлено два конструктора, которые определяют способы создания MyException. Во втором конструкторе явно вызывается конструктор базового класса с аргументом String с помощью использования ключевого слова super.
Информация трассировки направляется в System.err, так как это лучше, поскольку она будет выводиться, даже если System.out будет перенаправлен.
Программа выводит следующее:
Throwing MyException from f() MyException at FullConstructors.f(FullConstructors.java:16) at FullConstructors.main(FullConstructors.java:24) Throwing MyException from g() MyException: Originated in g() at FullConstructors.g(FullConstructors.java:20) at FullConstructors.main(FullConstructors.java:29)
Вы можете увидеть недостаток деталей в этих сообщениях MyException, выбрасываемых из f( ).
Процесс создания вашего собственного исключения может быть развит больше. Вы можете добавить дополнительные конструкторы и члены:
//: c10:ExtraFeatures.java
// Дальнейшее украшение класса исключения.
class MyException2 extends Exception { public MyException2() {} public MyException2(String msg) { super(msg); } public MyException2(String msg, int x) { super(msg); i = x; } public int val() { return i; } private int i; }
public class ExtraFeatures { public static void f() throws MyException2 { System.out.println( "Throwing MyException2 from f()"); throw new MyException2(); } public static void g() throws MyException2 { System.out.println( "Throwing MyException2 from g()"); throw new MyException2("Originated in g()"); } public static void h() throws MyException2 { System.out.println( "Throwing MyException2 from h()"); throw new MyException2( "Originated in h()", 47); } public static void main(String[] args) { try { f(); } catch(MyException2 e) { e.printStackTrace(System.err); } try { g(); } catch(MyException2 e) { e.printStackTrace(System.err); } try { h(); } catch(MyException2 e) { e.printStackTrace(System.err); System.err.println("e.val() = " + e.val()); } } } ///:~
Бал добавлен член - данные i, вместе с методами, которые читают его значение и дополнительные конструкторы, которые устанавливают его. Вод результат работы:
Throwing MyException2 from f() MyException2 at ExtraFeatures.f(ExtraFeatures.java:22) at ExtraFeatures.main(ExtraFeatures.java:34) Throwing MyException2 from g() MyException2: Originated in g() at ExtraFeatures.g(ExtraFeatures.java:26) at ExtraFeatures.main(ExtraFeatures.java:39) Throwing MyException2 from h() MyException2: Originated in h() at ExtraFeatures.h(ExtraFeatures.java:30) at ExtraFeatures.main(ExtraFeatures.java:44) e.val() = 47
Так как исключение является просто еще одним видом объекта, вы можете продолжать этот процесс наращивания мощность ваших классов исключений. Однако запомните, что все это украшение может быть потеряно для клиентского программиста, использующего ваш пакет, так как он может просто взглянуть на выбрасываемое исключение и ничего более. (Это способ чаще всего используется в библиотеке исключений Java.)