Операторы try, throw и catch. Темные дни С

Введение

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

Темные дни С

Типичная функция, написанная на С, выглядит примерно так:

Long DoSomething() { long *a, c; FILE *b; a = malloc(sizeof(long) * 10); if (a == NULL) return 1; b = fopen("something.bah", "rb"); if (b == NULL) { free(a); return 2; } fread(a, sizeof(long), 10, b); if (a != 0x10) { free(a); fclose(b); return 3; } fclose(b); c = a; free(a); return c; }

Выглядит не очень, не так ли? Вы целиком и полностью зависите от значений, которые возвращают вам функции и для каждой ошибки вам постоянно нужен код, который ее обрабатывает. Если вы, скажем, в функции работаете хотя бы с 10 указателями (рапределяете память, освобождаете ее и т.д.), то наверняка половину кода функции будет занимать код обработки ошибок. Такая же ситуация будет в коде, вызывающем эту функцию, так как здесь также нужно обработать все возвращаемые коды ошибок.

Try-catch-throw

Давайте же разберем основы обработки исключений в С++. Чтобы комфортно работать с исключениями в С++ вам нужно знать лишь три ключевых слова:

  • try (пытаться) - начало блока исключений;
  • catch (поймать) - начало блока, "ловящего" исключение;
  • throw (бросить) - ключевое слово, "создающее" ("возбуждающее") исключение.

А теперь пример, демонстрирующий, как применить то, что вы узнали:

Void func() { try { throw 1; } catch(int a) { cout << "Caught exception number: " << a << endl; return; } cout << "No exception detected!" << endl; return; }

Если выполнить этот фрагмент кода, то мы получим следующий результат:

Caught exception number: 1

Теперь закоментируйте строку throw 1; и функция выдаст такой результат:

No exception detected!

Как видите все очень просто, но если это применить с умом, такой подход покажется вам очень мощным средством обработки ошибок. Catch может "ловить" любой тип данных, так же как и throw может "кинуть" данные любого типа. Т.е. throw AnyClass(); будет правильно работать, так же как и catch (AnyClass &d) {};.

Как уже было сказано, catch может "ловить" данные любого типа, но вовсе не обязательно при это указывать переменную. Т.е. прекрасно будет работать что-нибудь типа этого:

Catch(dumbclass) { }

так же, как и

Catch(dumbclass&) { }

Так же можно "поймать" и все исключения:

Catch(...) { }

Троеточие в этом случае показывает, что будут пойманы все исключения. При таком подходе нельзя указать имя переменной. В случае, если "кидаются" данные нестандартного типа (экземпляры определенных вами классов, структур и т.д.), лучше "ловить" их по ссылке, иначе вся "кидаемая" переменная будет скопирована в стек вместо того, чтобы просто передать указатель на нее. Если кидаются данные нескольких типов и вы хотите поймать конкретную переменную (вернее, переменную конкретного типа), то можно использовать несколько блоков catch, ловящих "свой" тип данных:

Try { throw 1; // throw "a"; } catch (long b) { cout << "пойман тип long: " << b << endl; } catch (char b) { cout << "пойман тип char: " << b << endl; }"

Создание" исключений

Когда возбуждается исключительная ситуация, программа просматривает стек функций до тех пор, пока не находит соответствующий catch. Если оператор catch не найден, STL будет обрабатывать исключение в стандартном обработчике, который делает все менее изящно, чем могли бы сделать вы, показывая какие-то непонятные (для конечного пользователя) сообщения и обычно аварийно завершая программу.

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

Перегрузка глобальных операторов new/delete

А сейчас хотелось бы отправить вас к статье "Как обнаружить утечку памяти". В ней рассказывается, как обнаружить неправильное управление распределением памяти в вашей программе. Вы можете спросить, при чем тут перегрузка операторов? Если перегрузить стандартные new и delete, то открываются широкие возможности по отслеживанию ошибок (причем ошибок часто критических) с помощью исключений. Например:

Char *a; try { a = new char; } catch (...){ // a не создан - обработать ошибку распределения памяти, // выйти из программы и т.п. } // a успешно создан, продолжаем выполнение

Это, на первый взгляд, кажется длиннее, чем стандартная проверка в С "а равен NULL?", однако если в программе выделяется десяток динамических переменных, то такой метод оправдывает себя.

Операторы throw без параметров

Итак, мы увидели, как новый метод обработки ошибок удобен и прост. Блок try-catch может содержать вложенные блоки try-catch и если не будет определено соответствующего оператора catch на текущем уровен вложения, исключение будет поймано на более высоком уровне. Единственная вещь, о которой вы должны помнить, - это то, что операторы, следующие за throw, никогда не выполнятся.

Try { throw; // ни один оператор, следующий далее (до закрывающей скобки) // выполнен не будет } catch(...) { cout << "Исключение!" << endl; }

Такой метод может применяться в случаях, когда не нужно передавать никаких данных в блок catch.

Приложение

Приведем пример, как все вышеизложенное может быть использовано в конкретном приложении. Преположим, у вас в программе есть класс cMain и экземпляр этого класса Main: class cMain { public: bool Setup(); bool Loop(); // Основной цикл программы void Close(); }; cMain Main;

А в функции main() или WinMain() вы можете использовать этот класс как-нибудь так:

Try { Main.Setup(); Main.Loop(); Main.Close(); } catch (Exception &e) { // использование класса, ведущего лог. log("Exception thrown: %s", e.String()); // Показываем сообщение об ошибке и закрываем приложение. }

Основной цикл программы может выглядеть примерно так:

While (AppActive) { try { // какие-то действия } catch (Exception &e) { /* Если исключение критическое, типа ошибки памяти, посылаем исключение дальше, в main(), оператором throw e; или просто throw. Если исключение некритично, обрабатываем его и возвращаемся в основной цикл. */ } }

Заключение

Метод обработки исключений, приведенный в статье, является удобным и мощным средством, однако только вам решать, использовать его или нет. Одно можно скачать точно - приведенный метод облегчит вам жизнь. Если хотите узнать об исключениях чуть больше, посмотрите публикацию

В программировании часто возникают ситуации, когда программисту необходимо самому инициировать генерацию исключения для указания, напри­мер, на заведомо ошибочный результат выполнения операции, на некорректные значения параметра метода и др. Исключительную ситуацию можно создать с помощью оператора throw , если объект-исключение уже существует, или инициализировать его прямо после этого оператора. Оператор throw используется для генерации исключения. Для этого может быть использован объект класса Throwable или объект его подкласса, а также ссылки на них. Общая форма записи инструкции throw , генерирующей исключение:

throw объектThrowable;

Объект-исключение может уже существовать или создаваться с помощью оператора new :

t hrow new IOException () ;

При достижении оператора throw выполнение кода прекращается. Ближайший блок try проверяется на наличие соответствующего обработчика catch . Если он существует, управление передается ему, иначе проверяется следующий из вложенных операторов try . Инициализация объекта-исключения без оператора throw никакой исключительной ситуации не вызовет.

Ниже приведен пример, в котором сначала создается объект-исключение, затем оператор throw генерирует исключение, обрабатываемое в разделе catch , в котором генерируется другое исключение.

/* пример # 3: генерация исключений: ThrowGeneration . java */

package chapt08;

import java.io.File;

public class ThrowGeneration {

public static void connectFile(File file) {

if (file == null || !file.exists())

throw new IllegalArgumentException();/* генерация

исключения */

//

public static void main(String args) {

File f = new File("demo.txt");

// File f = null;

connectFile (f);

} catch (IllegalArgumentException e) {

System.err .print("обработка unchecked-"

+ " исключения вне метода: " + e);

Вызываемый метод connectFile () может (при отсутствии файла на диске или при аргументе null ) генерировать исключение, перехватываемое обработчиком. В результате этого объект непроверяемого исключения IllegalArgument Exception , как подкласса классаRuntime Exception , передается обработчику исключений в методе main() .

В случае генерации проверяемого исключения компилятор требует обра­ботки исключения в методе или отказа от нее с помощью инструкции throw s .

Если метод генерирует исключение с помощью оператора throw и при этом блок catch в методе отсутствует, то для передачи обработки исключения вызывающему методу тип проверяемого (checked) класса исключений должен быть указан в операторе throws при объявлении метода. Для исключений, являющихся подклассами класса RuntimeException (unchecked) и используемых для отображения программных ошибок, при выполнении приложения throws в объявлении должен отсутствовать.

Ключевое слово finally

Возможна ситуация, при которой нужно выполнить некоторые действия по завершению программы (закрыть поток, освободить соединение с базой данных) вне зависимости от того, произошло исключение или нет. В этом случае используется блок finally , который выполняется после инструкций try или catch . Например:

t ry {/*код, который может вызвать исключение*/ }

catch (Exception1 e1) {/*обработка исключения е1*/ }//необязателен

catch (Exception2 e2) {/*обработка исключения е2*/ }//необязателен

finally {/*выполняется или после try, или после catch */ }

Каждому разделу try должен соответствовать по крайней мере один раздел catch или блок finally . Блок finally часто используется для закрытия файлов и освобождения других ресурсов, захваченных для временного использования в начале выполнения метода. Код блока выполняется перед выходом из метода даже в том случае, если перед ним были выполнены инструкции вида return , break , continue . Приведем пример:

/* пример # 4: выполнение блоков finally: S tudent Finally.java */

package chapt08;

public class StudentFinally {

private static int age;

public static void setAge(int age) {

// реализация

if (age <= 0)

throw new RuntimeException("не бывает");

} finally {

System.out .print("освобождение ресурсов");

// реализация

System.out .print("конец метода");

public static int getAgeWoman() {

return age - 3;

} finally {

return age;

public static void main(String args) {

setAge (23);

setAge (-5);

} catch (RuntimeException e) {

e.printStackTrace();

System.out .print(getAgeWoman ());

В методе setAge () из-за генерации исключения происходит преждевременный выход из блока try , но до выхода из функции выполняется раздел finally . Метод getAgeWoman () завершает работу выполнением стоящего в блоке try оператора return , но и при этом перед выходом из метода выполняется код блока finally .

Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT Фролов Александр Вячеславович

Операторы try, throw и catch

Операторы try, throw и catch

Оператор try открывает блок кода, в котором может произойти ошибка. Если ошибка произошла, то оператор throw вызывает исключение. Исключение обрабатывается специальным обработчиком исключений. Обработчик исключения представляет собой блок кода, который начинается оператором catch.

Допустим ваше приложение должно вычислять значение выражения res = 100 / (num * (num – 7)). Если вы зададите значение переменной num, равное 0 или 7, то произойдет ошибка деления на нуль. Участок программы, в котором может случиться ошибка, объединим в блок оператора try. Вставим перед вычислением выражения проверку переменной nem на равенство нулю и семи. Если переменная num примет запрещенные значения, вызовем исключение, воспользовавшись оператором throw.

Сразу после блока try поместите обработчик исключения catch. Он будет вызываться в случае ошибки.

Пример такой программы, получившей название Exception, мы привели в листинге 1.1. Программа Exception принимает от пользователя значение переменной num, а затем вычисляет выражение res = 100 / (num * (num – 7)) и отображает полученный результат на экране.

В случае, если пользователь введет число 0 или 7, тогда вызывается исключение throw. В качестве параметра оператору throw указывается переменная num. Заметим, что так как переменная num имеет тип long, считается что данное исключение также будет иметь тип long.

После вызова оператора throw управление сразу передается обработчику исключения соответствующего типа. Определенный нами обработчик отображает на экране строку "Exception, num = ", а затем выводит значение переменной num.

После обработки исключения, управление не возвращается в блок try, а передается оператору, следующему после блока catch данного обработчика исключения. Программа выведет на экран строку “Stop program” и завершит свою работу.

Если пользователь введет разрешенные значения для переменной num, тогда исключение не вызывается. Программа вычислит значение res и отобразит его на экране. В этом случае обработчик исключения не выполнится и управление перейдет на оператор, следующий за блоком обработки исключения. Программа выведет на экран строку “Stop program” и завершит работу.

Листинг 1.1. Файл Exception.cpp

#include

// Введите число num

cout << "Input number: ";

cin >> num;

// Блок try, из которого можно вызвать исключение

if ((num == 7) || (num == 0))

// Если переменная num содержит значение 0 или 7,

// тогда вызываем исключение типа float

// Значения num равные 0 или 7 вызовут ошибку

// деления на нуль в следующем выражении

res = 100 / (num * (num – 7));

// Отображаем на экране результат вычисления

cout << "Result = " << res << endl;

// Обработчик исключения типа float

catch(long num) {

cout << "Exception, num = " << num << endl;

cout << "Stop program" << endl;

В определении обработчика сообщения можно не указывать имя переменной и ограничиться только названием типа. Так, обработчик в предыдущем примере может выглядеть следующим образом:

// Отображаем на экране значение переменной num

cout << "Exception, num = " << num << endl;

Из книги Linux для пользователя автора Костромин Виктор Алексеевич

5.3.3 Операторы && и || Операторы и || являются управляющими операторами. Если в командной строке стоит command1 command2, то command2 выполняется в том, и только в том случае, если статус выхода из команды command1 равен нулю, что говорит об успешном ее завершении. Аналогично, если

Из книги Справочное руководство по C++ автора Страустрап Бьярн

5.5.1 Операторы ›, ‹ и ›› Для обозначения перенаправления используются символы "›", "‹" и "››". Чаще всего используется перенаправление вывода команды в файл. Вот соответствующий пример:$ ls -l › /home/jim/dir.txtПо этой команде в файле /home/jim/dir.txt будет сохранен перечень файлов и

Из книги Язык программирования С# 2005 и платформа.NET 2.0. автора Троелсен Эндрю

R.17.6 Операторы оператор: помеченный-оператор оператор-выражение составной-оператор выбирающий-оператор оператор-цикла оператор-перехода оператор-описанияпомеченный-оператор: идентификатор: оператор case выражение-константа: оператор default:

Из книги Delphi. Учимся на примерах автора Парижский Сергей Михайлович

Общие операторы catch В C# также поддерживается "общий" блок catch, который не получает явно объект исключения, генерируемый данным членом.// Блок catch общего вида.static void Main(string args) { … try { for (int i = 0; i ‹ 10; i++) myCar.Accelerate(10); } catch { Console.WriteLine("Случилось что-то ужасное…"); } … }Очевидно,

Из книги Windows Script Host для Windows 2000/XP автора Попов Андрей Владимирович

Операторы Оператор - это конструкция языка Object Pascal, выполняющая определенную операцию. Все операторы можно разбить на несколько

Из книги HTML 5, CSS 3 и Web 2.0. Разработка современных Web-сайтов. автора Дронов Владимир

Операторы отношения и логические операторы Операторы отношения используются для сравнения значений двух переменных. Эти операторы, описанные в табл. П2.11, могут возвращать только логические значения true или false.Таблица П2.11. Операторы отношения Оператор Условие, при

Из книги Язык программирования Си для персонального компьютера автора Бочков C. О.

Операторы Операторов язык JavaScript поддерживает очень много - на все случаи жизни. Их можно разделить на несколько групп. Арифметические операторы Арифметические операторы служат для выполнения арифметических действий над числами. Все арифметические операторы,

Из книги Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ автора Борри Хелен

ОПЕРАТОРЫ Введение Операторы языка Си управляют процессом выполнения программы. Набор операторов языка Си содержит все управляющие конструкции структурного программирования. Ниже представлен полный список операторов:пустой операторсоставной оператор или

Из книги Искусство программирования на языке сценариев командной оболочки автора Купер Мендель

Операторы SQL Оператор SQL используется для выполнения запроса к базе данных. Язык запросов выражается в операторах, которые задают цель: что должно быть сделано (операция), объекты, с которыми это должно быть сделано, и детализация, как это должно быть сделано. По теории

Из книги Язык Си - руководство для начинающих автора Прата Стивен

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

Из книги СПРАВОЧНИК ПО WinAPI автора

8.1. Операторы присваиваниеvariable assignmentИнициализация переменной или изменение ее значения=Универсальный оператор присваивания, пригоден как для сравнения целых чисел, так и для сравнения строк.var=27category=minerals # Пробелы до и после оператора "=" -- недопустимы. Пусть вас не

Из книги Linux и UNIX: программирование в shell. Руководство разработчика. автора Тейнсли Дэвид

Операторы Операторы служат основными строительными блоками программы. Программа состоит из последовательности операторов с добавлением небольшого количества знаков пунктуации. Оператор является законченной инструкцией для компьютера. В языке Си указанием на

Из книги Основы программирования на Java автора Сухов С. А.

Catch Описание: function Catch(var CatchBuf): Integer;Копиpует состояние всех pегистpов системы и указатель команды в CatchBuf.Паpаметpы:CatchBuf: TCatchBuf для копиpования сpеды выполнения.Возвpащаемое значение:Нуль, если сpеда скопиpована.См. также:

Из книги автора

Throw Описание: function Throw(var CatchBuf: TCatchBuf; ThrowBack:: Integer);Восстанавливает сpеду выполнения пpикладной задачи. Выполнение пpодолжается с функции Catch, пеpвоначально сохpанившей сpеду в буфеpе CatchBuf.Паpаметpы:CatchBuf: TCatchBuf, содеpжащая сpеду выполнения.ThrowBack: Значение, возвpащаемое функции

Из книги автора

Из книги автора

4. ОПЕРАТОРЫ Операторы в языке Java - это специальные символы, которые сообщают транслятору о том, что вы хотите выполнить операцию с некоторыми операндами. Типы операций указываются с помощью операторов, а операнды - это переменные, выражения или литералы. Некоторые

Оператор throw используется для возбуждения исключения «вручную». Для того чтобы сделать это, нужно иметь объект подкласса класса Throwable, который можно либо получить как параметр оператора catch, либо создать с помощью оператора new. Ниже приведена общая форма оператораthrow ,

throw ОбъектТипа Throwable ;

При достижении этого оператора нормальное выполнение кода немедленно прекращается, так что следующий за ним оператор не выполняется. Ближайший окружающий блок try проверяется на наличие соответствующего возбужденному исключению обработчика catch. Если такой отыщется, управление передается ему. Если нет, то проверяется следующий из вложенных операторов try и так до тех пор, пока либо не будет найден подходящий раздел catch, либо обработчик исключений исполняющей системы Java не остановит программу, выведя при этом состояние стека вызовов. Ниже приведен пример, в котором сначала создается объект-исключение, затем оператор throw возбуждает исключительную ситуацию, после чего то же исключение возбуждается повторно - на этот раз уже кодом перехватившего его в первый раз раздела catch.

class ThrowDemo {
static void demoproc() {
{
throw new NullPointerException("demo");
}
catch (NullPointerException e) {
System.out.println("caught inside demoproc");
throw e;
}
}

try {
demoproc();
}
catch(NulPointerException e) {
System.out.println("recaught:" + e);
}
}
}

В этом примере обработка исключения проводится в два приема. Метод main создает контекст для исключения и вызывает demoproc. Метод demoproc также устанавливает контекст для обработки исключения, создает новый объект класса NullPointerException и с помощью оператора throw возбуждает это исключение. Исключение перехватывается в следующей строке внутри метода demoproc, причем объект-исключение доступен коду обработчика через параметр «е». Код обработчика выводит сообщение о том, что возбуждено исключение, а затем снова возбуждает его с помощью оператора throw, в результате чего оно передается обработчику исключений в методе main. Ниже приведен результат, полученный при запуске этого примера.

С:\> java ThrowDemo
caught inside demoproc
recaught: java.lang.NullPointerException: demo

9.8. Оператор throws

Если метод способен возбуждать исключения, которые он сам не обрабатывает, он должен объявить о таком поведении, чтобы вызывающие методы могли защитить себя от этих исключений. Для задания списка исключений, которые могут возбуждаться методом, используется оператор throws. Если метод в явном виде (т.е. с помощью оператора throw) возбуждает исключение соответствующего класса, тип класса исключений должен быть указан в операторе throws в объявлении этого метода. С учетом этого наш прежний синтаксис определения метода должен быть расширен следующим образом:

тип имя_метода(список аргументов) throws список исюпочений {}

Ниже приведен пример программы, в которой метод procedure пытается возбудить исключение, не обеспечивая ни программного кода для его перехвата, ни объявления этого исключения в заголовке метода. Такой программный код не будет оттранслирован.

class ThrowsDemo 1 {
static void procedure ()
{
System.out.println("inside procedure");
}
public static void main(String args) {
procedure ();
}
}

Для того чтобы мы смогли оттранслировать этот пример, нам придется сообщить транслятору, что procedure может возбуждать исключения типа IllegalAccessException и в методе main добавить код для обработки этого типа исключений:

class ThrowsDemo {
static void procedure() throws IllegalAccessException
{
System.out.println(" inside procedure");
throw new IllegalAccessException("demo");
}
public static void main(String aigs) {
try {
procedure();
}
catch (IllegalAccessException e) {
System.out.println("caught" + e);
}
}
}

Ниже приведен результат выполнения этой программы.

С :\> java ThrowsDemo
inside procedure
caught javaJangIllegalAccessException: demo

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

Ключевые слова исключений try, catch, throw, throws, finally

Механизм исключительных ситуаций в Java поддерживается пятью ключевыми словами:

  • catch,
  • throw,
  • throws,
  • finally.

Ниже приведена общая форма блока обработки исключений.

Try { // блок кода } catch ( е) { // обработчик исключений типа ExceptionType1 } catch ( е) { // обработчик исключений типа ExceptionType2 } finally { // ... }

Типы исключений

В вершине иерархии исключений стоит класс Throwable , который наследуется от Object . Каждый из типов исключений является подклассом Throwable. Два непосредственных наследника класса Throwable делят иерархию подклассов исключений на две различные ветви. Иерархия классов представлена на рисунке.

Класс Ехception используется для описания исключительных ситуации, которые должны перехватываться программным кодом пользователя. Класс Error предназначен для описания исключительных ситуаций, которые при обычных условиях не должны перехватываться в пользовательской программе.

Неперехваченные исключения

Объекты-исключения автоматически создаются исполняющей средой Java в результате возникновения определенных исключительных ситуаций. Пример программы, в которой создаем исключительную ситуацию при делении на нуль.

Package samples; class TestException { public static void main(String args) { int d = 0; int a = 42 / d; System.out.println ("a = " + a); } }

В консоль будет выведено следующее сообщение.

Exception in thread "main" java.lang.ArithmeticException: / by zero at samples.TestException.main(TestException.java:8)

Следует обратить внимание на тот факт, что типом возбужденного исключения был не Exception и не Throwable . Это подкласс класса Exception , а именно: ArithmeticException , поясняющий, какая ошибка возникла при выполнении программы.

Изменим класс добавлением статического метода subroutine , в котором создадим такую же исключительную ситуацию.

Package samples; public class TestException { static void subroutine() { int d = 0; int a = 10 / d; System.out.println ("a = " + a); } public static void main(String args) { TestException.subroutine(); } }

Сообщение выполнения программы показывает, как обработчик исключений исполняющей системы Java выводит содержимое всего стека вызовов.

Exception in thread "main" java.lang.ArithmeticException: / by zero at samples.TestException.subroutine(TestException.java:8) at samples.TestException.main(TestException.java:14)

Перехват исключений try/catch

Для защиты программного кода от исключений необходимо использовать связанные блоки с ключевыми словами try catch ; catch помещается сразу же после try-блока. В блоке catch задается тип исключения, которое необходимо обработать.

Class TestException { public static void main(String args) { try { int d = 0; int a = 42 / d; } catch (ArithmeticException e) { System.out.println("division by zero"); } } }

Целью большинства хорошо сконструированных catch -разделов должна быть обработка возникшей исключительной ситуации и приведение переменных программы в некоторое разумное состояние - такое, чтобы программу можно было продолжить так, будто никакой ошибки и не было (в нашем примере выводится предупреждение - division by zero).

Несколько разделов catch

В отдельных случаях блок программного кода может вызвать исключения различных типов. Для того, чтобы локализовать обработку подобных ситуаций, можно использовать несколько catch -разделов для одного try-блока . Блоки наиболее специализированных классов исключений должны идти первыми, поскольку ни один подкласс не будет достигнут, если поставить его после суперкласса.

В следующем примере перехватывается два различных типа исключений, причем за этими двумя специализированными обработчиками следует раздел catch общего назначения, перехватывающий все подклассы класса Throwable .

Class MultiCatch { static int c = { 1 }; public static void main(String args) { try { int a = args.length; System.out.println("a = " + String.valueOf(a)); int b = 23 / a; c = 33; } catch (ArithmeticException e) { System.out.println("ArithmeticException: " + e.getMessage()); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("ArrayIndexOutOfBoundsException: " + e.getMessage()); } } }

Данный пример, запущенный без параметров, вызывает возбуждение исключительной ситуации деления на нуль. Если в командной строке будет определен один или несколько параметров, тем самым установив "а" в значение больше нуля, то будет возбуждено исключение выхода индекса за границы массива ArrayIndexOutOfBounds . Ниже приведены результаты работы этой программы, запущенной и тем и другим способом.

А = 0 div by 0: java.lang.ArithmeticException: / by zero a = 1 array index oob: java.lang.ArrayIndexOutOfBoundsException:33

Вложенные операторы try

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

Class MultiNest { static int c = { 1 }; static void checkArray() { try { c = 33; } catch(ArrayIndexOutOfBoundsException e) { System.out.println "ArrayIndexOutOfBoundsException: " + e.getMessage()); } } public static void main(String args) { try { int a = args.length(); System.out.println("a = " + a); int b = 23 / a; checkArray(); } catch (ArithmeticException e) { System.out.println("ArithmeticException: " + e.getMessage()); } } }

Возбуждение исключений throw

Программа может явно вызывать исключение, используя оператор throw . После выполнения оператора throw процесс выполнения программы приостанавливается и последующие операторы не выполняются. JVM просматривает ближайший блоки try ... catch , соответствующий типу исключения, для "передачи управления". Если подходящий блок не будет найден, то обработчик исключений остановит программу и "распечатает" при этом состояние стека вызовов.

Пример исключения, в котором сначала создается объект-исключение, затем оператор throw возбуждает исключительную ситуацию, после чего то же исключение возбуждается повторно - на этот раз уже кодом перехватившего его в первый раз раздела catch .

Class TestThrow { static void method() { try { throw new NullPointerException("Exception in method"); } catch (NullPointerException e) { System.out.println(e.getMessage()); throw e; } } public static void main(String args) { try { method(); } catch(NullPointerException e) { System.out.println("Catch inside main: " + e.getMessage()); } } }

Результат выполнения программы приведен ниже.

Exception in method Catch inside main: Exception in method

Объявление об исключении throws

Если метод может возбуждать исключения, которые сам не обрабатывает, то он должен объявить об этом, чтобы вызывающие его другие методы могли защитить себя от этих исключений. Для задания списка исключений, которые могут возбуждаться методом, используется ключевое слово throws .

Если метод в явном виде (т.е. с помощью оператора throw ) возбуждает исключение, тип класса исключений должен быть указан в операторе throws в объявлении этого метода. Принимая данное положение во внимание синтаксис определения метода должен быть описан следующим образом:

Public class TestThrow { static void method() throws IllegalAccessException { try { System.out.println("inside method"); throw new IllegalAccessException ("Exception in method"); } catch (NullPointerException e) { System.out.println(e.getMessage()); } } public static void main(String args) { try { method(); } catch(IllegalAccessException e) { System.out.println("Catch inside main: " + e.getMessage()); } } }

Результат работы примера:

Inside method Catch inside main: Exception in method

Ключевое слово finally

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

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

Ниже приведен пример класса с двумя методами, завершение которых происходит по разным причинам, но в обоих перед выходом выполняется код раздела finally .

Public class TestFinally { static void methodA() { try { System.out.println("inside methodA"); throw new RuntimeException("Exception in methodA"); } finally { System.out.println("finally inside methodA"); } } static void methodB() { try { System.out.println("inside methodB"); return; } finally { System.out.println("finally inside methodB"); } } public static void main(String args) { try { methodA(); } catch (Exception e) { System.out.println("Catch exception iinside main"); } methodB(); } }

В тестовом примере в методе methodA возбуждается исключение. Но перед преждевременным выходом из блока try , выполняется раздел finally . Во втором методе methodB завершается работа в try -блоке оператором return , но и при этом перед выходом из метода выполняется программный код блока finally . Результат работы тестового примера:

Inside methodA finally inside methodA Catch exception iinside main inside methodB finally inside methodB

Обработка исключений в Java предоставляет исключительно мощный механизм для управления сложными программами. Ключевые слова try, throw, catch позволяют выполнять обработку ошибок и разных нештатных ситуаций в программе.

Наследование исключений

catch - полиморфная конструкция, т.е. catch по типу parent перехватывает исключения любого типа, которые является Parent"ом.

Public class TestException { public static void main(String args) { try { System.err.print("level 0"); throw new RuntimeException(); System.err.print("level 1"); } catch (Exception e) { // catch Exception ПЕРЕХВАТ RuntimeException System.err.print("level 2"); } System.err.println("level 3"); } }

В результате в консоли увидим

Level 0 level 2 level 3

Error и Exception из параллельных веток наследования от Throwable , поэтому catch по одному «брату» не может поймать другого «брата».

Public class TestError { public static void main(String args) { try { System.err.println("level 0"); if (true) { throw new Error(); } System.err.println("level 1"); } catch (Exception e) { System.err.println("level 2"); } System.err.println("level 3"); } }

Результат выполения программы

Level 0 Exception in thread "main" java.lang.Error at TestError.main(TestFinally.java:8)

Множественные исключения

Объявление исключений в методе может быть множественным. Пример:

Import java.io.EOFException; import java.io.FileNotFoundException; public class MultiException { // объявляем исключения public static void main(String args) throws EOFException, FileNotFoundException { if (System.currentTimeMillis() % 2 == 0) { throw new EOFException(); } else { throw new FileNotFoundException(); } } }