Как выбросить исключение в c
Перейти к содержимому

Как выбросить исключение в c

Операторы try, throw и catch (C++)

Для реализации обработки исключений в C++используется try throw и catch выражения.

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

throw Выражение сигнализирует о том, что исключительное условие ( часто ошибка) произошла в блоке try . Объект любого типа можно использовать в качестве операнда throw выражения. Обычно этот объект используется для передачи информации об ошибке. В большинстве случаев рекомендуется использовать std::exception класс или один из производных классов, определенных в стандартной библиотеке. Если один из них не подходит, рекомендуется наследовать собственный класс исключений. std::exception

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

В этом примере показан try блок и его обработчики. Предположим, GetNetworkResource() получает данные через сетевое подключение, а 2 типа исключений являются определенными пользователем классами, производными от std::exception . Обратите внимание, что исключения перехватываются по const ссылке в инструкции catch . Рекомендуется создавать исключения по значению и захватывать их ссылкой константы.

Пример

MyData md; try < // Code that could throw an exception md = GetNetworkResource(); >catch (const networkIOException& e) < // Code that executes when an exception of type // networkIOException is thrown in the try block // . // Log error message in the exception object cerr catch (const myDataFormatException& e) < // Code that handles another exception type // . cerr // The following syntax shows a throw expression MyData GetNetworkResource() < // . if (IOSuccess == false) throw networkIOException("Unable to connect"); // . if (readError) throw myDataFormatException("Format error"); // . >

Замечания

Код после try предложения является защищенным разделом кода. Выражение throw вызывает исключение. Блок кода после catch предложения является обработчиком исключений. Это обработчик, который перехватывает исключение, которое создается, если типы в throw выражениях catch совместимы. Список правил, которые управляют сопоставлением типов в catch блоках, см. в разделе «Оценка блоков catch». catch Если инструкция задает многоточие (. ) вместо типа, catch блок обрабатывает каждый тип исключения. При компиляции с /EHa параметром они могут включать структурированные исключения C и системные или асинхронные исключения, созданные приложением, такие как защита памяти, разделение на ноль и нарушения с плавающей запятой. Так как catch блоки обрабатываются в программе, чтобы найти соответствующий тип, обработчик многоточия должен быть последним обработчиком для связанного try блока. Используйте с осторожностью; не позволяйте catch(. ) программе продолжать работу, если блок catch не знает, как обрабатывать определенное исключение, которое поймано. Как правило, блок catch(. ) используется для ведения журнала ошибок и выполнения специальной очистки перед остановкой выполнения программы.

Выражение throw , которое не имеет операнда повторно выполняет обработку исключения. Мы рекомендуем эту форму при повторном создании исключения, так как это сохраняет сведения о полиморфном типе исходного исключения. Такое выражение должно использоваться только в catch обработчике или в функции, которая вызывается из обработчика catch . Объект исключения rethrown является исходным объектом исключения, а не копией.

try < throw CSomeOtherException(); >catch(. ) < // Catch all exceptions - dangerous. // Respond (perhaps only partially) to the exception, then // re-throw to pass the exception to some other handler // . throw; >

Обработка исключений

Обработка исключений (exception handling) позволяет упорядочить обработку ошибок времени исполнения. Используя обработку исключений С++, программа может автоматически вызвать функцию-обработчик ошибок тогда, когда такая ошибка возникает. Принципиальным достоин­ством обработки исключений служит то, что она позволяет автоматизировать большую часть кода для обработки ошибок, для чего раньше требовалось ручное кодирование.

Основы обработки исключений

Обработка исключений в С++ использует три ключевых слова: try, catch и throw. Те инструкции программы, где ожидается возможность появления исключительных ситуаций, содержатся в бло­ке try. Если в блоке try возникает исключение, т. е. ошибка, то генерируется исключение. Исклю­чение перехватывается, используя catch, и обрабатывается. Ниже это общее описание будет рас­смотрено более подробно.

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

try // блок try
catch (тип1 аргумент) // блок catch
catch (тип2 аргумент) // блок catch
catch (типЗ аргумент) // блок catch
>
.
catch (типN аргумент) // блок catch
>

Размеры блока try могут изменяться в больших пределах. Например, блок try может содержать несколько инструкций какой-либо функции, либо же, напротив, включать в себя весь код функ­ции main(), так что вся программа будет охвачена обработкой исключений.

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

Общая форма записи инструкции throw имеет вид:

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

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

Ниже представлен пример, иллюстрирующий способ обработки исключений в С++:

Программа выведет на экран следующий текст:

Start
Inside try block
Caught an exception — value is: 100
End

Рассмотрим внимательнее эту программу. Как можно видеть, блок try содержит три инструкции. За ним следует инструкция catch(int i), обрабатывающая исключения целого типа. В блоке try будут выполняться только две инструкции: первая и вторая — throw. Как только исключение было сгенерировано, управление передается инструкции catch, а блок try прекращает свое исполнение. Таким образом, catch не вызывается. Скорее можно сказать, что к ней переходит исполнение программы. Для этого автоматически осуществляется переустановка стека. Таким образом, инст­рукция после инструкции throw никогда не выполняется.

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

Как отмечалось, тип исключения должен соответствовать указанному в инструкции типу. Напри­мер, в предыдущем примере если изменить тип инструкции catch на double, то исключение не будет перехвачено и произойдет аварийное завершение программы. Такое изменение показано ниже:

Эта программа выдаст следующий результат, поскольку исключение целого типа не будет пере­хвачено инструкцией catch (double i):

Start
Inside try block
Abnormal program termination

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

Эта программа выдаст следующий результат:

Start
Inside try block
Inside Xtest, test is: 0
Inside Xtest, test is: 1
Caught an exception — value is: 1
End

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

#include
// try/catch могут находиться в функции вне main()
void Xhandler(int test)
try if (test) throw test;
>
catch(int i) cout >
>
int main()
cout Xhandler(1);
Xhandler(2);
Xhandler(0);
Xhandler(3);
cout return 0;
>

Эта программа выдаст на экран следующий текст:

Start
Caught Exception #: 1
Caught Exception #: 2
Caught Exception #: 3
End

Как видно, сгенерировано три исключения. После каждого исключения функция возвращает управ­ление в функцию main. При каждом новом вызове функции возвращается обработка исключений.

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

Современные рекомендации по C++ по исключению и обработке ошибок

В современных C++в большинстве сценариев предпочтительный способ сообщать об ошибках логики и обработать ошибки логики и ошибки среды выполнения — использовать исключения. Это особенно верно, если стек может содержать несколько вызовов функций между функцией, которая обнаруживает ошибку, и функцию, которая имеет контекст для обработки ошибки. Исключения предоставляют формальный, четко определенный способ для кода, который обнаруживает ошибки для передачи информации в стек вызовов.

Использование исключений для исключительного кода

Ошибки программы часто делятся на две категории: ошибки логики, вызванные ошибками программирования, например ошибкой «индекс вне диапазона». И ошибки среды выполнения, которые выходят за рамки контроля программиста, например ошибка «сетевая служба недоступна». В программировании на языке C и com отчеты об ошибках управляются либо путем возврата значения, представляющего код ошибки или код состояния для конкретной функции, либо путем задания глобальной переменной, которую вызывающий объект может при необходимости получить после каждого вызова функции, чтобы узнать, были ли сообщения об ошибках. Например, com-программирование использует возвращаемое значение HRESULT для передачи ошибок вызывающей программе. А API Win32 имеет GetLastError функцию для получения последней ошибки, сообщаемой стеком вызовов. В обоих случаях вызывающий объект распознает код и отвечает на него соответствующим образом. Если вызывающий объект не обрабатывает код ошибки явным образом, программа может завершиться без предупреждения. Кроме того, он может продолжать выполняться с использованием плохих данных и создавать неправильные результаты.

Исключения предпочтительны в современном C++ по следующим причинам:

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

В следующем упрощенном примере показан необходимый синтаксис для создания и перехвата исключений в C++.

#include #include #include using namespace std; void MyFunc(int c) < if (c >numeric_limits < char>::max()) throw invalid_argument("MyFunc argument too large."); //. > int main() < try < MyFunc(256); //cause an exception to throw >catch (invalid_argument& e) < cerr //. return 0; > 

Исключения в C++ похожи на такие языки, как C# и Java. В блоке try , если исключение создается , оно будет поймано первым связанным catch блоком, тип которого соответствует типу исключения. Другими словами, выполнение переходит от инструкции throw к оператору catch . Если не найден доступный блок перехвата, std::terminate вызывается и программа завершает работу. В C++любой тип может быть создан; однако рекомендуется создать тип, производный напрямую или косвенно от std::exception . В предыдущем примере тип invalid_argument исключения определен в стандартной библиотеке в файле заголовка . C++ не предоставляет или не требует finally блока, чтобы убедиться, что все ресурсы освобождены, если исключение возникает. Приобретение ресурсов — идиома инициализация (RAII), которая использует интеллектуальные указатели, предоставляет необходимые функциональные возможности для очистки ресурсов. Дополнительные сведения см. в разделе «Практическое руководство . Проектирование для обеспечения безопасности исключений». Сведения о механизме очистки стека C++ см. в разделе «Исключения» и очистка стека.

Основные рекомендации

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

  • Используйте утверждения для проверка ошибок, которые никогда не должны возникать. Используйте исключения для проверка ошибок, которые могут возникать, например, ошибки при проверке входных данных для параметров общедоступных функций. Дополнительные сведения см. в разделе «Исключения и утверждения».
  • Используйте исключения, если код, обрабатывающий ошибку, отделен от кода, который обнаруживает ошибку одним или несколькими промежуточными вызовами функций. Рассмотрим, следует ли использовать коды ошибок вместо циклов, критически важных для производительности, когда код, обрабатывающий ошибку, тесно связан с кодом, который обнаруживает его.
  • Для каждой функции, которая может вызывать или распространять исключение, укажите одну из трех гарантий исключения: надежную гарантию, базовую гарантию или гарантию nothrow (noexcept). Дополнительные сведения см. в разделе «Практическое руководство . Проектирование для обеспечения безопасности исключений».
  • Создает исключения по значению, перехватывает их по ссылке. Не перехватывать то, что вы не можете обрабатывать.
  • Не используйте спецификации исключений, которые устарели в C++11. Дополнительные сведения см. в разделе спецификаций исключений и noexcept раздела.
  • Используйте стандартные типы исключений библиотеки при их применении. Производные пользовательские типы исключений exception из иерархии классов .
  • Не разрешайте исключениям экранироваться от деструкторов или функций размещения памяти.

Исключения и производительность

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

Исключения и утверждения

Исключения и утверждения являются двумя различными механизмами обнаружения ошибок во время выполнения в программе. Используйте assert инструкции для проверки условий во время разработки, которые никогда не должны быть верными, если весь код правильный. Нет смысла обрабатывать такую ошибку с помощью исключения, так как ошибка указывает, что что-то в коде должно быть исправлено. Это не означает условие, которое программа должна восстановить во время выполнения. При остановке assert выполнения инструкции можно проверить состояние программы в отладчике. Исключение продолжает выполнение из первого соответствующего обработчика перехвата. Используйте исключения для проверка условий ошибки, которые могут возникнуть во время выполнения, даже если код исправлен, например «файл не найден» или «вне памяти». Исключения могут обрабатывать эти условия, даже если восстановление просто выводит сообщение в журнал и заканчивает программу. Всегда проверка аргументы для общедоступных функций с помощью исключений. Даже если функция не является ошибкой, возможно, у вас нет полного контроля над аргументами, которые пользователь может передать в него.

Исключения C++ и исключения SEH Для Windows

Программы C и C++ могут использовать структурированный механизм обработки исключений (SEH) в операционной системе Windows. Основные понятия в SEH похожи на те, которые используются в исключениях C++, за исключением того, что SEH использует __try __except __finally и конструкции, а не . try catch В компиляторе Microsoft C++ (MSVC) исключения C++ реализуются для SEH. Однако при написании кода C++ используйте синтаксис исключения C++.

Спецификации исключений и noexcept

Спецификации исключений были представлены в C++ в качестве способа указания исключений, которые может вызывать функция. Однако спецификации исключений оказались проблематичными на практике и устарели в стандарте черновика C++11. Рекомендуется не использовать throw спецификации исключений, за исключением того throw() , что функция не позволяет экранировать исключения. Если необходимо использовать спецификации исключений устаревшей формы throw( type-name ) , поддержка MSVC ограничена. Дополнительные сведения см. в разделе «Спецификации исключений (throw)». Описатель noexcept представлен в C++11 в качестве предпочтительной альтернативы throw() .

Обработка ошибок и исключения

2. Коды возврата. Основная идея — в случае ошибки возвращать специальное значение, которое не может быть корректным. Например, если в методе есть операция деления, то придется проверять делитель на равенство нулю. Также проверим корректность аргументов a и b :

Double f(Double a, Double b) < if ((a == null) || (b == null)) < return null; > //. if (Math.abs(b) < EPS) < return null; > else < return a / b; > >

При вызове метода необходимо проверить возвращаемое значение:

Double d = f(a, b); if (d != null) < //. > else < //. >

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

3.Использовать флаг ошибки: при возникновении ошибки устанавливать флаг в соответствующее значение:

boolean error; Double f(Double a, Double b) < if ((a == null) || (b == null)) < error = true; return null; > //. if (Math.abs(b) < EPS) < error = true; return b; > else < return a / b; > >
error = false; Double d = f(a, b); if (error) < //. > else < //. >

Минусы такого подхода аналогичны минусам использования кодов возврата.

4.Можно вызвать метод обработки ошибки и возвращать то, что вернет этот метод.

Double f(Double a, Double b) < if ((a == null) || (b == null)) < return nullPointer(); > //. if (Math.abs(b) < EPS) < return divisionByZero(); > else < return a / b; > >

Но в таком случае не всегда возможно проверить корректность результата вызова основного метода.

5.В случае ошибки просто закрыть программу.

if (Math.abs(b) < EPS) < System.exit(0); return this; >

Это приведет к потере данных, также невозможно понять, в каком месте возникла ошибка.

Исключения

В Java возможна обработка ошибок с помощью исключений:

Double f(Double a, Double b) < if ((a == null) || (b == null)) < throw new IllegalArgumentException("arguments of f() are null"); > //. return a / b; >

Проверять b на равенство нулю уже нет необходимости, так как при делении на ноль метод бросит непроверяемое исключение ArithmeticException .

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

Каждый раз, когда при выполнении программы происходит ошибка, создается объект-исключение, содержащий информацию об ошибке, включая её тип и состояние программы на момент возникновения ошибки. После создания исключения среда выполнения пытается найти в стеке вызовов метод, который содержит код, обрабатывающий это исключение. Поиск начинается с метода, в котором произошла ошибка, и проходит через стек в обратном порядке вызова методов. Если не было найдено ни одного подходящего обработчика, выполнение программы завершается.

Таким образом, механизм обработки исключений содержит следующие операции:

  1. Создание объекта-исключения.
  2. Заполнение stack trace’а этого исключения.
  3. Stack unwinding (раскрутка стека) в поисках нужного обработчика.

Классификация исключений

Класс Java Throwable описывает все, что может быть брошено как исключение. Наследеники Throwable — Exception и Error — основные типы исключений. Также RuntimeException , унаследованный от Exception , является существенным классом.

Иерархия стандартных исключений

Проверяемые исключения

Наследники класса Exception (кроме наслеников RuntimeException ) являются проверяемыми исключениями(checked exception). Как правило, это ошибки, возникшие по вине внешних обстоятельств или пользователя приложения – неправильно указали имя файла, например. Эти исключения должны обрабатываться в ходе работы программы, поэтому компилятор проверяет наличие обработчика или явного описания тех типов исключений, которые могут быть сгенерированы некоторым методом.

Все исключения, кроме классов Error и RuntimeException и их наследников, являются проверяемыми.

Error

Класс Error и его подклассы предназначены для системных ошибок. Свои собственные классы-наследники для Error писать (за очень редкими исключениями) не нужно. Как правило, это действительно фатальные ошибки, пытаться обработать которые довольно бессмысленно (например OutOfMemoryError ).

RuntimeException

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

Обработка исключений

Чтобы сгенерировать исключение используется ключевое слово throw . Как и любой объект в Java, исключения создаются с помощью new .

if (t == null) < throw new NullPointerException("t = null"); >

Есть два стандартных конструктора для всех исключений: первый — конструктор по умолчанию, второй принимает строковый аргумент, поэтому можно поместить подходящую информацию в исключение.

Возможна ситуация, когда одно исключение становится причиной другого. Для этого существует механизм exception chaining. Практически у каждого класса исключения есть конструктор, принимающий в качестве параметра Throwable – причину исключительной ситуации. Если же такого конструктора нет, то у Throwable есть метод initCause(Throwable) , который можно вызвать один раз, и передать ему исключение-причину.

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

void f() throws InterruptedException, IOException < //. 

try-catch-finally

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

try < // Код, который может сгенерировать исключение >

Сразу после блока проверки следуют обработчики исключений, которые объявляются ключевым словом catch.

try < // Код, который может сгенерировать исключение > catch(Type1 id1) < // Обработка исключения Type1 > catch(Type2 id2) < // Обработка исключения Type2 >

Сatch -блоки обрабатывают исключения, указанные в качестве аргумента. Тип аргумента должен быть классом, унаследованного от Throwable , или самим Throwable . Блок catch выполняется, если тип брошенного исключения является наследником типа аргумента и если это исключение не было обработано предыдущими блоками.

Код из блока finally выполнится в любом случае: при нормальном выходе из try , после обработки исключения или при выходе по команде return .

NB: Если JVM выйдет во время выполнения кода из try или catch , то finally -блок может не выполниться. Также, например, если поток выполняющий try или catch код остановлен, то блок finally может не выполниться, даже если приложение продолжает работать.

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

import java.io.IOException; public class ExceptionTest < public static void main(String[] args) < try < try < throw new Exception("a"); > finally < throw new IOException("b"); > > catch (IOException ex) < System.err.println(ex.getMessage()); > catch (Exception ex) < System.err.println(ex.getMessage()); > > >

После того, как было брошено первое исключение — new Exception(«a») — будет выполнен блок finally , в котором будет брошено исключение new IOException(«b») , именно оно будет поймано и обработано. Результатом его выполнения будет вывод в консоль b . Исходное исключение теряется.

Обработка исключений, вызвавших завершение потока

При использовании нескольких потоков бывают ситуации, когда поток завершается из-за исключения. Для того, чтобы определить с каким именно, начиная с версии Java 5 существует интерфейс Thread.UncaughtExceptionHandler . Его реализацию можно установить нужному потоку с помощью метода setUncaughtExceptionHandler . Можно также установить обработчик по умолчанию с помощью статического метода Thread.setDefaultUncaughtExceptionHandler .

Интерфейс Thread.UncaughtExceptionHandler имеет единственный метод uncaughtException(Thread t, Throwable e) , в который передается экземпляр потока, завершившегося исключением, и экземпляр самого исключения. Когда поток завершается из-за непойманного исключения, JVM запрашивает у потока UncaughtExceptionHandler , используя метод Thread.getUncaughtExceptionHandler() , и вызвает метод обработчика – uncaughtException(Thread t, Throwable e) . Все исключения, брошенные этим методом, игнорируются JVM.

Информация об исключениях

  • getMessage() . Этот метод возвращает строку, которая была первым параметром при создании исключения;
  • getCause() возвращает исключение, которое стало причиной текущего исключения;
  • printStackTrace() печатает stack trace, который содержит информацию, с помощью которой можно определить причину исключения и место, где оно было брошено.
Exception in thread "main" java.lang.IllegalStateException: A book has a null property at com.example.myproject.Author.getBookIds(Author.java:38) at com.example.myproject.Bootstrap.main(Bootstrap.java:14) Caused by: java.lang.NullPointerException at com.example.myproject.Book.getId(Book.java:22) at com.example.myproject.Author.getBookIds(Author.java:35)

Все методы выводятся в обратном порядке вызовов. В примере исключение IllegalStateException было брошено в методе getBookIds , который был вызван в main . «Caused by» означает, что исключение NullPointerException является причиной IllegalStateException .

Разработка исключений

Чтобы определить собственное проверяемое исключение, необходимо создать наследника класса java.lang.Exception . Желательно, чтобы у исключения был конструкор, которому можно передать сообщение:

public class FooException extends Exception < public FooException() < super(); > public FooException(String message) < super(message); > public FooException(String message, Throwable cause) < super(message, cause); > public FooException(Throwable cause) < super(cause); > >

Исключения в Java7

  • обработка нескольких типов исключений в одном catch -блоке:

catch (IOException | SQLException ex)

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

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

  • Try с ресурсами позволяет прямо в try -блоке объявлять необходимые ресурсы, которые по завершению блока будут корректно закрыты (с помощью метода close() ). Любой объект реализующий java.lang.AutoCloseable может быть использован как ресурс.
static String readFirstLineFromFile(String path) throws IOException < try (BufferedReader br = new BufferedReader(new FileReader(path))) < return br.readLine(); > >

В приведенном примере в качестве ресурса использутся объект класса BufferedReader , который будет закрыт вне зависимосити от того, как выполнится try -блок.

Можно объявлять несколько ресурсов, разделяя их точкой с запятой:

public static void viewTable(Connection con) throws SQLException < String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES"; try (Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(query)) < //Work with Statement and ResultSet > catch (SQLException e) < e.printStackTrace; >>

Во время закрытия ресурсов тоже может быть брошено исключение. В try-with-resources добавленна возможность хранения «подавленных» исключений, и брошенное try -блоком исключение имеет больший приоритет, чем исключения получившиеся во время закрытия. Получить последние можно вызовом метода getSuppressed() от исключения брошенного try -блоком.

  • Перебрасывание исключений с улучшенной проверкой соответствия типов.

Компилятор Java SE 7 тщательнее анализирует перебрасываемые исключения. Рассмотрим следующий пример:

static class FirstException extends Exception < >static class SecondException extends Exception < >public void rethrowException(String exceptionName) throws Exception < try < if ("First".equals(exceptionName)) < throw new FirstException(); > else < throw new SecondException(); > > catch (Exception ex) < throw e; > >

В примере try -блок может бросить либо FirstException , либо SecondException . В версиях до Java SE 7 невозможно указать эти исключения в декларации метода, потому что catch -блок перебрасывает исключение ex , тип которого — Exception .

В Java SE 7 вы можете указать, что метод rethrowException бросает только FirstException и SecondException . Компилятор определит, что исключение Exception ex могло возникнуть только в try -блоке, в котором может быть брошено FirstException или SecondException . Даже если тип параметра catch — Exception , компилятор определит, что это экземпляр либо FirstException , либо SecondException :

public void rethrowException(String exceptionName) throws FirstException, SecondException < try < // . > catch (Exception e) < throw e; > >

Если FirstException и SecondException не являются наследниками Exception , то необходимо указать и Exception в объявлении метода.

Примеры исключений

  • любая операция может бросить VirtualMachineError . Как правило это происходит в результате системных сбоев.
  • OutOfMemoryError . Приложение может бросить это исключение, если, например, не хватает места в куче, или не хватает памяти для того, чтобы создать стек нового потока.
  • IllegalArgumentException используется для того, чтобы избежать передачи некорректных значений аргументов. Например:
public void f(Object a) < if (a == null) < throw new IllegalArgumentException("a must not be null"); > >
  • IllegalStateException возникает в результате некорректного состояния объекта. Например, использование объекта перед тем как он будет инициализирован.

Гарантии безопасности

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

  • Отсутствие гарантий (no exceptional safety). Если было брошено исключение, то не гарантируется, что все ресурсы будут корректно закрыты и что объекты, методы которых бросили исключения, могут в дальнейшем использоваться. Пользователю придется пересоздавать все необходимые объекты и он не может быть уверен в том, что может переиспозовать те же самые ресурсы.
  • Отсутствие утечек (no-leak guarantee). Объект, даже если какой-нибудь его метод бросает исключение, освобождает все ресурсы или предоставляет способ сделать это.
  • Слабые гарантии (weak exceptional safety). Если объект бросил исключение, то он находится в корректном состоянии, и все инварианты сохранены. Рассмотрим пример:
class Interval < //invariant: left double left; double right; //. >

Если будет брошено исключение в этом классе, то тогда гарантируется, что ивариант «левая граница интервала меньше правой» сохранится, но значения left и right могли измениться.

  • Сильные гарантии (strong exceptional safety). Если при выполнении операции возникает исключение, то это не должно оказать какого-либо влияния на состояние приложения. Состояние объектов должно быть таким же как и до вызовов методов.
  • Гарантия отсутствия исключений (no throw guarantee). Ни при каких обстоятельствах метод не должен генерировать исключения. В Java это невозможно, например, из-за того, что VirtualMachineError может произойти в любом месте, и это никак не зависит от кода. Кроме того, эту гарантию практически невозможно обеспечить в общем случае.

Источники

  • Обработка ошибок и исключения — Сайт Георгия Корнеева
  • Лекция Георгия Корнеева — Лекториум
  • The Java Tutorials. Lesson: Exceptions
  • Обработка исключений — Википедия
  • Throwable (Java Platform SE 7 ) — Oracle Documentation
  • try/catch/finally и исключения — www.skipy.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *