Многие программисты почему-то считают, что исключения и ошибки — это одно и то же. Кто-то постоянно кидает 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 давно был разработан механизм обработки ошибок, и он отлично работает. Я им отлично пользуюсь там, где это надо.
Что такое исключения в программировании
Рассказ об ошибках, которые можно предусмотреть заранее.
Большинство наших проектов устроены так: когда во время работы программы возникает какая-то ошибка, то программа аварийно завершается. Иногда при этом она выдаёт сообщение об ошибке. Кажется, что это нормальная ситуация, но на самом деле большинство ошибок можно предусмотреть и научить программу правильно с ними работать. Для этого нам нужны обработчики ошибок.
Что такое обработчик ошибок
Чтобы программа знала, что делать, если возникла какая-то ошибка, используют обработчики исключительных ситуаций, или, проще говоря, обработчики исключений. Смысл такой:
Такие обработчики есть не в каждом языке программирования, но большинство современных языков это умеют делать.
Пример программы без обработчика исключений
Допустим, у нас в программе на Python предусмотрено чтение данных из файла и есть такой код:
Но если на диске этого файла не будет, то компьютер, когда дойдёт до этой строчки, выдаст ошибку:
Давайте нарисуем это в виде простой схемы:
Получается, что наша задача — предусмотреть вариант, что на диске не будет нужного файла, и придумать поведение программы в этом случае. Используем для этого обработчик исключений.
Программа с обработчиком исключений
Если мы знаем, что в каком-то месте возможна ошибка, то можем тогда предусмотреть этот сценарий и подстраховаться. Для этого используют обработчик и делают так:
В этой ситуации программа не зависнет и не вывалится с ошибкой, а сама сможет её обработать и делать дальше то, что нужно:
Команда try — это начало нашего обработчика исключений. Она говорит компьютеру: «Попробуй выполнить вот эту команду, а мы посмотрим, что произойдёт».
Except — это какую ошибку мы ожидаем здесь увидеть. В нашем случае мы хотим предусмотреть случай, что такого файла нет, поэтому пишем стандартную ошибку для такой ситуации.
👉 Сравните текст этой ошибки с тем, что нам выдал компьютер в предыдущем разделе.
В других языках конструкция обработчика исключений может выглядеть по-другому, но смысл тот же: говорим компьютеру, какую команду нужно выполнить и что делать, если появилась конкретная ошибка.
Когда что-то не предусмотрено — будет ошибка
Если программе в этом блоке встретится другая ошибка, не та, которую мы предусмотрели, то программа остановится и всё перестанет работать. Например, вот какие ошибки могут возникнуть с файлом:
Во всех этих случаях программа сломается, потому что мы не предусмотрели эти ситуации:
Получается, всё нужно делать с обработкой исключений?
Конечно, есть места в коде, которые лучше делать с обработкой ошибок: работа с файлами, сетевые запросы или получение внешних данных. Но запихивать исключения на каждую команду в программе точно не стоит.
Коды возврата vs исключения — битва за контроль ошибок
Что такое ошибка?
Как видите, много всего разного и нехорошего может произойти — а ведь это далеко не полный список :). А что делать программисту? Тут, на мой взгляд, перед нами встает очень интересный и важный вопрос — как именно нашей программе реагировать на ту или иную ошибку? Пожалуй сейчас я еще раз напомню, что излагаю свое сугубо личное мнение. И скажу следующее — как именно реагировать на ошибку целиком зависит от конкретной программы. Если у нас закончилась память в драйвере — мы должны любой ценой выжить, чтобы пользователь не получил синего экрана смерти. Если же у нас закончилась память в игрушке типа веселая ферма — то имеет смысл упасть, извиниться и попросить отправить багрепорт разработчику. Системный сервис, призванный крутиться многие месяцы без перезагрузки, должен с пониманием отнестись к ошибке CreateEvent(). Та жа ошибка в прикладной программе типа Photoshop означает что скорее всего система через секунду умрет, и лучше честно упасть, нежели попытаться проглотить ошибку, дать пользователю сохранить файл и благополучно его испортить из-за последующего сбоя во время записи. Следовательно ошибки мы можем делить на ожидаемые и неожиданные. Для разных программ и разных требований одни и те же ошибки могут считаться как ожидаемыми, так и неожиданными. С ожидаемыми ошибками мы как-то работаем. Не получилось открыть файл — говорим об этом пользователю и продолжаем работу. Не удалось выделить память для загрузки туда гигабайтного файла — говорим об этом пользователю и продолжаем работу. С неожиданными ошибками мы в большинстве случаев не работаем. Закончилась память при попытке выделить двадцать байт для создания объекта — падаем. Не создался системный объект которых на всю программу три штуки — падаем. Не читается системный пайп который по спецификации должен читаться? Лучше упасть, чем оставить программу в нестабильном состоянии и потом испортить пользователю данные. Программу он если что перезапустит, а вот за испорченный файл возненавидит до конца дней своих. А для серьезных случаев есть автосейв и перезапускающий нас ежели чего watchdog.
Что было до исключений?
Через некоторое время разработчики заметили, что большинство успешных решений использует ООП и решили что неплохо бы его вынести в синтаксис языка, дабы писать больше кода по делу и меньше — повторяющегося кода для поддержки архитектуры.
Что стало после введения исключений
Давайте возьмем код выше и посмотрим, как он трансформировался после добавления ООП в синтаксис языков программирования. Конструирование и уничтожение объектов (fopen, fclose) стало конструкторами и деструкторами. Переброс неожиданной ошибки (BOOL ret в первом примере, макрос ENSURE во втором) однозначно стал исключением.
А вот с ожидаемой ошибкой случилось самое интересное — случился выбор. Можно было использовать возвращаемое значение — теперь, когда заботу о неожиданных ошибках взяли на себя исключения, возвращаемое значение снова стало в полном распоряжении программиста. А можно было использовать исключения другого типа — если функции копирования файлов самой не нужно обрабатывать ожидаемые ошибки то логично вместо if и REQUIRE просто ничего не делать — и оба типа ошибок уйдут вверх по стеку. Соответственно, у программистов снова получилось два варианта:
Почему выжили коды возврата?
Выводы
Что бы я хотел резюмировать. На мой взгляд, большинство проблем с исключениями были вызваны первыми, не очень удачными реализациями — особенно в C++. Так что выбор «использовать только коды возврата», «использовать исключения для неожиданных ошибок и коды возврата для ожидаемых» или «использовать исключения для всего» по большей части имеется только для C++. Надеюсь, мой краткий рассказ о причинах появления исключений в современных языках программирования поможет разработчикам чуть лучше ориентироваться в современных API и замечать места, где авторы использую исключения некорректно. Понимание какие из ошибок мы считаем для нашей программы ожидаемыми, какие — неожиданные и как это оптимальным образом ложится на предоставляемую языком и API модель исключений позволяет писать простой, понятный и внимательно следящий за ошибками код.
BestProg
Содержание
Поиск на других ресурсах:
1. Что такое исключительная ситуация?
Исключительная ситуация – это программная ошибка, которая возникает во время выполнения последовательности программного кода. Программная ошибка может быть логической ошибкой программиста во время разработки программы. Например, исключительная ситуация может возникать в случаях:
Правильная обработка исключений есть важным элементом написания профессиональных программ на Java.
2. Понятие исключения в языке Java
Как правило, каждая исключительная ситуация может иметь свой код ошибки и обработку этого кода (вывод соответствующих сообщений, и т.п.). В языке программирования Java разработан механизм обработки исключительных ситуаций.
В языке программирования Java, исключение – это специальный объект, описывающий исключительную ситуацию, которая возникла в некоторой части программного кода. Объект представляющий исключение, генерируется в момент возникновения исключительной ситуации. После возникновения критической (исключительной) ситуации исключение перехватывается и обрабатывается. Таким образом, возникает понятие генерирования исключения. Генерирование исключения – процесс создания объекта, который описывает данное исключение.
3. Какими способами могут генерироваться исключения?
В языке Java исключения могут генерироваться одним из двух способов:
4. В каких случаях вызывается стандартный обработчик исключений Java?
Стандартный обработчик исключений Java вызывается в случаях, когда программа:
Если программа содержит собственный код try…catch для обработки данной исключительной ситуации, тогда стандартный обработчик исключений не вызывается.
5. Какие ключевые слова используются для обработки исключений в Java?
Для обработки исключений в Java используются следующие ключевые слова:
В языке Java можно вручную перехватывать исключения (исключительные ситуации) и соответствующим образом обрабатывать их. Важно, чтобы при возникновении исключительной ситуации программа не прекратила свое выполнение.
В блоке try размещаются операторы программы, которые нужно проконтролировать и, в случае возникновения исключительной ситуации, сгенерировать исключение. Внутри блока try могут быть вызваны различные методы, способные сгенерировать то или иное исключение. Однако, обработчик исключения будет только один.
Операторы try и catch составляют единое целое. Оператор finally может отсутствовать.
7. Пример генерирования исключения и его перехват стандартным обработчиком исключений Java
Если в программе отсутствует собственный блок try…catch перехвата исключения, будет вызван стандартный обработчик исключения. В примере демонстрируется вызов стандартного обработчика.
Текст класса DemoExceptions следующий:
Если в другом классе использовать метод DivNumbers() следующим образом
то будет вызванный стандартный обработчик Java, который выведет следующее сообщение
8. Пример перехвата и обработки исключения с помощью оператора try…catch
Текст класса DemoExceptions следующий
Теперь, при использовании метода DivNumbers2() в другом классе
система выдаст следующий результат после запуска программы на выполнение
Можно изменить вывод собственного сообщения об исключении на стандартное сообщение. В этом случае нужно изменить текст в блоке catch на следующий
После изменений, результат выполнения программы будет такой
Текст класса DemoExceptions следующий
будет вызываться всегда. И перед оператором
Использование метода DivNumbers3() в другом классе может быть следующим
Результат выполнения программы
10. Как реализовать вывод описания исключения? Пример
В результате программа будет выводить следующий результат
11. Пример перехвата нескольких исключительных ситуаций (несколько операторов catch )
Реализация класса DivArrays следующая
Использование класса в другом программном коде
Исключения в Java. Часть 1
Разбираемся, что такое исключения, зачем они нужны и как с ними работать.
Из этой статьи вы узнаете:
Код вашей программы исправно компилируется и запускается, только вот вместо желанного результата вы видите непонятный текст. Строчки его будто кричат на вас, аж побагровели.
За примером далеко ходить не надо: сделаем то, что нам запрещали ещё в школе, — поделим на ноль.
Это и есть исключение.
«Исключение» — сокращение от слов «исключительный случай». Это ситуация, в которой программа не может продолжить работу или её работа становится бессмысленной. Причём речь не только о нештатных ситуациях — исключения бывают и намеренными, такие разработчик вызывает сам.
Это интересно. Исключения в Java появились уже в первой версии языка. А вот в языках, где их нет, вместо них возвращают коды ошибок.
Хлебом не корми —
дай кому-нибудь про Java рассказать.
Иерархия исключений и ошибки
У всех классов исключений есть общий класс-предок Throwable, от него наследуются классы Error и Exception, базовые для всех прочих.
Что такое ошибки (Errors)
Error is the superclass of all the exceptions from which ordinary programs are not ordinarily expected to recover.
Что в переводе означает: ошибки (Error) — это такие исключительные ситуации, в которых восстанавливать работу программы не предполагается.
То есть это проблемы, которые нельзя (недопустимо) исправлять на ходу. Всё, что нам остаётся, — извиниться перед пользователем и впредь писать программы, где возникнет меньше подобных ситуаций. Например, не допускать такой глубокой рекурсии, как в коде ниже:
При работе этого метода у нас возникнет ошибка: Exception in thread «main» java.lang.StackOverflowError — стек вызовов переполнился, так как мы не указали условие выхода из рекурсии.
Что такое исключения (Exceptions)
А теперь об Exception. Эти исключительные ситуации возникают, если разработчик допустил невыполнимую операцию, не предусмотрел особые случаи в бизнес-логике программы (или сообщает о них с помощью исключений).
1. Невыполнимая операция
Мир не рухнул, как в случае с Error, просто Java не знает, что делать дальше. Как раз из этого разряда деление на ноль в начале статьи: и правда, какое значение тогда присвоить переменной oops?
Убедитесь сами, что исключение класса ArithmeticException наследуется как раз от Exception.
Другая частая ситуация — обращение к несуществующему элементу массива. Например, у нас в нём десять элементов, а мы пытаемся обратиться к одиннадцатому.
2. Особый случай в бизнес-логике программы
Классика. Программируем задачу о перевозке волка, козы и капусты через реку: в лодке может быть только два пассажира, но волка с козой и козу с капустой нельзя оставлять на берегу вместе. Это и есть особый случай в бизнес-логике, который нельзя нарушать.
Или пользователь вводит дату начала некоторого периода и дату его окончания. Вторая дата не может быть раньше первой.
Или, допустим, у нас есть метод, который читает файл. Сам метод написан верно. Пользователь передал в него корректный путь. Только вот у этого работника нет права читать этот файл (его роль и права обусловлены предметной областью). Что же тогда методу возвращать? Вернуть-то нечего, ведь метод не отработал. Самое очевидное решение — выдать исключение.
В дерево исключений мы ещё углубимся, а сейчас посмотрим, что и как с ними делают.
Что делать с исключениями
Простейший вариант — ничего; возникает исключение — программа просто прекращает работать.












