Философия Java3
Шрифт:
Метод dispose должен вызываться пользователем при завершении работы с объектом InputFile. Он освобождает системные ресурсы (такие, как открытые файлы), закрепленные за объектами BufferedReader и (или) FileReader. Делать это следует только тогда, когда работа с объектом InputFile действительно будет завершена. Казалось бы, подобные действия удобно разместить в методе fina-lize, но, как упоминалось в главе 5, вызов этого метода не гарантирован (и даже если вы знаете, что он будет вызван, то неизвестно,
Самый безопасный способ использования класса, который способен выдать исключение при конструировании и требует завершающих действий, основан на использовании вложенных блоков try:
//: exceptions/Cleanup.java
// Гарантированное освобождение ресурсов.
public class Cleanup {
public static void main(String[] args) { try {
InputFile in = new InputFileC'Cleanup java"); try {
String s; int i = 1;
whileC(s = in getLineO) != null) ; // Построчная обработка .. } catch(Exception e) {
System.out.println("Перехвачено Exception в main"). e.printStackTrace(System.out); } finally {
in.disposeO;
}
} catch(Exception e) {
System out println("Сбой при конструировании InputFile"):
}
}
} /* Output:
disposeO успешен
*///:-
Присмотритесь к логике происходящего: конструирование объекта InputFile фактически заключено в собственный блок try. Если попытка завершается неудачей, мы входим во внешнюю секцию catch и метод dispose не вызывается. Но, если конструирование прошло успешно, мы хотим обеспечить гарантированное завершение, поэтому сразу же после конструирования создается новый блок try. Блок finally, выполняющий завершение, связывается с внутренним блоком try; таким образом, блок finally не выполняется при неудачном конструировании и всегда выполняется, если конструирование прошло удачно.
Эта универсальная идиома применяется и в тех ситуациях, когда конструктор не выдает исключений. Основной принцип: сразу же после создания объекта, требующего завершения, начинается конструкция try-finally:
//: exceptions/Cleanupldiom java
// За каждым освобождаемым объектом следует try-finally
class NeedsCleanup { // Конструирование не может завершиться неудачно private static long counter = 1,
private final long id = counter++, Л
продолжение &
pub.lic void disposeO {
System out printin("NeedsCleanup " + id + " завершен");
class ConstructionException extends Exception {}
class NeedsCleanup2 extends NeedsCleanup { // Возможны сбои при конструировании, public NeedsCleanup2 throws ConstructionException {}
public class Cleanupldiom {
public static void main(String[] args) { // Секция 1-
NeedsCleanup ncl = new NeedsCleanupO; try {
// .. } finally {
ncl.disposeO.
//
// Если сбои при конструировании исключены, // объекты можно группировать. NeedsCleanup nc2 = new NeedsCleanupO; NeedsCleanup псЗ = new NeedsCleanupO; try {
// .. } finally {
nc3 disposeO; // Обратный порядок конструирования nc2.disposeO;
// Секция 3-
// Если при конструировании возможны сбои, каждый объект // защищается отдельно; try {
NeedsCleanup2 nc4 = new NeedsCleanup20; try {
NeedsCleanup2 nc5 = new NeedsCleanup2; try {
// ...
} finally {
nc5.disposeO;
}
} catch(ConstructionException e) { // Конструктор nc5
System.out.println(e), } finally {
nc4 disposeO;
}
} catch(ConstructionException e) { // Конструктор nc4 System.out.println(e);
}
}
} /* Output; NeedsCleanup 1 завершен NeedsCleanup 3 завершен
Идентификация исключений 343
NeedsCleanup 2 завершен NeedsCleanup 5 завершен NeedsCleanup 4 завершен */// ~
Секция 1 метода main весьма прямолинейна: за созданием завершаемого объекта следует try-finally. Если конструирование не может завершиться неудачей, наличие catch не требуется. В секции 2 мы видим, что конструкторы, которые не могут завершиться неудачей, могут группироваться как для конструирования, так и для завершения.
Секция 3 показывает, как поступать с объектами, при конструировании которых возможны сбои и которые нуждаются в завершении. Здесь программа усложняется, потому что каждое конструирование должно заключаться в отдельную копию try-catch и за ним должна следовать конструкция try-finally, обеспечивающая завершение.
Неудобства обработки исключения в подобных случаях — веский аргумент в пользу создания конструкторов, выполнение которых заведомо обходится без сбоев (хотя это и не всегда возможно).
Идентификация исключений
Механизм обработки исключений ищет в списке «ближайший» подходящий обработчик в порядке их следования. Когда соответствие обнаруживается, исключение считается найденным и дальнейшего поиска не происходит.
Идентификация исключений не требует обязательного соответствия между исключением и обработчиком. Объект порожденного класса подойдет и для обработчика, изначально написанного для базового класса:
//: exceptions/Human.java // Перехват иерархии исключений.
class Annoyance extends Exception {} class Sneeze extends Annoyance {}
public class Human {
public static void main(String[] args) { // Перехват точного типа try {
throw new SneezeO; } catch(Sneeze s) {
System out println("Перехвачено Sneeze"). } catch(Annoyance a) {
System 0ut.println("nepexBa4eH0 Annoyance"),
}
// Перехват базового типа try {
throw new SneezeO. } catch(Annoyance a) {
System out рпп^пС'Перехвачено Annoyance").