Оператор try-catch-finally
В некоторых случаях один фрагмент кода может инициировать более одного исключения. Используется два или более операторов catch , каждый для перехвата своего типа исключений.
Когда возбуждается исключение, каждый оператор catch проверяется по порядку, и первый из них, чей тип соответствует исключению, выполняется.
После того как выполнится один из операторов catch , все остальные пропускаются, и выполнение программы продолжается с места, следующего за блоком try-catch .
Рассмотрим пример применения нескольких операторов catch :
public class MultiCatch < public static void main(String[] args) < try < int a = args.length; int b = 42 / a; int[] c = new int[a]; c[a] = 6; >catch (ArithmeticException e) < System.out.println("Деление на ноль." + e); >catch (ArrayIndexOutOfBoundsException e) < e.printStackTrace(); >System.out.println("Уже после блока try-catch-catch."); > >
Когда используются множественные операторы catch , важно помнить, что подклассы исключений должны следовать перед любыми их супер классами. Это потому, что оператор catch , который использует супер класс, будет перехватывать все исключения этого супер класса плюс всех его подклассов. То есть подкласс исключения никогда не будет обработан, если вы попытаетесь его перехватить после его супер класса. Более того, в Java недостижимый код является ошибкой:
public class SuperSubCatch < public static void main(String[] args) < try < int a = 0; int b = 42 / a; >catch (Exception e) < System.out.println("Перехват исключений общего класса Exception."); >/*catch (ArithmeticException e) < //ОШИБКА: недостижимый код ! System.out.println("Этoт код вообще недостижим."); >*/ > >
2. Объявление нескольких исключений в одном блоке catch (multi-catch блок)
try < . >catch(IllegalStateException e) < System.out.println(e.getMessage()); >catch(SQLException e) < System.out.println(e.getMessage()); >catch(ContextException e)
Стало после Java 7:
try < . >catch(IllegalStateException | SQLException | ContextException e)
Пример использования multi-catch блока:
public class MultiCatch2 < public static void main(String[] args) < try < int a = args.length; int b = 42 / a; int[] c = new int[a]; c[a] = 6; >catch (ArithmeticException | ArrayIndexOutOfBoundsException e) < System.out.println("Ошибка." + e); >System.out.println("Уже после блока try-catch."); > >
Параметр оператора catch должен быть фактически завершенным ( effective final ). Это означает, что ему нельзя присваивать новое значение в блоке оператора catch или же он должен быть явно объявлен как final .
Multi-catch блок может содержать только исключения не входящие в одну иерархию.
3. Оператор finally
Когда генерируется исключение, выполнение метода направляется по нелинейному пути. Если файл открывается в начале метода и закрывается в конце, то вряд ли кого-нибудь устроит, что код, закрывающий файл, будет обойден механизмом обработки исключений. Для таких непредвиденных обстоятельств и служит оператор finallу .
Оператор finallу образует блок кода, который будет выполнен по завершении блока операторов try-catch , но перед следующим за ним кодом.
Блок оператора finallу выполняется независимо от того, выброшено ли исключение или нет. Если исключение выброшено, блок оператора finallу выполняется, даже при условии, что ни один из операторов catch не совпадает с этим исключением. В любой момент, когда метод собирается возвратить управление вызывающему коду из блока оператора try-саtch (через необработанное исключение или явным образом через оператор return ), блок оператора finallу выполняется перед возвратом управления из метода.
public class FinallyUse < // Выход из метода через исключение public static void procA() < try < System.out.println("Внутри procA"); throw new RuntimeException("demo"); >finally < System.out.println("finally для procA "); >> // Возврат изнутри try-блока public static void procB() < try < System.out.println("Внутри procB"); return; >finally < System.out.println("finally для procB "); >> // Нормальное выполнение try-блока public static void procC() < try < System.out.println("Внутри procC"); >finally < System.out.println("finally procC"); >> public static void main(String[] args) < try < procA(); >catch (Exception e) < System.out.println("Исключение выброшено"); >procB(); procC(); > >
- Обработка исключений
- Классы исключений
- Создание подклассов исключений
- Оператор throw
- Оператор throws
- Исключения при наследовании
- Задания
Как работает try catch java
Нередко в процессе выполнения программы могут возникать ошибки, при том необязательно по вине разработчика. Некоторые из них трудно предусмотреть или предвидеть, а иногда и вовсе невозможно. Так, например, может неожиданно оборваться сетевое подключение при передаче файла. Подобные ситуации называются исключениями .
В языке Java предусмотрены специальные средства для обработки подобных ситуаций. Одним из таких средств является конструкция try. catch. finally . При возникновении исключения в блоке try управление переходит в блок catch, который может обработать данное исключение. Если такого блока не найдено, то пользователю отображается сообщение о необработанном исключении, а дальнейшее выполнение программы останавливается. И чтобы подобной остановки не произошло, и надо использовать блок try..catch. Например:
int[] numbers = new int[3]; numbers[4]=45; System.out.println(numbers[4]);
Так как у нас массив numbers может содержать только 3 элемента, то при выполнении инструкции numbers[4]=45 консоль отобразит исключение, и выполнение программы будет завершено. Теперь попробуем обработать это исключение:
try < int[] numbers = new int[3]; numbers[4]=45; System.out.println(numbers[4]); >catch(Exception ex) < ex.printStackTrace(); >System.out.println("Программа завершена");
При использовании блока try. catch вначале выполняются все инструкции между операторами try и catch. Если в блоке try вдруг возникает исключение, то обычный порядок выполнения останавливается и переходит к инструкции сatch. Поэтому когда выполнение программы дойдет до строки numbers[4]=45; , программа остановится и перейдет к блоку catch
Выражение catch имеет следующий синтаксис: catch (тип_исключения имя_переменной) . В данном случае объявляется переменная ex , которая имеет тип Exception . Но если возникшее исключение не является исключением типа, указанного в инструкции сatch, то оно не обрабатывается, а программа просто зависает или выбрасывает сообщение об ошибке.
Но так как тип Exception является базовым классом для всех исключений, то выражение catch (Exception ex) будет обрабатывать практически все исключения. Обработка же исключения в данном случае сводится к выводу на консоль стека трассировки ошибки с помощью метода printStackTrace() , определенного в классе Exception.
После завершения выполнения блока catch программа продолжает свою работу, выполняя все остальные инструкции после блока catch.
Конструкция try..catch также может иметь блок finally . Однако этот блок необязательный, и его можно при обработке исключений опускать. Блок finally выполняется в любом случае, возникло ли исключение в блоке try или нет:
try < int[] numbers = new int[3]; numbers[4]=45; System.out.println(numbers[4]); >catch(Exception ex) < ex.printStackTrace(); >finally < System.out.println("Блок finally"); >System.out.println("Программа завершена");
Обработка нескольких исключений
В Java имеется множество различных типов исключений, и мы можем разграничить их обработку, включив дополнительные блоки catch:
int[] numbers = new int[3]; try < numbers[6]=45; numbers[6]=Integer.parseInt("gfd"); >catch(ArrayIndexOutOfBoundsException ex) < System.out.println("Выход за пределы массива"); >catch(NumberFormatException ex)
Если у нас возникает исключение определенного типа, то оно переходит к соответствующему блоку catch.
Оператор throw
Чтобы сообщить о выполнении исключительных ситуаций в программе, можно использовать оператор throw . То есть с помощью этого оператора мы сами можем создать исключение и вызвать его в процессе выполнения. Например, в нашей программе происходит ввод числа, и мы хотим, чтобы, если число больше 30, то возникало исключение:
package firstapp; import java.util.Scanner; public class FirstApp < public static void main(String[] args) < try< Scanner in = new Scanner(System.in); int x = in.nextInt(); if(x>=30) < throw new Exception("Число х должно быть меньше 30"); >> catch(Exception ex) < System.out.println(ex.getMessage()); >System.out.println("Программа завершена"); > >
Здесь для создания объекта исключения используется конструктор класса Exception, в который передается сообщение об исключении. И если число х окажется больше 29, то будет выброшено исключение и управление перейдет к блоку catch.
В блоке catch мы можем получить сообщение об исключении с помощью метода getMessage() .
Перехват ошибок, «try..catch»
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/try-catch.
Как бы мы хорошо ни программировали, в коде бывают ошибки. Или, как их иначе называют, «исключительные ситуации» (исключения).
Обычно скрипт при ошибке, как говорят, «падает», с выводом ошибки в консоль.
Но бывают случаи, когда нам хотелось бы как-то контролировать ситуацию, чтобы скрипт не просто «упал», а сделал что-то разумное.
Для этого в JavaScript есть замечательная конструкция try..catch .
Конструкция try…catch
Конструкция try..catch состоит из двух основных блоков: try , и затем catch :
try < // код . >catch (err) < // обработка ошибки >
Работает она так:
- Выполняется код внутри блока try .
- Если в нём ошибок нет, то блок catch(err) игнорируется, то есть выполнение доходит до конца try и потом прыгает через catch .
- Если в нём возникнет ошибка, то выполнение try на ней прерывается, и управление прыгает в начало блока catch(err) . При этом переменная err (можно выбрать и другое название) будет содержать объект ошибки с подробной информацией о произошедшем.
Таким образом, при ошибке в try скрипт не «падает», и мы получаем возможность обработать ошибку внутри catch .
Посмотрим это на примерах.
-
Пример без ошибок: при запуске сработают alert (1) и (2) :
try < alert('Начало блока try'); // (1) catch(e) < alert('Блок catch не получит управление, так как нет ошибок'); // (3) >alert("Потом код продолжит выполнение. ");
try < alert('Начало блока try'); // (1) catch(e) < alert('Ошибка ' + e.name + ":" + e.message + "\n" + e.stack); // (3) alert("Потом код продолжит выполнение. ");
try..catch подразумевает, что код синтаксически верен
Если грубо нарушена структура кода, например не закрыта фигурная скобка или где-то стоит лишняя запятая, то никакой try..catch здесь не поможет. Такие ошибки называются синтаксическими, интерпретатор не может понять такой код.
Здесь же мы рассматриваем ошибки семантические, то есть происходящие в корректном коде, в процессе выполнения.
try..catch работает только в синхронном коде
Ошибку, которая произойдёт в коде, запланированном «на будущее», например в setTimeout , try..catch не поймает:
try < setTimeout(function() < throw new Error(); // вылетит в консоль >, 1000); > catch (e)
На момент запуска функции, назначенной через setTimeout , этот код уже завершится, интерпретатор выйдет из блока try..catch .
Чтобы поймать ошибку внутри функции из setTimeout , и try..catch должен быть в той же функции.
Объект ошибки
В примере выше мы видим объект ошибки. У него есть три основных свойства:
name Тип ошибки. Например, при обращении к несуществующей переменной: "ReferenceError" . message Текстовое сообщение о деталях ошибки. stack Везде, кроме IE8-, есть также свойство stack , которое содержит строку с информацией о последовательности вызовов, которая привела к ошибке.
В зависимости от браузера у него могут быть и дополнительные свойства, см. Error в MDN и Error в MSDN.
Пример использования
В JavaScript есть встроенный метод JSON.parse(str), который используется для чтения JavaScript-объектов (и не только) из строки.
Обычно он используется для того, чтобы обрабатывать данные, полученные по сети, с сервера или из другого источника.
Мы получаем их и вызываем метод JSON.parse , вот так:
var data = ''; // строка с данными, полученная с сервера var user = JSON.parse(data); // преобразовали строку в объект // теперь user -- это JS-объект с данными из строки alert( user.name ); // Вася alert( user.age ); // 30
Более детально формат JSON разобран в главе Формат JSON, метод toJSON.
В случае, если данные некорректны, JSON.parse генерирует ошибку, то есть скрипт «упадёт».
Устроит ли нас такое поведение? Конечно нет!
Получается, что если вдруг что-то не так с данными, то посетитель никогда (если, конечно, не откроет консоль) об этом не узнает.
А люди очень-очень не любят, когда что-то «просто падает», без всякого объявления об ошибке.
Бывают ситуации, когда без try..catch не обойтись, это – одна из таких.
Используем try..catch , чтобы обработать некорректный ответ:
var data = "Has Error"; // в данных ошибка try < var user = JSON.parse(data); // catch (e) < // . выполнится catch alert( "Извините, в данных ошибка, мы попробуем получить их ещё раз" ); alert( e.name ); alert( e.message ); >
Здесь в alert только выводится сообщение, но область применения гораздо шире: можно повторять запрос, можно предлагать посетителю использовать альтернативный способ, можно отсылать информацию об ошибке на сервер… Свобода действий.
Генерация своих ошибок
Представим на минуту, что данные являются корректным JSON… Но в этом объекте нет нужного свойства name :
var data = '< "age": 30 >'; // данные неполны try < var user = JSON.parse(data); // catch (e) < // не выполнится alert( "Извините, в данных ошибка" ); >
Вызов JSON.parse выполнится без ошибок, но ошибка в данных есть. И, так как свойство name обязательно должно быть, то для нас это такие же некорректные данные, как и "Has Error" .
Для того, чтобы унифицировать и объединить обработку ошибок парсинга и ошибок в структуре, мы воспользуемся оператором throw .
Оператор throw
Оператор throw генерирует ошибку.
Технически в качестве объекта ошибки можно передать что угодно, это может быть даже не объект, а число или строка, но всё же лучше, чтобы это был объект, желательно – совместимый со стандартным, то есть чтобы у него были как минимум свойства name и message .
В качестве конструктора ошибок можно использовать встроенный конструктор: new Error(message) или любой другой.
В JavaScript встроен ряд конструкторов для стандартных ошибок: SyntaxError , ReferenceError , RangeError и некоторые другие. Можно использовать и их, но только чтобы не было путаницы.
В данном случае мы используем конструктор new SyntaxError(message) . Он создаёт ошибку того же типа, что и JSON.parse .
var data = '< "age": 30 >'; // данные неполны try < var user = JSON.parse(data); // alert( user.name ); > catch (e)
Получилось, что блок catch – единое место для обработки ошибок во всех случаях: когда ошибка выявляется при JSON.parse или позже.
Проброс исключения
В коде выше мы предусмотрели обработку ошибок, которые возникают при некорректных данных. Но может ли быть так, что возникнет какая-то другая ошибка?
Конечно, может! Код – это вообще мешок с ошибками, бывает даже так, что библиотеку выкладывают в открытый доступ, она там 10 лет лежит, её смотрят миллионы людей и на 11-й год находятся опаснейшие ошибки. Такова жизнь, таковы люди.
Блок catch в нашем примере предназначен для обработки ошибок, возникающих при некорректных данных. Если же в него попала какая-то другая ошибка, то вывод сообщения о «некорректных данных» будет дезинформацией посетителя.
Ошибку, о которой catch не знает, он не должен обрабатывать.
Такая техника называется «проброс исключения»: в catch(e) мы анализируем объект ошибки, и если он нам не подходит, то делаем throw e .
При этом ошибка «выпадает» из try..catch наружу. Далее она может быть поймана либо внешним блоком try..catch (если есть), либо «повалит» скрипт.
В примере ниже catch обрабатывает только ошибки SyntaxError , а остальные – выбрасывает дальше:
var data = '< "name": "Вася", "age": 30 >'; // данные корректны try < var user = JSON.parse(data); if (!user.name) < throw new SyntaxError("Ошибка в данных"); >blabla(); // произошла непредусмотренная ошибка alert( user.name ); > catch (e) < if (e.name == "SyntaxError") < alert( "Извините, в данных ошибка" ); >else < throw e; >>
Заметим, что ошибка, которая возникла внутри блока catch , «выпадает» наружу, как если бы была в обычном коде.
В следующем примере такие ошибки обрабатываются ещё одним, «более внешним» try..catch :
function readData() < var data = '< "name": "Вася", "age": 30 >'; try < // . blabla(); // ошибка! >catch (e) < // . if (e.name != 'SyntaxError') < throw e; // пробрасываем >> > try < readData(); >catch (e) < alert( "Поймал во внешнем catch: " + e ); // ловим >
В примере выше try..catch внутри readData умеет обрабатывать только SyntaxError , а внешний – все ошибки.
Без внешнего проброшенная ошибка «вывалилась» бы в консоль с остановкой скрипта.
Оборачивание исключений
И, для полноты картины – последняя, самая продвинутая техника по работе с ошибками. Она, впрочем, является стандартной практикой во многих объектно-ориентированных языках.
Цель функции readData в примере выше – прочитать данные. При чтении могут возникать разные ошибки, не только SyntaxError , но и, возможно, к примеру URIError (неправильное применение функций работы с URI) да и другие.
Код, который вызвал readData , хотел бы иметь либо результат, либо информацию об ошибке.
При этом очень важным является вопрос: обязан ли этот внешний код знать о всевозможных типах ошибок, которые могут возникать при чтении данных, и уметь перехватывать их?
Обычно внешний код хотел бы работать «на уровень выше», и получать либо результат, либо «ошибку чтения данных», при этом какая именно ошибка произошла – ему неважно. Ну, или, если будет важно, то хотелось бы иметь возможность это узнать, но обычно не требуется.
Это важнейший общий подход к проектированию – каждый участок функциональности должен получать информацию на том уровне, который ей необходим.
Мы его видим везде в грамотно построенном коде, но не всегда отдаём себе в этом отчёт.
В данном случае, если при чтении данных происходит ошибка, то мы будем генерировать её в виде объекта ReadError , с соответствующим сообщением. А «исходную» ошибку на всякий случай тоже сохраним, присвоим в свойство cause (англ. – причина).
Выглядит это так:
function ReadError(message, cause) < this.message = message; this.cause = cause; this.name = 'ReadError'; this.stack = cause.stack; >function readData() < var data = '< bad data >'; try < // . JSON.parse(data); // . >catch (e) < // . if (e.name == 'URIError') < throw new ReadError("Ошибка в URI", e); >else if (e.name == 'SyntaxError') < throw new ReadError("Синтаксическая ошибка в данных", e); >else < throw e; // пробрасываем >> > try < readData(); >catch (e) < if (e.name == 'ReadError') < alert( e.message ); alert( e.cause ); // оригинальная ошибка-причина >else < throw e; >>
Этот подход называют «оборачиванием» исключения, поскольку мы берём ошибки «более низкого уровня» и «заворачиваем» их в ReadError , которая соответствует текущей задаче.
Секция finally
Конструкция try..catch может содержать ещё один блок: finally .
Выглядит этот расширенный синтаксис так:
try < .. пробуем выполнить код .. >catch(e) < .. перехватываем исключение .. >finally
Секция finally не обязательна, но если она есть, то она выполняется всегда:
- после блока try , если ошибок не было,
- после catch , если они были.
Попробуйте запустить такой код?
Как работает try catch java
Конструкция try-catch в Java используется для обработки исключений в программе. Идея состоит в том, что вы помещаете код, который может вызвать исключение, в блок try , а затем обрабатываете возможные исключения в блоке catch
Вот как это выглядит:
Copy code try // код, который может вызвать исключение > catch (ExceptionType1 e1) // обработка исключения типа ExceptionType1 > catch (ExceptionType2 e2) // обработка исключения типа ExceptionType2 > finally // код, который выполняется всегда, независимо от того, возникло исключение или нет >
В блоке try помещается код, который может вызвать исключение. Если исключение происходит в блоке try , программа переходит в соответствующий блок catch , где обрабатывается исключение.
Блок finally опционален и выполняется всегда, независимо от того, было ли брошено исключение или нет. Этот блок часто используется для закрытия ресурсов (например, файлов), которые были открыты в блоке try
Для работы с потоками данных в Java , их также можно использовать в блоке try с ресурсами. В этом случае, когда блок try завершится.
Пример использования потоков в блоке try с ресурсами:
try (InputStream input = new FileInputStream("file.txt"); OutputStream output = new FileOutputStream("output.txt")) byte[] buffer = new byte[1024]; int length; while ((length = input.read(buffer)) > 0) output.write(buffer, 0, length); > > catch (IOException e) e.printStackTrace(); >
В этом примере, FileInputStream и FileOutputStream используются в блоке try с ресурсами, чтобы открыть файлы для чтения и записи. Когда выполнение блока try завершится, потоки будут автоматически закрыты благодаря этому блоку.
