Обработка ошибок и исключения
Содержание
Методы обработки ошибок [ править ]
2. Коды возврата. Основная идея — в случае ошибки возвращать специальное значение, которое не может быть корректным. Например, если в методе есть операция деления, то придется проверять делитель на равенство нулю. Также проверим корректность аргументов a и b :
При вызове метода необходимо проверить возвращаемое значение:
Минусом такого подхода является необходимость проверки возвращаемого значения каждый раз при вызове метода. Кроме того, не всегда возможно определить тип ошибки.
3.Использовать флаг ошибки: при возникновении ошибки устанавливать флаг в соответствующее значение:
Минусы такого подхода аналогичны минусам использования кодов возврата.
4.Можно вызвать метод обработки ошибки и возвращать то, что вернет этот метод.
Но в таком случае не всегда возможно проверить корректность результата вызова основного метода.
5.В случае ошибки просто закрыть программу.
Это приведет к потере данных, также невозможно понять, в каком месте возникла ошибка.
Исключения [ править ]
В Java возможна обработка ошибок с помощью исключений:
Каждый раз, когда при выполнении программы происходит ошибка, создается объект-исключение, содержащий информацию об ошибке, включая её тип и состояние программы на момент возникновения ошибки. После создания исключения среда выполнения пытается найти в стеке вызовов метод, который содержит код, обрабатывающий это исключение. Поиск начинается с метода, в котором произошла ошибка, и проходит через стек в обратном порядке вызова методов. Если не было найдено ни одного подходящего обработчика, выполнение программы завершается.
Таким образом, механизм обработки исключений содержит следующие операции:
Классификация исключений [ править ]
Проверяемые исключения [ править ]
Наследники класса Exception (кроме наслеников RuntimeException ) являются проверяемыми исключениями(checked exception). Как правило, это ошибки, возникшие по вине внешних обстоятельств или пользователя приложения – неправильно указали имя файла, например. Эти исключения должны обрабатываться в ходе работы программы, поэтому компилятор проверяет наличие обработчика или явного описания тех типов исключений, которые могут быть сгенерированы некоторым методом.
Все исключения, кроме классов Error и RuntimeException и их наследников, являются проверяемыми.
Error [ править ]
Класс Error и его подклассы предназначены для системных ошибок. Свои собственные классы-наследники для Error писать (за очень редкими исключениями) не нужно. Как правило, это действительно фатальные ошибки, пытаться обработать которые довольно бессмысленно (например OutOfMemoryError ).
RuntimeException [ править ]
Обработка исключений [ править ]
Как и было сказано раньше, определение метода должно содержать список всех проверяемых исключений, которые метод может бросить. Также можно написать более общий класс, среди наследников которого есть эти исключения.
try-catch-finally [ править ]
Сразу после блока проверки следуют обработчики исключений, которые объявляются ключевым словом catch.
Обработка исключений, вызвавших завершение потока [ править ]
Информация об исключениях [ править ]
Разработка исключений [ править ]
Исключения в Java7 [ править ]
Можно объявлять несколько ресурсов, разделяя их точкой с запятой:
Компилятор Java SE 7 тщательнее анализирует перебрасываемые исключения. Рассмотрим следующий пример:
Примеры исключений [ править ]
Гарантии безопасности [ править ]
При возникновении исключительной ситуации, состояния объектов и программы могут удовлетворять некоторым условиям, которые определяются различными типами гарантий безопасности:
Если будет брошено исключение в этом классе, то тогда гарантируется, что ивариант «левая граница интервала меньше правой» сохранится, но значения left и right могли измениться.
Что такое баги, ворнинги и исключения в программировании
Разбираемся, какие бывают типы ошибок в программировании и как с ними справляться.
Многим известно слово баг (англ. bug — жук), которым называют ошибки в программах. Однако баг — это не совсем ошибка, а скорее неожиданный результат работы. Также есть и другие термины: ворнинг, исключение, утечка.
В этой статье мы на примере C++ разберём, что же значат все эти слова и как эти проблемы влияют на эффективность программы.
Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.
Ошибки в программировании
Словом «ошибка» (англ. error) можно описать любую проблему, но чаще всего под ним подразумевают синтаксическую ошибку — некорректно написанный код, который даже не скомпилируется:
Компилятор тут же скажет, что в коде ошибка и скорее всего не хватает запятой или точки с запятой.
Также существуют ворнинги (англ. warning — предупреждение). Они не являются ошибками, поэтому программа всё равно будет собрана. Вот пример:
Предупреждения не являются чем-то критичным, но могут иметь негативные последствия. Например, ваша программа будет использовать больше памяти, чем должна. Так как C++ нужен в том числе и для разработки высоконагруженных систем, этого допускать нельзя.
После восклицательного знака в треугольнике — количество предупреждений
Третий вид ошибок — ошибки сегментации (англ. segmentation fault, сокр. segfault, жарг. сегфолт). Они возникают, если программа пытается записать что-то в ячейку, недоступную для записи. Например:
Вот результат работы такого кода:
Баги в программах
Мы выяснили, что баг — это не совсем ошибка, а скорее неожиданное поведение программы или результат такого поведения. Баги могут быть чем-то забавным или неприятным. Например, как в играх:
Но они могут привести и к более серьёзным последствиям. Если неправильно спроектировать работу многопоточного приложения, то потоки будут постоянно опережать друг друга. Например, сообщение об ошибке из одного потока может опоздать на миллисекунду, из-за чего второй поток подумает, что никакой ошибки не было, и продолжит работу.
Если ваш код приводит в действие какое-нибудь потенциально опасное устройство, то ценой такой ошибки может быть чья-нибудь жизнь. Такое случилось с кодом для аппарата лучевой терапии Therac-25 — как минимум два человека умерло и ещё больше пострадали из-за превышения дозы радиации.
Исключения в программах
Также во время работы программы могут возникать ситуации, которые мешают корректной работе программы. Например, если вы просите пользователя ввести число, а он вводит строку.
Конвертировать введённое значение не всегда возможно, поэтому функция, которая занимается преобразованием, «выбрасывает» исключение (англ. exception). Это специальное сообщение говорит о том, что что-то идёт не так.
Если разработчик не описывает логику работы программы при вы выбрасывании исключения, то программа аварийно закрывается. Подробнее мы рассказали об этом в статье про ввод и конвертацию в C++.
Одно из самых известных исключений — переполнение стека (англ. stack overflow). В честь него даже назвали сайт, на котором программисты ищут помощь в решении своих проблем.
Компилятор C++ при этом может выдать ошибку сегментации, а не сообщение о переполнении стека:
Вот аналогичный код на языке C#:
Однако сообщение в этот раз более конкретное:
В обоих случаях программа завершается, потому что не может дальше корректно работать.
Похожая ситуация — переполнение буфера (англ. buffer overflow). Она происходит, когда записываемое значение больше выделенной области в памяти.
Обратите внимание, что мы получили предупреждение об арифметическом переполнении (англ. integer overflow):
Тем не менее программа скомпилировалась. Если же такая ситуация возникнет во время вычислений, то мы можем не получить предупреждения.
Арифметическое переполнение стало причиной одной из самых дорогих аварий, произошедших из-за ошибки в коде. В 1996 году ракета-носитель «Ариан-5» взорвалась на 40-й секунде полёта — потери оценивают в 360–500 миллионов долларов.
Как избежать всех этих ошибок
К сожалению, вручную всё это заметить и исправить не получится. Однако существуют различные инструменты и технологии, которые могут помочь.
Один из таких инструментов — отладчик. Он помогает контролировать ход работы программы, чтобы отслеживать разные показатели.
Второй, более эффективный метод — unit-тесты. Они представляют из себя набор описанных ситуаций для каждого компонента программы с указанием ожидаемого поведения.
Например, у вас есть функция sum (int a, int b), которая возвращает сумму двух чисел. Вы можете написать unit-тесты, чтобы проверять следующие ситуации:
Если какой-то из этих тестов не пройден, вы узнаете об этом и сможете всё исправить. Это намного быстрее, чем проверять всё вручную.
Заключение
Ошибок существует слишком много. При этом самые опасные тяжелее обнаружить, что только усугубляет ситуацию.
Если вы хотите научиться писать качественный код и находить в нём ошибки, вы можете записаться на наш курс по разработке на C++.
Когда нужны исключения
Предисловие
Тема Исключений (за и против) не нова, и уже не раз обсуждалась. Но всё же, я надеюсь, что каждый из прочитавших данную статью почерпнёт что-то новое и полезное для себя.
Причиной появления этой публикации стало нежелание больше молчать и просто смотреть, как интернет заполняют «всезнающие» программисты, которые учат новичков в нашей сфере забивать гвозди микроскопом, находя десятки аргументов в защиту своих методов! Потому, эта публикация направлена, скорее, на новичка постигающего программирование и задающего вопросы. Она является моим «правильным ответом»
Итак, что же такое Исключение (Exception)?
Исключение — это то, вероятность (возможность) чего исключается системой… это то что в условиях программы произойти не может.
Посмотрите в свой код. Можете ли вы к каждому исключению дописать «но ведь это невозможно» или «но это же исключено»? Думаю, мало кто сможет честно ответить «да». Если ваш ответ «нет» — значит часть исключений на самом деле не являются таковыми, просто вы использовали этот механизм, потому как вам показалось это более удобным. То самое «удобство» такого подхода будет рассмотрено далее.
Кто же должен определять, какие ситуации программа исключает, а какие нет?
Все исключения могут быть продиктованы только заданием или здравым смыслом, и ничем или никем иным. Исключение не определяется ни типом данных, ни их источником, хотя нередко можно услышать или прочитать в «умных» статьях: «Неправильный клиентский ввод не может быть исключением»… не верьте! Вот, просто, не надо в такое верить.
Вы можете в задании получить вполне разумное исключение неверного клиентского ввода: «С системой будут работать специально обученные менеджеры нашей компании, потому ввод неправильных данных исключён» или «Менеджер будет загружать в систему письма, которые уже прошли проверку правильности формата, поэтому формат всегда будет именно такой». Вот теперь заказчик сам вам сказал, где следует породить исключение в написанной для него программе (и ничего страшного, что это клиентский ввод). Разумеется, если речь всё же идёт о неком продукте, а не библиотеке, программу не надо просто ронять. Пускай на верхнем уровне висит обработчик исключений и превращает все необработанные исключения в ошибки, сохраняет данные и деликатно завершает выполнение (или передаёт управление основному модулю, если ошибка не в нём).
Но, как и обычно, не забываем читать между строк и уточнять задание. Если вас просят сохранить файл, уточните, возможна ли ситуация, что файл не сохранится. А если решили не уточнять, тогда не исключайте такую вероятность, а реализуйте реакцию на это событие. Ваш код не должен ни о чём умалчивать.
Или предусмотрите, или исключите.
Почему любое лишнее Исключение является вредным для кода?
Не раз сталкивался с ситуацией, когда отрицательный результат выполнения функции возвращали в виде исключения, хотя такой результат был предусмотрен самой задачей. На этот счёт слышал разные пояснения:
Заключение
Старался не вдаваться лишние подробности и в каждое предложение вкладывать максимум смысла. Надеюсь у меня это получилось, и читая эти строки, вы думаете: «пять минут, а столько нового!» Ну что же, надеюсь это так.
Напоследок хотелось бы сказать, что не следует изворачиваться под давлением факторов, сроков и «так же проще», и писать неправильный код, зато быстро.
Совершенство и отказоустойчивость нашего кода и определяет наш профессионализм.
Многие программисты почему-то считают, что исключения и ошибки — это одно и то же. Кто-то постоянно кидает exception, кто-то через errorHandler превращает ошибки в исключения. Некоторые пытаются увеличить производительность, используя исключения. Но, на самом деле, exception и ошибки — это совершенно разные механизмы. Не надо одним механизмом заменять другой. Они созданы для разных целей.
Когда появился php5 с исключениями, а затем ZendFramework, который всегда кидает исключения — я не мог понять: чем же exception лучше моего любимого trigger_error()? Долго думал, обсуждал с коллегами и разобрался в этом вопросе. Теперь я чётко знаю, где использовать trigger_error(), а где throw new Exception().
В чём же принципиальная разница между ними?
Ошибки
Ошибки — это то, что нельзя исправить, об этом можно только сообщить: записать в лог, отправить email разработчику и извинится перед пользователем. Например, если мой движок не может подключиться к БД, то это ошибка. Всё. Точка. Без БД сайт не работает, и я не могу с этим ничего сделать. Поэтому я вызываю ales_kaput() и trigger_error(), а мой errorHandler отправит мне email и покажет посетителю сообщение «Извините, сайт не работает».
Exception
Исключения — это не ошибки, это всего лишь особые ситуации, которые нужно как-то обработать. Например, если в калькуляторе вы попробуете разделить на ноль, то калькулятор не зависнет, не будет отсылать сообщения разработчику и извинятся перед вами. Такие ситуации можно обрабатывать обычным if-ом. Строго говоря, исключения — это конструкция языка позволяющая управлять потоком выполнения. Это конструкция, стоящая в одном ряду с if, for и return. И всё. Этот механизм ничем более не является. Только управление потоком.
Их основное предназначение: пробрасывать по каскаду. Покажу это на примере: есть три функции, которые вызывают друг друга каскадом:
Эту задачу можно было бы решить без механизма exception. Например, можно заставить все функции возвращать специальный тип (если ты матёрый пэхапэшник, то должен вспомнить PEAR_Error). Для простоты я обойдусь null-ом:
Задача выполнена, но, обратите внимание, мне пришлось модифицировать промежуточную функцию b(), чтобы она пробрасывала результат работы нижестоящей функции выше по каскаду. А если у меня каскад из 5 или 10 функций? То мне пришлось бы модифицировать ВСЕ промежуточные функции. А если исключительная ситуация в конструкторе? То мне пришлось бы подставлять костыли.
А теперь решение с использованием Exception:
Таким образом, получается, что ошибки и исключения — это совершенно разные инструменты для решения совершенно разных задач:
ошибка — не поправимая ситуация;
исключение – позволяет прервать выполнение каскада функций и пробросить некоторую информацию. Что-то вроде глобального оператора return. Если у Вас нет каскада, то вам достаточно использовать if или return.
Ошибки не всегда являются ошибками
Некоторые могут мне возразить: «Посмотри в Zend Framework — там всегда кидают исключения. Это best practics, и надо делать также. Даже если не удалось подключиться к БД, надо кидать исключение».
В этой статье я как раз хочу развеять это заблуждение. Zend действительно является best practics, но программисты Зенда находятся на другой лодке и делают другие вещи. Принципиальная разница между ними и мной в том, что они пишут универсальную библиотеку, которая будет использоваться во многих проектах. И они со своей колокольни не могут сказать, что является критической ошибкой, а что является поправимой.
Например, в вашем проекте может быть несколько MySQL серверов и вы можете переключаться между ними при падении одного из них. По этому, Zend_Db, как универсальная библиотека, кидает исключение, а что с ним делать — решайте сами. Exception это гибко — вы сами решаете на каком уровне и какой тип ситуаций ловить. Вы можете вывести сообщение об ошибке или попытаться исправить возникшую ситуацию, если знаете как. При написании универсальных библиотек необходимо всегда кидать исключения. Это делает библиотеку более гибкой.
В php давно был разработан механизм обработки ошибок, и он отлично работает. Я им отлично пользуюсь там, где это надо.
Исключение (программирование)
Обработка исключительных ситуаций (англ. exception handling ) — механизм языков программирования, предназначенный для описания реакции программы на ошибки времени выполнения и другие возможные проблемы (исключения), которые могут возникнуть при выполнении программы и приводят к невозможности (бессмысленности) дальнейшей отработки программой её базового алгоритма. В русском языке также применяется более короткая форма термина: «обработка исключений».
Содержание
Исключения
Общее понятие исключительной ситуации
Во время выполнения программы могут возникать ситуации, когда состояние данных, устройств ввода-вывода или компьютерной системы в целом делает дальнейшие вычисления в соответствии с базовым алгоритмом невозможным или бессмысленными. Классические примеры подобных ситуаций:
Виды исключительных ситуаций
Исключительные ситуации, возникающие при работе программы, можно разделить на два основных типа: синхронные и асинхронные, принципы реакции на которые существенно различаются.
Обработчики исключений
Общее описание
В отсутствие собственного механизма обработки исключений для прикладных программ наиболее общей реакцией на любую исключительную ситуацию является немедленное прекращение выполнения с выдачей пользователю сообщения о характере исключения. Можно сказать, что в подобных случаях единственным и универсальным обработчиком исключений становится операционная система. Возможно игнорирование исключительной ситуации и продолжение работы, но такая тактика опасна, так как приводит к ошибочным результатам работы программ или возникновению ошибок впоследствии. Например, проигнорировав ошибку чтения из файла блока данных, программа получит в своё распоряжение не те данные, которые она должна была считать, а какие-то другие. Результаты их использования предугадать невозможно.
Обработка исключительных ситуаций самой программой заключается в том, что при возникновении исключительной ситуации, управление передаётся некоторому заранее определённому обработчику — блоку кода, процедуре, функции, которые выполняют необходимые действия.
Существует два принципиально разных механизма функционирования обработчиков исключений:
Существует два варианта подключения обработчика исключительных ситуаций к программе: структурная и неструктурная обработка исключений.
Неструктурная обработка исключений
Неструктурная обработка исключений реализуются в виде механизма регистрации функций или команд-обработчиков для каждого возможного типа исключения. Язык или системные библиотеки предоставляют программисту как минимум две стандартные процедуры: регистрации обработчика и разрегистрации обработчика. Вызов первой из них «привязывает» обработчик к определённому исключению, вызов второй — отменяет эту «привязку». Если исключение происходит, выполнение основного кода программы немедленно прерывается и начинается выполнение обработчика. По завершении обработчика управление передаётся либо в некоторую наперёд заданную точку программы, либо обратно в точку возникновения исключения (в зависимости от заданного способа обработки — с возвратом или без). Независимо от того, какая часть программы в данный момент выполняется, на определённое исключение всегда реагирует последний зарегистрированный для него обработчик. В некоторых языках зарегистрированный обработчик сохраняет силу только в пределах текущего блока кода (процедуры, функции), тогда процедура разрегистрации не требуется. Ниже показан условный фрагмент кода программы с неструктурной обработкой исключений:
Неструктурная обработка — практически единственный вариант для обработки асинхронных исключений, но для синхронных исключений она неудобна: приходится часто вызывать команды установки/снятия обработчиков, всегда остаётся опасность нарушить логику работы программы, пропустив регистрацию или разрегистрацию обработчика.
Структурная обработка исключений
Структурная обработка исключений требует обязательной поддержки со стороны языка программирования — наличия специальных синтаксических конструкций. Такая конструкция содержит блок контролируемого кода и обработчик (обработчики) исключений. Наиболее общий вид такой конструкции (условный):
Здесь «НачалоБлока» и «КонецБлока» — ключевые слова, которые ограничивают блок контролируемого кода, а «Обработчик» — начало блока обработки соответствующего исключения. Если внутри блока, от начала до первого обработчика, произойдёт исключение, то произойдёт переход на обработчик, написанный для него, после чего весь блок завершится и исполнение будет продолжено со следующей за ним команды. «ОбработчикНеобработанных» — это обработчик исключений, которые не соответствуют ни одному из описанных выше в данном блоке. Обработчики исключений в реальности могут описываться по-разному (один обработчик на все исключения, по одному обработчику на одно исключение и так далее), но принципиально они работают одинаково: при возникновении исключения находится первый соответствующий ему обработчик в данном блоке, его код выполняется, после чего выполнение блока завершается. Исключения могут возникать как в результате программных ошибок, так и путём явной их генерации с помощью соответствующей команды (в примере — команда «СоздатьИсключение»). С точки зрения обработчиков такие искусственно созданные исключения ничем не отличаются от любых других.
Блоки обработки исключений могут многократно входить друг в друга, как явно (текстуально), так и неявно (например, в блоке вызывается процедура, которая сама имеет блок обработки исключений). Если ни один из обработчиков в текущем блоке не может обработать исключение, выполнение данного блока немедленно завершается, и управление передаётся на ближайший подходящий обработчик более высокого уровня иерархии. Это продолжается до тех пор, пока обработчик не найдётся и не обработает исключение.
Иногда бывает неудобно завершать обработку исключения в текущем блоке, то есть желательно, чтобы при возникновении исключения в текущем блоке обработчик выполнил какие-то действия, но исключение продолжило бы обрабатываться на более высоком уровне (обычно так бывает, когда обработчик данного блока не полностью обрабатывает исключение, а лишь частично). В таких случаях в обработчике исключений генерируется новое исключение или возобновляется с помощью специальной команды ранее появившееся. Код обработчиков не является защищённым в данном блоке, поэтому созданное в нём исключение будет обрабатываться в блоках более высокого уровня.
Блоки с гарантированным завершением
Помимо блоков контролируемого кода для обработки исключений, языки программирования могут поддерживать блоки с гарантированным завершением. Их использование оказывается удобным тогда, когда в некотором блоке кода, независимо от того, произошли ли какие-то ошибки, необходимо перед его завершением выполнить определённые действия. Простейший пример: если в процедуре динамически создаётся какой-то локальный объект в памяти, то перед выходом из этой процедуры объект должен быть уничтожен (чтобы избежать утечки памяти), независимо от того, произошли после его создания ошибки или нет. Такая возможность реализуется блоками кода вида:
Заключённые между ключевыми словами «НачалоБлока» и «Завершение» операторы (основной код) выполняются последовательно. Если при выполнении их не возникает исключений, то затем выполняются операторы между ключевыми словами «Завершение» и «КонецБлока» (код завершения). Если же при выполнении основного кода возникает исключение (любое), то сразу же выполняется код завершения, после чего весь блок завершается, а возникшее исключение продолжает существовать и распространяться до тех пор, пока его не перехватит какой-либо блок обработки исключений более высокого уровня.
Принципиальное отличие блока с гарантированным завершением — он не обрабатывает исключение, а лишь гарантирует выполнение определённого набора операций перед тем, как включится механизм обработки. Стоит заметить, что блок с гарантированным завершением легко реализуется с помощью команд «возбудить исключение» и «структурный обработчик исключения».
Поддержка в различных языках
За исключением незначительных различий в синтаксисе, существует лишь пара вариантов обработки исключений. В наиболее распространённом из них исключительная ситуация генерируется специальным оператором ( throw или raise ), а само исключение, с точки зрения программы, представляет собой некоторый объект данных. То есть генерация исключения состоит из двух этапов: создания объекта-исключения и возбуждения исключительной ситуации с этим объектом в качестве параметра. При этом конструирование такого объекта само по себе выброса исключения не вызывает. В одних языках объектом-исключением может быть объект любого типа данных (в том числе строкой, числом, указателем и так далее), в других — только предопределённого типа-исключения (чаще всего он имеет имя Exception) и, возможно, его производных типов (типов-потомков, если язык поддерживает объектные возможности).
В целом, обработка исключений может выглядеть следующим образом (в некотором абстрактном языке):
В некоторых языках может быть лишь один обработчик, который разбирается с различными типами исключений самостоятельно.
Достоинства и недостатки
Достоинства использования исключений особенно заметно проявляется при разработке библиотек процедур и программных компонентов, ориентированных на массовое использование. В таких случаях разработчик часто не знает, как именно должна обрабатываться исключительная ситуация (при написании универсальной процедуры чтения из файла невозможно заранее предусмотреть реакцию на ошибку, так как эта реакция зависит от использующей процедуру программы), но ему это и не нужно — достаточно сгенерировать исключение, обработчик которого предоставляется реализовать пользователю компонента или процедуры. Единственная альтернатива исключениям в таких случаях — возврат кодов ошибок, которые вынужденно передаются по цепочке между несколькими уровнями программы, пока не доберутся до места обработки, загромождая код и снижая его понятность.
Использование исключений в целях контроля ошибок повышает читаемость кода, так как позволяет отделить обработку ошибок от самого алгоритма, и облегчает программирование и использование компонентов других разработчиков.
Недостаток исключений — в том, что их поддержка снижает скорость работы программы. Поэтому в местах программы, критичных по скорости, не рекомендуют возбуждать и обрабатывать исключения, хотя следует отметить, что в реальности такие случаи крайне редки, во всяком случае, в прикладном программировании.
Проверяемые исключения
Некоторые проблемы простой обработки исключений
Изначально (например, в C++), не существовало никакой формальной дисциплины описания, генерации и обработки исключений: любое исключение может быть возбуждено в любом месте программы, и если для него не находится обработчика в стеке вызовов, то выполнение программы прерывается аварийно. Если функция (особенно библиотечная) генерирует исключения, то для устойчивой работы использующая её программа должна перехватывать их все. Когда по какой-либо причине одно из возможных исключений оказывается необработанным, будет происходить неожиданное аварийное завершение программы. С подобными эффектами можно бороться организационными мерами, описывая возможные исключения, возникающие в библиотечных модулях, в соответствующей документации. Но при этом всегда остаётся возможность пропустить необходимый обработчик из-за случайной ошибки или несоответствия документации коду (что вовсе не редкость). Чтобы полностью исключить потерю обработки исключений, в обработчики приходится специально добавлять ветвь обработки «всех остальных» исключений (которая гарантированно перехватит любые, даже заранее неизвестные исключения), но такой выход не всегда оптимален.
Механизм проверяемых исключений
Позже в ряде языков, например, в Java, появились проверяемые исключения. Сущность этого механизма состоит в добавлении в язык следующих правил и ограничений:
Преимущества и недостатки
Проверяемые исключения снижают количество ситуаций, когда исключение, которое могло быть обработано, вызвало критическую ошибку в программе, поскольку за наличием обработчиков следит компилятор. Это особенно полезно при изменениях кода, когда метод, который не мог ранее выбрасывать исключение типа X, начал это делать: компилятор автоматически отследит все случаи его использования и проверит наличие соответствующих обработчиков.
Другим полезным качеством проверяемых исключений является то, что они способствуют осмысленному написанию обработчиков: программист явно видит полный и правильный список исключений, которые могут возникнуть в данном месте программы, и может написать на каждое из них осмысленный обработчик вместо того, чтобы создавать «на всякий случай» общий обработчик всех исключений, одинаково реагирующий на все нештатные ситуации.
У проверяемых исключений есть и недостатки.
Из-за перечисленных недостатков при обязательности использования проверяемых исключений этот механизм часто обходят. Например, многие библиотеки объявляют все методы как выбрасывающие некоторый суперкласс исключений (например, Exception ), и только на этот тип исключения создаются обработчики. Однако результатом становится то, что компилятор заставляет писать обработчики исключений даже там, где они объективно не нужны. Более правильным подходом считается перехват внутри метода новых исключений, порождённых вызываемым кодом, а при необходимости передать исключение дальше — «заворачивание» его в исключение, уже возвращаемое методом. Например, если метод изменили так, что он начинает обращаться к базе данных вместо файловой системы, то он может сам ловить SQLException и выбрасывать вместо него вновь создаваемый IOException, указывая в качестве причины исходное исключение. Обычно рекомендуется изначально объявлять именно те исключения, которые придётся обрабатывать вызывающему коду. Скажем, если метод извлекает входные данные, то для него целесообразно объявить IOException, а если он оперирует SQL-запросами, то, вне зависимости от природы ошибки, он должен объявлять SQLException. В любом случае, набор выбрасываемых методом исключений должен тщательно продумываться. При необходимости имеет смысл создавать собственные классы исключений, наследуя их от Exception или других подходящих проверяемых исключений.













