Можно ли использовать try без блока catch

Урок №182. Обработка исключений. Операторы throw, try и catch

Обновл. 26 Мар 2021 |

На предыдущем уроке мы говорили о необходимости и пользе исключений. Исключения в языке C++ реализованы с помощью 3-х ключевых слов, которые работают в связке друг с другом: throw, try и catch.

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

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

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

Для использования оператора throw применяется ключевое слово throw, а за ним указывается значение любого типа данных, которое вы хотите задействовать, чтобы сигнализировать об ошибке. Как правило, этим значением является код ошибки, описание проблемы или настраиваемый класс-исключение. Например:

throw -1; // генерация исключения типа int

throw ENUM_INVALID_INDEX; // генерация исключения типа enum

throw «Can not take square root of negative number»; // генерация исключения типа const char* (строка C-style)

throw dX; // генерация исключения типа double (переменная типа double, которая была определена ранее)

throw MyException(«Fatal Error»); // генерация исключения с использованием объекта класса MyException

Каждая из этих строк сигнализирует о том, что возникла какая-то ошибка, которую нужно обработать.

Поиск исключений

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

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

try

{

// Здесь мы пишем стейтменты, которые будут генерировать следующее исключение

throw -1; // типичный стейтмент throw

}

Обратите внимание, блок try не определяет, КАК мы будем обрабатывать исключение. Он просто сообщает компилятору: «Эй, если какой-либо из стейтментов внутри этого блока try сгенерирует исключение — поймай его!».

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

Пока арбитр не объявит о штрафном броске, и пока этот штрафной бросок не будет выполнен, игра не возобновится. Другими словами, штрафной бросок должен быть обработан до возобновления игры.

Фактически, обработка исключений — это работа блока(ов) catch. Ключевое слово catch используется для определения блока кода (так называемого «блока catch»), который обрабатывает исключения определенного типа данных.

Вот пример блока catch, который обрабатывает (ловит) исключения типа int:

catch (int a)

{

// Обрабатываем исключение типа int

std::cerr << «We caught an int exception with value» << a << ‘n’;

}

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

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

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

Как и в случае с функциями, если параметр не используется в блоке catch, то имя переменной можно не указывать:

catch (double) // примечание: Мы не указываем имя переменной, так как в этом нет надобности (мы её нигде в блоке не используем)

{

// Обрабатываем исключение типа double здесь

std::cerr << «We caught an exception of type double» << ‘n’;

}

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

Использование throw, try и catch вместе

Вот полная программа, которая использует throw, try и несколько блоков catch:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

#include <iostream>

#include <string>

int main()

{

try

{

// Здесь мы пишем стейтменты, которые будут генерировать следующее исключение

throw -1; // типичный стейтмент throw

}

catch (int a)

{

// Любые исключения типа int, сгенерированные в блоке try, приведенном выше, обрабатываются здесь

std::cerr << «We caught an int exception with value: » << a << ‘n’;

}

catch (double) // мы не указываем имя переменной, так как в этом нет надобности (мы её нигде в блоке не используем)

{

// Любые исключения типа double, сгенерированные в блоке try, приведенном выше, обрабатываются здесь

std::cerr << «We caught an exception of type double» << ‘n’;

}

catch (const std::string &str) // ловим исключения по константной ссылке

{

// Любые исключения типа std::string, сгенерированные внутри блока try, приведенном выше, обрабатываются здесь

std::cerr << «We caught an exception of type std::string» << ‘n’;

}

std::cout << «Continuing our way!n»;

return 0;

}

Читайте также:  Можно ли использовать веб камеру как камеру заднего

Результат выполнения программы:

We caught an int exception with value -1

Continuing our way!

Оператор throw используется для генерации исключения -1 типа int. Затем блок try обнаруживает оператор throw и перемещает его в соответствующий блок catch, который обрабатывает исключения типа int. Блок catch типа int и выводит соответствующее сообщение об ошибке.

После обработки исключения, программа продолжает свое выполнение и выводит на экран Continuing our way!.

Резюмируем

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

При выбрасывании исключения (оператор throw), точка выполнения программы немедленно переходит к ближайшему блоку try. Если какой-либо из обработчиков catch, прикрепленных к блоку try, обрабатывает этот тип исключения, то точка выполнения переходит в этот обработчик и, после выполнения кода блока catch, исключение считается обработанным.

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

Обратите внимание, компилятор не выполняет неявные преобразования при сопоставлении исключений с блоками catch! Например, исключение типа char не будет обрабатываться блоком catch типа int, а исключение типа int, в свою очередь, не будет обрабатываться блоком catch типа float.

Это действительно всё, что вам нужно запомнить.

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

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

#include <iostream>

int main()

{

try

{

throw 7.4; // выбрасывается исключение типа double

std::cout << «This never sn»;

}

catch(double a) // обрабатывается исключение типа double

{

std::cerr << «We caught a double of value: » << a << ‘n’;

}

return 0;

}

Рассмотрим выполнение этой программы пошагово:

Оператор throw — это первый оператор, который выполняется. Это приводит к генерации исключения типа double.

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

Затем проверяются обработчики catch на соответствие типу данных. Поскольку у нас исключение типа double, то компилятор ищет обработчик catch типа double. У нас такой есть, поэтому он и выполняется.

Следовательно, результат выполнения программы:

We caught a double of value: 7.4

Обратите внимание, строка This never s никогда не выводится, так как генерация исключения заставило точку выполнения программы немедленно перейти к обработчику исключений типа double.

Еще один пример

Рассмотрим более популярный пример:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#include <iostream>

#include «math.h» // для функции sqrt()

int main()

{

std::cout << «Enter a number: «;

double a;

std::cin >> a;

try // ищем исключения внутри этого блока и отправляем их в соответствующий обработчик catch

{

// Если пользователь ввел отрицательное число, то выбрасывается исключение

if (a < 0.0)

throw «Can not take sqrt of negative number»; // выбрасывается исключение типа const char*

// Если пользователь ввел положительное число, то выполняется операция и выводится результат

std::cout << «The sqrt of » << a << » is » << sqrt(a) << ‘n’;

}

catch (const char* exception) // обработчик исключений типа const char*

{

std::cerr << «Error: » << exception << ‘n’;

}

}

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

Результат:

Enter a number: 16

The sqrt of 16 is 4

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

Результат:

Enter a number: -3

Error: Can not take sqrt of negative number

Что обычно делают блоки catch?

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

Во-первых, блок catch может вывести сообщение об ошибке (либо в консоль, либо в лог-файл).

Во-вторых, блок catch может возвратить значение или код ошибки обратно в caller.

Читайте также:  Меновазин при беременности можно ли использовать при боли в пояснице

В-третьих, блок catch может сгенерировать другое исключения. Поскольку блок catch не находится внутри блока try, то новое сгенерированное исключение будет обрабатываться следующим блоком try.

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

Оценить статью:

Загрузка…

Источник

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

В повседневной жизни иногда возникают ситуации, которые мы не планировали. Например, встаешь утром на работу, ищешь зарядное устройство к телефону — а его нет. Идешь в ванную, чтобы умыться — отключили воду. Сел в машину — не заводится. Но человек в состоянии довольно легко справиться с такими непредвиденными ситуациями. А как с ними справляются Java-программы, постараемся разобраться в этой статье. Исключения в Java - 1

Что такое исключения / exception java?

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

Кратко о ключевых словах try, catch, finally, throws

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

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

Эти ключевые слова используются для создания в программном коде специальных обрабатывающих конструкций: try{}catch, try{}catch{}finally, try{}finally{}.

  • throw — используется для возбуждения исключения;
  • throws — используется в сигнатуре методов для предупреждения, о том что метод может выбросить исключение.

Пример использования ключевых слов в Java-программе:

//метод считывает строку с клавиатуры public String input() throws MyException {//предупреждаем с помощью throws, // что метод может выбросить исключение MyException BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String s = null; //в блок try заключаем код, в котором может произойти исключение, в данном // случае компилятор нам подсказывает, что метод readLine() класса // BufferedReader может выбросить исключение ввода/вывода try { s = reader.readLine(); // в блок catch заключаем код по обработке исключения IOException } catch (IOException e) { System.out.ln(e.getMessage()); // в блоке finally закрываем поток чтения } finally { // при закрытии потока тоже возможно исключение, например, если он не был открыт, поэтому «оборачиваем» код в блок try try { reader.close(); // пишем обработку исключения при закрытии потока чтения } catch (IOException e) { System.out.ln(e.getMessage()); } } if (s.equals(«»)) { // мы решили, что пустая строка может нарушить в дальнейшем работу нашей программы, например, на результате этого метода нам надо вызывать метод substring(1,2), поэтому мы вынуждены прервать выполнение программы с генерацией своего типа исключения MyException с помощью throw throw new MyException(«String can not be empty!»); } return s; }

Для чего нужен механизм исключений?

Посмотрим на пример из реального мира. Представьте, что на автомобильной дороге есть участок с аварийным мостом с ограниченной грузоподъемностью. Если по нему поедет автомобиль с массой, превышающей грузоподъемность моста, он может разрушиться, и ситуация для водителя может стать, мягко говоря, исключительной. Чтобы этого не произошло, дорожная служба заблаговременно устанавливает предупредительные знаки на дороге. Водитель автомобиля, глядя на предупреждающий знак, будет сравнивать массу своего автомобиля с разрешенной для проезда по мосту. Если она превышает ее — он поедет по объездному пути. Благодаря действиям дорожной службы водители грузового транспорта, во-первых, получили возможность заблаговременно изменять свой путь, во-вторых, предупреждены об опасности на основном пути, и, наконец, предупреждены о невозможности использования моста при определенных условиях.

Исключения в Java - 2

Возможность предупреждения и разрешения исключительной ситуации в программе для ее продолжения — одна из причин использования исключений в Java. Механизм исключений также позволяет защитить написанный вами код (программный интерфейс) от неправильного использования пользователем за счет валидации (проверки) входящих данных. Давайте теперь на секунду побудем дорожной службой. Во-первых, вы должны знать места, где автомобилистов могут ждать неприятности. Во-вторых, вам нужно заготовить и установить предупредительные знаки. И, наконец, вам нужно предусмотреть объездные маршруты в случае опасности на основном пути. В Java механизм исключений работает похожим образом. На стадии разработки программы мы «ограждаем» опасные участки кода в отношении исключений с помощью блока try{}, предусматриваем «запасные» пути с помощью блока catch{}, в блоке finally{} мы пишем код, который выполняется в программе при любом исходе. В случаях, когда мы не можем предусмотреть «запасной путь» или намеренно хотим предоставить право его выбора пользователю, мы должны, по крайней мере, предупредить его об опасности. Почему? А вы только вообразите негодование водителя, который доедет до аварийного моста, по которому нельзя проехать, не встретив по дороге ни одного предупреждающего знака! В программировании при написании своих классов и методов мы не всегда можем предвидеть контекст их использования другими разработчиками в своих программах, поэтому не можем предвидеть на 100% правильный путь для разрешения исключительной ситуации. В то же время, правило хорошего тона — предупредить пользователей нашего кода о возможности исключительной ситуации. Механизм исключений Java позволяет нам сделать это с помощью throws — по сути, объявления общего поведения нашего метода, заключающееся в выбрасывании исключения, и предоставляя, таким образом, написание кода по обработке исключения в Java пользователю метода.

Читайте также:  Можно ли использовать летом масло 5w40

Предупреждение о «неприятностях»

Когда вы не планируете обрабатывать исключение в своем методе, но хотите предупредить пользователей метода о возможных исключительных ситуациях — используйте ключевое слово throws. Это ключевое слово в сигнатуре метода означает, что при определенных условиях метод, может выбросить исключение. Такое предупреждение является частью интерфейса метода и предоставляет право пользователю на собственный вариант реализации обработчика исключения. После throws мы указываем тип выбрасываемого исключения. Обычно это наследники класса Exception Java. Поскольку Java является объектно-ориентированным языком, все исключения в Java представляют собой объекты.

Исключения в Java - 3

Иерархия исключений Java

При возникновении ошибки в процессе выполнения программы исполняющая среда JVM создает объект нужного типа из иерархии исключений Java — множества возможных исключительных ситуаций, унаследованных от общего «предка» — класса Throwable. Исключительные ситуации, возникающие в программе, можно разделить на две группы:

  1. Ситуации, при которых восстановление дальнейшей нормальной работы программы невозможно
  2. Восстановление возможно.

К первой группе относят ситуации, когда возникают исключения, унаследованные из класса Error. Это ошибки, возникающие при выполнении программы в результате сбоя работы JVM, переполнения памяти или сбоя системы. Обычно они свидетельствуют о серьезных проблемах, устранить которые программными средствами невозможно. Такой вид исключений в Java относится к неконтролируемым (unchecked) на стадии компиляции. К этой группе также относят RunException — исключения, наследники класса Exception, генерируемые JVM во время выполнения программы. Часто причиной возникновения их являются ошибки программирования. Эти исключения также являются неконтролируемыми (unchecked) на стадии компиляции, поэтому написание кода по их обработке не является обязательным. Ко второй группе относят исключительные ситуации, предвидимые еще на стадии написания программы, и для которых должен быть написан код обработки. Такие исключения являются контролируемыми (checked). Основная часть работы разработчика на Java при работе с исключениями — обработка таких ситуаций.

Создание исключения

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

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

Создание блоков кода, для которых мы предусматриваем обработку исключений в Java, производится в программе с помощью конструкций try{}catch, try{}catch{}finally, try{}finally{}.

Исключения в Java - 4

При возбуждении исключения в блоке try обработчик исключения ищется в следующем за ним блоке catch. Если в catch есть обработчик данного типа исключения — управление переходит к нему. Если нет, то JVM ищет обработчик этого типа исключения в цепочке вызовов методов до тех пор, пока не будет найден подходящий catch. После выполнения блока catch управление передается в необязательный блок finally. В случае, если подходящий блок catch не найден, JVM останавливает выполнение программы, и выводит стек вызовов методов — stack trace, выполнив перед этим код блока finally при его наличии. Пример обработки исключений:

public class { void (String s) { if (s == null) { throw new NullPointerException(«Exception: s is null!»); } System.out.ln(«Inside method : » + s); } public ic void main(String[] args) { = new (); List list= Arrays.asList(«first step», null, «second step»); for (String s:list) { try { .(s); } catch (NullPointerException e) { System.out.ln(e.getMessage()); System.out.ln(«Exception was processed. Program continues»); } finally { System.out.ln(«Inside bloсk finally»); } System.out.ln(«Go program….»); System.out.ln(«——————«); } } }

Результаты работы метода main:

Inside method : first step Inside bloсk finally Go program…. —————— Exception: s is null! Exception was processed. Program continues Inside bloсk finally Go program…. —————— Inside method : second step Inside bloсk finally Go program…. ——————

Блок finally обычно используется для того, чтобы закрыть открытые в блоке try потоки или освободить ресурсы. Однако при написании программы не всегда возможно уследить за закрытием всех ресурсов. Для облегчения нашей жизни разработчики Java предложили нам конструкцию try-with-resources, которая автоматически закрывает ресурсы, открытые в блоке try. Наш первый пример можно переписать так с помощью try-with-resources:

public String input() throws MyException { String s = null; try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){ s = reader.readLine(); } catch (IOException e) { System.out.ln(e.getMessage()); } if (s.equals(«»)){ throw new MyException («String can not be empty!»); } return s; }

Благодаря возможностям Java, начиная с версии 7, мы также можем объединять перехват разнотипных исключений в одном блоке, делая код более компактным и читабельным. Например:

public String input() { String s = null; try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { s = reader.readLine(); if (s.equals(«»)) { throw new MyException(«String can not be empty!»); } } catch (IOException | MyException e) { System.out.ln(e.getMessage()); } return s; }

Итоги

Использование исключений в Java позволяет повысить отказоустойчивость программы за счет использования «запасных» путей, отделить логику основного кода от кода обработки исключительных ситуаций за счет использования блоков catch, а также дает нам возможность переложить обработку исключений на пользователя нашего кода с помощью throws.

Источник