Обработка исключений ( англ. exception handling ) - механизм языков программирования, предназначенный для обработки ошибок времени выполнения и других возможных проблем (исключений), которые могут возникнуть при выполнении программы.
В целом, при возникновении исключительной ситуации, управление передается некоторому заранее назначенному обработчику. В некоторых языках, обработчик может возобновить выполнение программы с места возникновения исключения. Таким образом, обработка ошибок передается на высший уровень и обеспечивается возможность так называемого нелокального выхода, то есть передачи управления на некоторую «удаленную», возможно заранее неизвестную, точку приложения через произвольное число вызовов функций.
Преимущества и недостатки обработки исключений
Исключения предоставляют основные преимущества при разработке отдельных компонентов, когда разработчик компонента не знает, как нужно обработать исключение и оставляет написания обработчика исключения пользователю компонента.
Использование исключений в целях контроля ошибок повышает читаемость кода, так как позволяет отделить обработку ошибок от самого алгоритма, и облегчает программирование и использование компонентов других разработчиков.
Основной недостаток исключений - в их невысокой скорости. В местах программы, критичных по скорости, не стоит нарушать и обрабатывать исключения.
В сложных программах возникают большие «накопления» операторов try... finally и try... catch (try... except), но без применения механизма обработки исключений аналогичная по функциональности программа выглядела бы еще более захламленной.
Поддержка в различных языках обработки исключений
Большинство современных языков программирования, таких как ActionScript, Ada, C + +, Common Lisp, D, Delphi, Eiffel, Java, JavaScript, OBJECTIVE-C, OCaml, Ruby,PHP (с версии 5), Python, SML, Глагол, все языки платформы. NET и т.д., имеют встроенную поддержку обработки исключений. В этих языках, при возникновении исключительной ситуации (точнее, исключения, поддерживаемого языком), происходит раскручивание стека вызовов до первого обработчика исключений соответствующего типа, и управление передается обработчику.
За исключением незначительных различий в синтаксисе, существует лишь пара вариантов обработки исключений. В распространенном из них исключительная ситуация генерируется специальным оператором (throw или raise) с объектом-исключением. При этом, конструирования такого объекта само по себе выброса исключения не вызывает.Область действия обработчиков начинается специальным ключевым словом try или просто языковым маркером начала блока (например, begin ) и заканчивается перед описанием обработчиков ( catch, except, resque ). Обработчиков может быть несколько, один за другим, и каждый может указывать тип исключения, который он обрабатывает.
Некоторые языки также допускают специальный блок ( else ), который выполняется, если ни исключения не згенерувано в соответствующей области действия. Чаще встречается возможность безусловного выполнения кода ( finally, ensure ), даже в случае если исключение было выброшено, но не обработано. Заметным исключением является Си + +, где такой конструкции нет. Вместо нее используется автоматический вызов деструкторов объектов. Вместе с тем существуют нестандартные расширения Си + +, поддерживающие и функциональность finally (например в MFC ).
В целом, обработка исключений может выглядеть следующим образом (в некоторой абстрактной языке):
try {
line = console.readLine();
if (line.length() == 0)
throw new EmptyLineException(" Строка, прочитанный с консоли, пустой!");
console.printLine("Привет, %s!" % line);
}
catch (EmptyLineException exception) {
console.printLine("Привет!");
}
catch (Exception exception) {
console.printLine("Ошибка: " + exception.message());
}
else {
console.printLine(" Программа выполнена без исключительных ситуаций");
}
finally {
console.printLine("Программа завершилась");
}
В некоторых языках может быть только один обработчик, который разбирается с различными типами исключений самостоятельно.
В некоторых языках, например Си или Perl, нет встроенной обработки исключений.
Исключения, которые проверяются
Сначала (например, в C++ ), исключения не были обязательными для обработки. Если какой исключение не обрабатывается, то есть если для него нет обработчика в стеке вызова, или обработчик выбросил исключение заново, то выполнение программы прерывается.
В более новых языках, например в Java, вместе с «классическими» появились исключения, которые проверялись. Обработка таких исключений проверяется компилятором. Метод, в котором оно может возникнуть (в том числе и в методах, вызываемых) обязан или обработать его, либо объявить, что может выкинуть такое исключение.
Преимущества и недостатки
Исключения, которые проверяются, снижают количество ситуаций, когда исключение, которое могло быть обработанным, вызвал критическую ошибку в программе, поскольку при наличии обработчиков следит компилятор. Это может быть особенно полезно, если метод, который не мог выбрасывать исключение типа X стал это делать: компилятор автоматически отследит все случаи его использования и проверит наличие соответствующего обработчика.
Однако, в исключений, проверяемых, есть и недостатки. Во-первых, они часто «заставляют» обрабатывать то, с чем программист в принципе не может справиться, например ошибку ввода-вывода в веб-сервере. Это приводит к появлению «глупых» отделочников, которые не делают ничего или выводят стек вызова исключений и, в итоге, только засоряют код. Во-вторых, это делает невозможным добавление нового исключения, проверяемого в методе, описанном в библиотеке, поскольку это нарушает обратную совместимость. (Это верно и для небиблиотечних методов, но в этом случае проблема менее существенна).
В результате, многие библиотеки объявляют все методы как имеющие выбрасывают некоторое суперкласс исключений (например, Exception). В результате, компилятор «заставляет» писать обработчики исключений даже там, где они, казалось бы, не нужны.