Эккель Брюс
Шрифт:
Цепочки исключений
Зачастую необходимо перехватить одно исключение и возбудить следующее, не потеряв при этом информации о первом исключении — это называется цепочкой исключений (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) {