56

56


at Rethrowi ng.f(Rethrowi ng.java•7) at Ret h rowi ng. h (Reth rowi ng. j a va: 20) at Rethrowing main(Rethrowing.java-35) main- printStackTraceO java lang.Exception: thrown from f()
at Ret h rowi ng. h (Ret h rowi ng. j a va • 24) at Rethrowi ng.mai n(Rethrowi ng.java:35)
*///:-
Строка с вызовом fiUInStackTrace() становится новой точкой выдачи исклю¬чения.
Выдаваемое исключение также может отличаться от исходного. В этом слу¬чае эффект получается примерно таким же, как при использовании fillln- StackTrace() — информация о месте зарождения исключения теряется, а остает¬ся информация, относящаяся к новой команде throw.
//: exceptions/RethrowNew java // Повторное возбуждение объекта, // отличающегося от первоначального
class OneException extends Exception {
public OneException(String s) { super(s); }
}
class TwoException extends Exception {
public TwoException(String s) { super(s), }
}
public class RethrowNew {
public static void f() throws OneException {
System.out printin("создание исключения в f(D; throw new OneException("из f()");
}
public static void main(String[] args) { try {
try {
f().
} catch(OneException e) {
System out.printin(
"Во внутреннем блоке try.
e printStackTraceO"). .
e.printStackTrace(System.out);
throw new TwoException("из внутреннего блока try"),
}
} catch(TwoException e) {
System.out.printin(
"Во внешнем блоке try, e.printStackTraceO"), e.printStackTrace(System.out),
}
}
создание исключения в f()
Во внутреннем блоке try, е.printStackTraceO
OneException- thrown from fO
at RethrowNew.f(RethrowNew.java•15) at Reth rowNew.ma i n(Reth rowNew.j ava•20) Во внешнем блоке try, e.printStackTraceO TwoException из внутреннего блока try
at RethrowNew main(RethrowNew.java 25)
*///.-
О последнем исключении известно только то, что оно поступило из внутрен¬него блока try, но не из метода f().
Вам никогда не придется заботиться об удалении предыдущих исключений, и исключений вообще. Все они являются объектами, созданными в общей куче оператором new, и сборщик мусора уничтожает их автоматически.
Цепочки исключений
Зачастую необходимо перехватить одно исключение и возбудить следующее, не потеряв при этом информации о первом исключении — это называется цепоч¬кой исключений (exception chaining). До выпуска пакета JDK 1.4 программистам приходилось самостоятельно писать код, сохраняющий информацию о преды¬дущем исключении, однако теперь конструкторам всех подклассов Throwable может передаваться объект-причина (cause). Предполагается, что причиной яв¬ляется изначальное исключение и передача ее в новый объект обеспечивает трассировку стека вплоть до самого его начала, хотя при этом создается и воз¬буждается новое исключение.
Интересно отметить, что единственными подклассами класса Throwable, при¬нимающими объект-причину в качестве аргумента конструктора, являются три основополагающих класса исключений: Error (используется виртуальной маши¬ной (JVM) для сообщений о системных ошибках), Exception и RuntimeException. Для организации цепочек из других типов исключений придется использовать метод initCause(), а не конструктор.
Следующий пример демонстрирует динамическое добавление полей в объ¬ект DynamicFields во время работы программы:
//. exceptions/DynamicFields.java // Динамическое добавление полей в класс. // Пример использования цепочки исключений, import static net mindview.util Print *;
class DynamicFieldsException extends Exception {}
public class DynamicFields { private Object[][] fields; public DynamicFields(int initialSize) {
fields = new 0bject[initialSize][2]. for(int i = 0. i < initialSize. i++)
fields[i] = new Object[] { null, null };
}
public String toStringO {
StringBuilder result = new StringBuilderO. . Л
продолжение &
for(Objected obj : fields) {
result.append(obj[0]); result.append("• "); result.append(obj[l]); result.append("\n");
}
return result.toStringO;
}
private int hasField(String id) {
for(int i = 0; i < fields.length; i++) if(id.equals(fields[1][0])) return i;
return -1:
}
private int
getFieldNumber(String id) throws NoSuchFieldException { int fieldNum = hasField(id); if(fieldNum == -1)
throw new NoSuchFieldException0; return fieldNum;
}
private int makeField(String id) {
for(int i = 0; i < fields.length; i++) 1f(f1elds[i][0] == null) { fields[1][0] « id; return i;
}
// Пустых полей нет. Добавим новое:
Object[][]tmp = new Object[fields.length + 1][2];
for(int i = 0; i < fields.length; i++)
tmp[i] = fields[i]; for(int i = fields.length; i < tmp.length; i++) tmp[i] = new Object[] { null, null }; fields = tmp;
// Рекурсивный вызов с новыми полями: return makeField(id);
}
public Object
getField(String id) throws NoSuchFieldException { return fields[getFieldNumber(id)][l];
}
public Object setField(String id. Object value)
throws DynamicFieldsException { if(value == null) {
// У большинства исключений нет конструктора.
// принимающего объект-«причину».
// В таких случаях следует использовать
// метод initCauseO, доступный всем подклассам
// класса Throwable.
DynamicFieldsException dfe =
new DynamicFieldsExceptionO; dfe.i ni tCause(new Nul1Poi nterExcepti on О); throw dfe;
}
int fieldNumber = hasField(id); if(fieldNumber == -1)
fieldNumber = makeField(id); Object result = null;
try {
result = getField(id). 11 Получаем старое значение } catchCNoSuchFieldException e) {
// Используем конструктор с «причиной» throw new RuntimeException(e),
}
fields[fieldNumber][l] = value; return result;
}
public static void main(String[] args) {
DynamicFields df = new DynamicFields(3); print(df); try {
df setFieldC'd". "Значение d"); df setField("число", 47); df.setField("4Haio2\ 48); print(df);
df.setFieldC'd". "Новое значение d"), df setField("4HOio3". 11). printCdf: " + df).
printCdf getField(\"d\")) " + df getFieldCd")); Object field = df setFieldC'd". null). // Исключение } catch(NoSuchFieldException e) {
e printStackTrace(System out); } catch(DynamicFieldsException e) {
e.printStackTrace(System.out);
}
}
} /* Output: null null null: null' null: null d: Значение d число: 47 число2: 48
df- d- Новое значение d число: 47 число:2 48 числоЗ- 11
Значение df .getFieldCd") . Новое значение d Dynami cFi eldsExcepti on
at DynamicFields.setField(DynamicFields.java:64) at DynamicFields main(DynamicFields java:94) Caused by: java.lang.NullPointerException
at DynamicFields.setField(DynamicFields.java 66) )
Каждый объект DynamicFields содержит массив пар Object-Object. Первый объ¬ект содержит идентификатор поля (String), а второй объект — значение поля, которое может быть любого типа, кроме неупакованных примитивов. При соз¬дании объекта необходимо оценить примерное количество полей. Метод setField() либо находит уже существующее поле с заданным именем, либо создает новое поле и сохраняет значение. Когда пространство для полей заканчивается, метод наращивает его, создавая массив размером на единицу больше и копи¬руя в него старые элементы. При попытке размещения пустой ссылки null ме¬тод инициирует исключение DynamicFieldsException, создавая объект нужного типа и передавая методу initCause() в качестве причины исключение NullPointer- Exception.
Для возвращаемого значения метод setField() использует старое значение поля, получая его методом getField(), который может возбудить исключение NoSuchFieldException. Если метод getField() вызывает программист-клиент, то он ответственен за обработку возможного исключения NoSuchFieldException, однако, если последнее возникает в методе setField(), это является ошибкой программы; соответственно, полученное исключение преобразуется в исключение Runtime- Exception с помощью конструктора, принимающего аргумент-причину.
Для создания результата toStringO использует объект StringBuilder. Этот класс будет подробно рассмотрен при описании работы со строками.
Стандартные исключения Java
Класс Java Throwable описывает все объекты, которые могут возбуждаться как исключения. Существует две основные разновидности объектов Throwable (то есть ветви наследования). Тип Error представляет системные ошибки и ошибки времени компиляции, которые обычно не перехватываются (кроме нескольких особых случаев). Тип Exception может быть возбужден из любого метода стандартной библиотеки классов Java или пользовательского метода в случае неполадок при исполнении программы. Таким образом, для програм¬мистов интерес представляет прежде всего тип Exception.
Лучший способ получить представление об исключениях — просмотреть до¬кументацию JDK. Стоит сделать это хотя бы раз, чтобы получить представле¬ние о различных видах исключений, но вскоре вы убедитесь в том, что наиболее принципиальным различием между разными исключениями являются их имена. К тому же количество исключений в Java постоянно растет, и едва ли имеет смысл описывать их в книге. Любая программная библиотека от стороннего про¬изводителя, скорее всего, также будет иметь собственный набор исключений. Здесь важнее понять принцип работы и поступать с исключениями сообразно.
Основной принцип заключается в том, что имя исключения относительно полно объясняет суть возникшей проблемы. Не все исключения определены в пакете java.lang, некоторые из них созданы для поддержки других библиотек, таких как util, net и io, как можно видеть из полных имен их классов или из ба¬зовых классов. Например, все исключения, связанные с вводом/выводом (I/O), унаследованы от java.io.IOException.
Особый случай: RuntimeException
Вспомним первый пример в этой главе:
if(t == null)
throw new NullPointerExceptionO;
Только представьте, как ужасно было бы проверять таким образом каждую ссылку, переданную вашему методу. К счастью, делать это не нужно — такая проверка автоматически выполняется во время исполнения Java-программы, и при попытке использования null-ссылок автоматически возбуждается Null- PointerException. Таким образом, использованная в примере конструкция избы¬точна.
Есть целая группа исключений, принадлежащих к этой категории. Они все¬гда возбуждаются в Java автоматически, и вам не придется включать их в спе¬цификацию исключений. Все они унаследованы от одного базового класса RuntimeException, что дает нам идеальный пример наследования: семейство классов, имеющих общие характеристики и поведение. Вам также не придется создавать спецификацию исключений, указывающую на возбуждение методом RuntimeException (или любого унаследованного от него исключения), так как эти исключения относятся к неконтролируемым (unchecked). Такие исключения означают ошибки в программе, и фактически вам никогда не придется перехва¬тывать их — это делается автоматически. Проверка RuntimeException привела бы к излишнему загромождению программы. И хотя вам обычно не требуется пе¬рехватывать RuntimeException, возможно, вы посчитаете нужным возбуждать не¬которые из них в своих собственных библиотеках программ.
Что же происходит, когда подобные исключения не перехватываются? Так как компилятор не заставляет перечислять такие исключения в спецификациях, можно предположить, что исключение RuntimeException проникнет прямо в ме¬тод mainQ, и не будет перехвачено. Чтобы увидеть все в действии, испытайте следующий цример:

Report Page