Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык программирования JAVA.pdf
Скачиваний:
373
Добавлен:
02.05.2014
Размер:
2.57 Mб
Скачать

converted to PDF by BoJIoc

оператора try. Разумеется, возникающие в них исключения могут быть обработаны любым внешним блоком try, для которого внутренние catch или finally являются вложенными.

7.4.1. Условие finally

Условие finally оператора try позволяет выполнить некоторый фрагмент программы независимо от того, произошло исключение или нет. Обычно работа такого фрагмента сводится к чисткевнутреннего состояния объекта или освобождению необъектныхресурсов (например, открытых файлов), хранящихся в локальных переменных. Приведем пример метода, который закрывает файл после завершения своей работы даже в случае возникновения ошибки:

public boolean searchFor(String file, String word) throws StreamException

{

Stream input = null; try {

input = new Stream(file); while (!input.eof())

if (input.next() == word) return true;

return false;

// поиск завершился неудачно

} finally {

 

if (input != null)

 

input.close();

 

}

 

}

 

Если создание объекта оператором new закончится неудачно, то input сохранит свое исходное значение null. Если же выполнение new будет успешным, то input будет содержать ссылку на объект, соответствующий открытому файлу. Во время выполнения условия finally поток input будет закрываться лишь в том случае, если он предварительно был открыт. Независимо от того, возникло ли исключение при работе с потоком или нет, условие finally обеспечивает закрытие файла; благодаря этому экономится такой ограниченный ресурс, как количество одновременно открытых файлов. Метод searchFor объявляет о том, что он возбуждает StreamException, чтобы все порожденные в нем

исключения после выполнения завершающих действий передавались в вызывающий фрагмент программы.

Условие finally может также использоваться и для выполнения завершающих действий после операторов break, continue и return — вот почему иногда можно встретить try без соответствующих ему catch. При обработке любого оператора, передающего управление программы в другую точку, выполняются все условия finally. Невозможно покинуть try- блок без выполнения его условия finally.

В приведенном выше примере finally используется и для выполнения завершающих действий в случае нормального возврата по оператору return. Один из самых распространенных случаев использования goto в других языках необходимость выполнения определенных действий при завершении программного блока, как успешном, так и аварийном. В нашем примере finally обеспечивает закрытие файла и при выполнении оператора return, и при возбуждении исключения.

У условия finally всегда имеется некоторая причина. Она может состоять в нормальном завершении блока try, или в выполнении управляющего оператора наподобие return, или же в возбуждении исключения во фрагменте, заключенном в try-блок. Эта причина запоминается и при выходе из блока finally. Тем не менее, если в блоке finally возникает новая причина выхода (скажем, выполняется управляющий оператор вроде break или return или возбуждается исключение), то она отменяет старую, и о существовании последней забывается. Например, рассмотрим следующий фрагмент:

converted to PDF by BoJIoc

try {

// ... сделать что-нибудь ...

return 1; } finally {

return 2;

}

Когда выполняется return внутри блока try, то на входе блока finally код возврата равен 1. Однако внутри самого блока finally возвращается значение 2, так что исходный код возврата забывается. В сущности, если бы в блоке try было возбуждено исключение, то код возврата также был бы равен 2. Если бы блок finally не возвращал никакого значения, а просто завершался нормальным образом, то код возврата был бы равен 1.

7.5. Когда применяются исключения

В начале этой главы мы воспользовались выражением неожиданное ошибочное состояние”, чтобы описать момент возбуждения исключения. Исключения не предназначаются для простых, предсказуемых ситуаций. Например, достижение конца входного потока является заранее предсказуемым, так что проверка на исчерпаниепотока является частью ожидаемого поведения метода. Код возврата, сигнализирующий о конце ввода, и проверка его при вызове также выглядят вполне разумно к тому же такую конструкцию оказывается проще понять. Сравним типичный цикл, в котором

используется флаг возврата

while ((token = stream.next()) != Stream.END) process(token);

stream.close();

с другим циклом, в котором о достижении конца ввода сигнализирует исключение:

try {

for (;;) { process(stream.next());

}

} catch (StreamEndException e) { stream.close();

}

В первом случае логика выполнения программы оказывается прямолинейной и понятной. Цикл выполняется до тех пор, пока не будет достигнут конец входного потока, после чего поток закрывается. Во втором случае цикл выглядит так, словно он выполняется бесконечно. Если не знать о том, что о конце ввода сигнализирует StreamEndException, то становится непонятно, когда же происходит выход из цикла. Впрочем, даже если вы догадываетесь о существовании StreamEndException, то факт завершения цикла оказывается вынесенным из его тела (for) во внешний блок try, что затрудняет понимание происходящего.

Встречаются ситуации, в которых невозможно найти приемлемый код возврата. Например, в классе, представляющем поток значений типа double, может содержаться любое допустимое double, поэтому числовой маркер конца потока невозможен. Более разумный подход предусматривает специальный метод eof для проверки конца потока, который должен выполняться перед каждой операцией чтения:

while (!stream.eof()) process(stream.nextDouble());

stream.close();

С другой стороны, попытка чтения после достижения конца файла оказывается непредсказуемой. Она свидетельствует о том, что программа не заметила конца входного потока и теперь пытается сделать что-то такое, чего делать ни в коем случае не следует. Перед нами отличная возможность применить исключение ReadPastEndException.