Что обозначается ключевым словом catch

Исключения в Java, Часть I (try-catch-finally)

Это первая часть статьи, посвященной такому языковому механизму Java как исключения (вторая (checked/unchecked) вот). Она имеет вводный характер и рассчитана на начинающих разработчиков или тех, кто только приступает к изучению языка.

Также я веду курс «Scala for Java Developers» на платформе для онлайн-образования udemy.com (аналог Coursera/EdX).

1. Ключевые слова: try, catch, finally, throw, throws

«Магия» (т.е. некоторое поведение никак не отраженное в исходном коде и потому неповторяемое пользователем) исключений #1 заключается в том, что catch, throw, throws можно использовать исключительно с java.lang.Throwable или его потомками.

throws:
Годится

catch:
Годится

throw:
Годится

Кроме того, throw требуется не-null аргумент, иначе NullPointerException в момент выполнения

throw и new — это две независимых операции. В следующем коде мы независимо создаем объект исключения и «бросаем» его

Однако, попробуйте проанализировать вот это

2. Почему используем System.err, а не System.out

System.out — buffered-поток вывода, а System.err — нет. Таким образом вывод может быть как таким

Так и вот таким (err обогнало out при выводе в консоль)

Давайте это нарисуем

когда Вы пишете в System.err — ваше сообщение тут же выводится на консоль, но когда пишете в System.out, то оно может на какое-то время быть буферизированно. Stacktrace необработанного исключение выводится через System.err, что позволяет им обгонять «обычные» сообщения.

3. Компилятор требует вернуть результат (или требует молчать)

Если в объявлении метода сказано, что он возвращает НЕ void, то компилятор зорко следит, что бы мы вернули экземпляр требуемого типа или экземпляр типа, который можно неявно привести к требуемому

вот так не пройдет (другой тип)

Вот так не выйдет — нет возврата

и вот так не пройдет (компилятор не может удостовериться, что возврат будет)

Компилятор отслеживает, что бы мы что-то вернули, так как иначе непонятно, что должна была бы напечатать данная программа

Из-забавного, можно ничего не возвращать, а «повесить метод»

Тут в d никогда ничего не будет присвоено, так как метод sqr повисает

Компилятор пропустит «вилку» (таки берем в квадрат ИЛИ висим)

Но механизм исключений позволяет НИЧЕГО НЕ ВОЗВРАЩАТЬ!

Итак, у нас есть ТРИ варианта для компилятора

Но КАКОЙ ЖЕ double вернет функция, бросающая RuntimeException?
А НИКАКОЙ!

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

Давайте рассмотрим некоторый пример из практики.

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

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

Мы не можем ничего не вернуть

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

Можно вернуть специальное значение, показывающее, что что-то не так (error code), но кто гарантирует, что его прочитают, а не просто воспользуются им?

Можем, конечно, целиком остановить виртуальную машину

Но «правильный путь» таков: если обнаружили возможное некорректное поведение, то
1. Вычисления остановить, сгенерировать сообщение-поломку, которое трудно игнорировать, предоставить пользователю информацию о причине, предоставить пользователю возможность все починить (загрузить белье назад и повторно нажать кнопку старт)

4. Нелокальная передача управления (nonlocal control transfer)

Механизм исключительных ситуация (исключений) — это механизм НЕЛОКАЛЬНОЙ ПЕРЕДАЧИ УПРАВЛЕНИЯ.
Что под этим имеется в виду?
Программа, в ходе своего выполнения (точнее исполнения инструкций в рамках отдельного потока), оперирует стеком («стопкой») фреймов. Передача управления осуществляется либо в рамках одного фрейма

и другие операторы.

return — выходим из ОДНОГО фрейма (из фрейма #4(метод h()))

throw — выходим из ВСЕХ фреймов

При помощи catch мы можем остановить летящее исключение (причина, по которой мы автоматически покидаем фреймы).
Останавливаем через 3 фрейма, пролетаем фрейм #4(метод h()) + пролетаем фрейм #3(метод g()) + фрейм #2(метод f())

Обратите внимание, стандартный сценарий работы был восстановлен в методе main() (фрейм #1)

Останавливаем через 2 фрейма, пролетаем фрейм #4(метод h()) + пролетаем фрейм #3(метод g())

Останавливаем через 1 фрейм (фактически аналог return, просто покинули фрейм «другим образом»)

Итак, давайте сведем все на одну картинку

5. try + catch (catch — полиморфен)

Напомним иерархию исключений

То, что исключения являются объектами важно для нас в двух моментах
1. Они образуют иерархию с корнем java.lang.Throwable (java.lang.Object — предок java.lang.Throwable, но Object — уже не исключение)
2. Они могут иметь поля и методы (в этой статье это не будем использовать)

По первому пункту: catch — полиморфная конструкция, т.е. catch по типу Parent перехватывает летящие экземпляры любого типа, который является Parent-ом (т.е. экземпляры непосредственно Parent-а или любого потомка Parent-а)

Даже так: в блоке catch мы будем иметь ссылку типа Exception на объект типа RuntimeException

catch по потомку не может поймать предка

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

По предыдущим примерам — надеюсь вы обратили внимание, что если исключение перехвачено, то JVM выполняет операторы идущие ПОСЛЕ последних скобок try+catch.
Но если не перехвачено, то мы
1. не заходим в блок catch
2. покидаем фрейм метода с летящим исключением

А что будет, если мы зашли в catch, и потом бросили исключение ИЗ catch?

В таком случае выполнение метода тоже прерывается (не печатаем «3»). Новое исключение не имеет никакого отношения к try-catch

Мы можем даже кинуть тот объект, что у нас есть «на руках»

И мы не попадем в другие секции catch, если они есть

Обратите внимание, мы не напечатали «3», хотя у нас летит Error а «ниже» расположен catch по Error. Но важный момент в том, что catch имеет отношение исключительно к try-секции, но не к другим catch-секциям.

Как покажем ниже — можно строить вложенные конструкции, но вот пример, «исправляющий» эту ситуацию

Как вы видели, мы можем расположить несколько catch после одного try.

Но есть такое правило — нельзя ставить потомка после предка! (RuntimeException после Exception)

Ставить брата после брата — можно (RuntimeException после Error)

Как происходит выбор «правильного» catch? Да очень просто — JVM идет сверху-вниз до тех пор, пока не найдет такой catch что в нем указано ваше исключение или его предок — туда и заходит. Ниже — не идет.

Выбор catch осуществляется в runtime (а не в compile-time), значит учитывается не тип ССЫЛКИ (Throwable), а тип ССЫЛАЕМОГО (Exception)

7. try + finally

finally-секция получает управление, если try-блок завершился успешно

finally-секция получает управление, даже если try-блок завершился исключением

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

finally-секция НЕ вызывается только если мы «прибили» JVM

System.exit(42) и Runtime.getRuntime().exit(42) — это синонимы

И при Runtime.getRuntime().halt(42) — тоже не успевает зайти в finally

exit() vs halt()
javadoc: java.lang.Runtime#halt(int status)
… Unlike the exit method, this method does not cause shutdown hooks to be started and does not run uninvoked finalizers if finalization-on-exit has been enabled. If the shutdown sequence has already been initiated then this method does not wait for any running shutdown hooks or finalizers to finish their work.

Однако finally-секция не может «починить» try-блок завершившийся исключение (заметьте, «more» — не выводится в консоль)

Трюк с «if (true) <. >» требуется, так как иначе компилятор обнаруживает недостижимый код (последняя строка) и отказывается его компилировать

И finally-секция не может «предотвратить» выход из метода, если try-блок вызвал return («more» — не выводится в консоль)

Однако finally-секция может «перебить» throw/return при помощи другого throw/return

finally-секция может быть использована для завершающего действия, которое гарантированно будет вызвано (даже если было брошено исключение или автор использовал return) по окончании работы

Например для освобождения захваченной блокировки

Или для закрытия открытого файлового потока

Специально для этих целей в Java 7 появилась конструкция try-with-resources, ее мы изучим позже.

Вообще говоря, в finally-секция нельзя стандартно узнать было ли исключение.
Конечно, можно постараться написать свой «велосипед»

Не рекомендуемые практики
— return из finally-секции (можем затереть исключение из try-блока)
— действия в finally-секции, которые могут бросить исключение (можем затереть исключение из try-блока)

8. try + catch + finally

Не заходим в catch, заходим в finally, продолжаем после оператора

Есть исключение и есть подходящий catch

Заходим в catch, заходим в finally, продолжаем после оператора

Есть исключение но нет подходящего catch

Не заходим в catch, заходим в finally, не продолжаем после оператора — вылетаем с неперехваченным исключением

9. Вложенные try + catch + finally

Операторы обычно допускают неограниченное вложение.
Пример с if

Суть в том, что try-cacth-finally тоже допускает неограниченное вложение.
Например вот так

Ну что же, давайте исследуем как это работает.

Вложенный try-catch-finally без исключения

Мы НЕ заходим в обе catch-секции (нет исключения), заходим в обе finally-секции и выполняем обе строки ПОСЛЕ finally.

Вложенный try-catch-finally с исключением, которое ПЕРЕХВАТИТ ВНУТРЕННИЙ catch

Мы заходим в ПЕРВУЮ catch-секцию (печатаем «3»), но НЕ заходим во ВТОРУЮ catch-секцию (НЕ печатаем «6», так как исключение УЖЕ перехвачено первым catch), заходим в обе finally-секции (печатаем «4» и «7»), в обоих случаях выполняем код после finally (печатаем «5»и «8», так как исключение остановлено еще первым catch).

Вложенный try-catch-finally с исключением, которое ПЕРЕХВАТИТ ВНЕШНИЙ catch

Мы НЕ заходим в ПЕРВУЮ catch-секцию (не печатаем «3»), но заходим в ВТОРУЮ catch-секцию (печатаем «6»), заходим в обе finally-секции (печатаем «4» и «7»), в ПЕРВОМ случае НЕ выполняем код ПОСЛЕ finally (не печатаем «5», так как исключение НЕ остановлено), во ВТОРОМ случае выполняем код после finally (печатаем «8», так как исключение остановлено).

Вложенный try-catch-finally с исключением, которое НИКТО НЕ ПЕРЕХВАТИТ

Мы НЕ заходим в ОБЕ catch-секции (не печатаем «3» и «6»), заходим в обе finally-секции (печатаем «4» и «7») и в обоих случаях НЕ выполняем код ПОСЛЕ finally (не печатаем «5» и «8», так как исключение НЕ остановлено), выполнение метода прерывается по исключению.

Контакты

Я занимаюсь онлайн обучением Java (вот курсы программирования) и публикую часть учебных материалов в рамках переработки курса Java Core. Видеозаписи лекций в аудитории Вы можете увидеть на youtube-канале, возможно, видео канала лучше систематизировано в этой статье.

Источник

try-catch (Справочник по C#)

Блок try содержит защищенный код, который может вызвать исключение. Этот блок выполняется, пока не возникнет исключение или пока он не будет успешно завершен. Например, следующая попытка приведения объекта null вызывает исключение NullReferenceException:

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

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

Вы можете перехватывать одно исключение и вызывать другое исключение. При этом следует указать перехватываемое исключение как внутреннее, как показано в следующем примере.

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

Дополнительные сведения о перехвате исключений см. в разделе try-catch-finally.

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

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

Задача может быть в состоянии сбоя, если в ожидаемом асинхронном методе произошло несколько исключений. Например, задача может быть результатом вызова метода Task.WhenAll. При ожидании такой задачи перехватывается только одно из исключений и невозможно предсказать, какое исключение будет перехвачено. См. пример в разделе Пример Task.WhenAll.

Пример

Пример двух блоков catch

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

Пример асинхронного метода

Пример Task.WhenAll

В следующем примере демонстрируется обработка исключений, когда несколько задач могут привести к нескольким исключениям. Блок try ожидает задачу, которая возвращается вызовом метода Task.WhenAll. Эта задача завершается после завершения трех задач, к которым применяется WhenAll.

Каждая из трех задач вызывает исключение. Блок catch выполняет итерацию по исключениям, которые обнаруживаются в свойстве Exception.InnerExceptions задачи, возвращенной методом Task.WhenAll.

Спецификация языка C#

Дополнительные сведения см. в разделе Оператор try в документации Спецификация C# 6.0.

Источник

Что обозначается ключевым словом catch

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

Для реализации механизма обработки исключений в язык Си++ введены следующие три ключевых (служебных) слова: try (контролировать), catch (ловить), throw (генерировать, порождать, бросать, посылать, формировать).

Служебное слово try позволяет выделить в любом месте исполняемого текста программы так называемый контролируемый блок:

Среди операторов, заключенных в фигурные скобки могут быть: описания, определения, обычные операторы языка Си++ и специальные операторы генерации (порождения, формирования) исключений:

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

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

Рассмотрим функцию для определения наибольшего общего делителя (НОД) двух целых чисел. Классический алгоритм Евклида определения наибольшего общего делителя двух целых чисел (x, y) может применяться только при следующих условиях:

На каждом шаге алгоритма выполняются сравнения:

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

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

В следующем примере определен класс Data с компонентами, позволяющими отображать в объекте-исключении как целочисленные параметры функции, так и указатель на строку с сообщением (о смысле события, при наступлении которого сформировано исключение).

Отметим, что объект класса Data формируется в теле функции при выполнении конструктора класса. Если бы этот объект не был исключением, он был бы локализован в теле функции и недоступен в точке ее вызова. Но по определению исключений они создаются как временные статические объекты. В данном примере исключения как безымянные объекты класса Data формируются в теле функции, вызываемой из контролируемого блока. В блоке обработчика исключений безымянный объект типа Data инициализирует переменную (параметр) Data d и тем самым информация из исключения становится доступной в теле обработчика исключений, что демонстрирует результат.

Синтаксис и семантика генерации и обработки исключений

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

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

Механизм исключений позволяет переносить анализ и обработку ситуации из точки ее возникновения (throw point), в другое место программы, специально предназначенное для ее обработки. Кроме того, из точки возникновения ситуации в место ее обработки (в список обработчиков исключений) может быть передано любое количество необходимой информации, например, сведения о том, какие данные и действия привели к возникновению такой ситуации.

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

Необходимо лишь помнить, что механизм исключений предназначен только для синхронных событий, то-есть таких, которые порождаются в результате работы самой программы (к примеру, попытка прерывания программы нажатием Ctrl+C во время ее выполнения не является синхронным событием).

Первый вариант подобен спецификации формального параметра и определении функции. Имя этого параметра используется в операторах обработки исключения. С его помощью к ним передается информация из обрабатываемого исключения.

Второй вариант не предполагает использования значения исключения. Для обработчика важен только его тип и факт его получения.

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

Продемонстрируем некоторые из перечисленных особенностей обработки исключений еще одной программой:

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

также соответствует этому исключению.

Генерация исключений

Выражение, формирующее исключение, может иметь две формы:

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

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

При вложении контролируемых блоков исключение, возникшее во внутреннем блоке, последовательно «просматривает» обработчики, переходя от внутреннего (вложенного) блока к внешнему до тех пор, пока не будет найдена подходящая процедура обработки. (Иногда действия по установлению соответствия между процедурой обработки исключением объясняют в обратном порядке. Говорят, что не исключение просматривает заголовок процедуры обработки, а обработчики анализируют исключение, посланное из контролируемого блока и последовательно проходящее через заголовки обработчиков. Однако это не меняет существа механизма.) Если во всей совокупности обработчиков не будет найден подходящий, то выполняется аварийное завершение программы с выдачей, например, такого сообщения: «Program Aborted». Аналогичная ситуация может возникнуть и при ретрансляции исключения, когда во внешних контролируемых блоках не окажется соответствующей исключению процедуры обработки.

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

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

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

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

Примеры прототипов функций с указанием генерируемых исключений:

В следующих примерах описываются еще некоторые функции с перечислением исключений:

Особое внимание необходимо обратить на перегрузку виртуальных функций тех классов, к которым относятся исключения. Речь идет о следующем. Пусть классы ALPHA и BETA определены следующим образом:

Теперь рассмотрим три ситуации:

Источник

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

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