Исключение — это нештатная ситуация, возникающая во время выполнения
последовательности кода. Обработка исключений Java управляется пятью ключевыми словами: try, catch, throw, throws и finally.
Скачать исходники для статьи можно ниже
Если кратко, они работают следующим образом.
Операторы программы, которые вы хотите отслеживать на предмет исключений, помещаются в блок try. Если исключение происходит в блоке try, оно создается и передается. Ваш код может перехватить исключение (используя блок catch) и обработать его некоторым осмысленным способом.
Системные исключения автоматически передаются системой времени выполнения Java.
Чтобы передать исключение вручную, используется ключевое слово throw. Любое исключение, которое создается и передается внутри метода, должно быть указано в его интерфейсе ключевым словом throws. Любой код, который в обязательном порядке должен быть выполнен после завершения блока try, помещается в блок finally.
Ниже показана общая форма блока обработки исключений.
try { // блок кода, в котором отслеживаются ошибки } catch (тип_исключения_1 exOb) { // обработчик исключений типа тип_исключения_1 } catch (тип_исключения_2 ехОb) { // обработчик исключений типа тип_исключения_2 } II... finally { // блок кода, который должен быть выполнен // после завершения блока try }
Здесь тип_исключения — тип происходящего исключения.
Использование блоков try и catch.
Обработка исключений дает два существенных преимущества.
Во-первых, вы получаете возможность исправить ошибку.
Во-вторых, предотвращается автоматическое прерывание выполнения программы.
Большинство пользователей будут недовольны (и это как минимум), если ваша программа будет останавливаться и выводить какие-то непонятные записи, всякий раз при возникновении ошибки.
К счастью, предотвратить это достаточно просто. Чтобы противостоять
этому и обрабатывать ошибки, нужно просто поместить код, в котором, как вы предполагаете может быть ошибка, внутрь блока try.
Непосредственно за блоком try следует включить конструкцию catch, которая задает тип перехватываемого исключения. Чтобы проиллюстрировать, насколько это просто делается, в следующую программу включен блок try с конструкцией catch, который обрабатывает исключение ArithmeticException, создаваемое в результате попытки деления на нуль.
class Ехс2 { public static void main(String args[]) { int d, a; try { // Мониторинг блока кода, d = 0; a = 42 / d; System.out.println("Это не будет выведено."); } catch (ArithmeticException e) { // перехват ошибки // деления на нуль System.out.println("Деление на нуль."); } System.out.printIn("После оператора catch."); } }
Эта программа создает следующий вывод:
Деление на нуль.
После оператора catch.
Обратите внимание на то, что вызов метода println() внутри блока try никогда не будет выполняться. Как только исключение передано, управление передается из блока try в блок catch.
То есть строка “Это не будет выведено” не отображается.
После того как блок catch будет выполнен, управление передается на
строку программы, следующую за всем блоком try/catch.
Операторы try и catch составляют единый узел. Область действия блока
catch не распространяется на те операторы, которые идут перед оператором try.
Оператор catch не может перехватить исключение, переданное другим оператором try. Операторы, которые защищены блоком try, должны быть заключены в фигурные скобки (т.е. они должны находиться внутри блока). Вы не можете применить оператор try к отдельному оператору программы.
Целью правильно построенных операторов catch является разрешение исключительных ситуаций и продолжение работы, как если бы ошибки вообще не случались.
Например, в следующей программе каждая итерация цикла for получает
два случайных числа. Эти два числа делятся одно на другое, а результат
используется для деления значения 12345. Окончательный результат помещается в переменную а.
Если какая-либо из операций деления вызывает ошибку деления
на нуль, эта ошибка перехватывается, значение переменной а устанавливается равным 0 и выполнение программы продолжается.
// Обработка исключения с продолжением работы, import java.util.Random; class HandleError { public static void main(String args[]) { int a=0, b=0, c=0 ; Random r = new Random(); for(int i=0; i<32000; i++) { try { b = r.nextInt(); с = r.nextInt(); a = 12345 / (b/c); } catch (ArithmeticException e) { System.out.println("Деление на нуль."); a = 0; // присвоить нуль и продолжить работу } System.out.println("а: " + а); } }
Отображение описания исключения.
Все исключения наследуются от общего класса Throwable. Класс Throwable
переопределяет метод toString() (определенный в классе Object) таким
образом, что он возвращает строку, содержащую описание исключения.
Вы можете отобразить это описание с помощью метода println(), просто
передав исключение в виде аргумента. Например, блок catch из предыдущего примера может быть переписан следующим образом.
catch (ArithmeticException е) { System.out.println("Исключение: " + e); a = 0; // присвоить нуль и продолжить работу }
Когда эта версия подставляется в программу и программа запускается, каждая попытка деления на нуль отобразит следующее сообщение.
Исключение: java.lang.ArithmeticException: / by zero
Хотя в данном контексте это не имеет особого значения, все же возможность отобразить описание исключения в некоторых случаях полезна — в частности, когда вы экспериментируете с исключениями или занимаетесь отладкой.
Оператор finally.
Когда исключение передано, выполнение метода направляется по нелинейному пути, изменяющему нормальный поток управления внутри метода.
В зависимости от того, как написан метод, существует даже возможность преждевременного возврата управления. В некоторых методах это может служить причиной серьезных проблем.
Например, если метод при входе открывает файл и закрывает его при
выходе, вероятно, вы не захотите, чтобы выполнение кода, закрывающего файл, было пропущено из-за применения механизма обработки исключений.
Ключевое слово finally предназначено для того, чтобы справиться с такой ситуацией.
Ключевое слово finally создает блок кода, который будет выполнен после
завершения блока try /catch, но перед кодом, следующим за ним.
Блок finally выполняется независимо от того, передано исключение или нет.
Если исключение передано, блок finally выполняется, даже если ни один оператор catch этому исключению не соответствует.
В любой момент, когда метод собирается возвратить управление вызывающему коду изнутри блока try/catch (из-за необработанного
исключения или явным применением оператора return), блок finally
будет выполнен перед возвратом управления из метода.
Это может быть удобно для закрытия файловых дескрипторов либо освобождения других ресурсов, которые были получены в начале метода и должны быть освобождены перед возвратом.
Оператор finally необязателен. Однако каждый оператор try требует
наличия, по крайней мере, одного оператора catch или finally.
Ниже приведен пример программы, которая показывает три метода, возвращающих управление разными способами, но ни один из них не пропускает выполнения блока finallу.
class FinallyDemo { // Передает исключение из метода, static void procA() { try { System.out.println("внутри procA"); throw new RuntimeException("демо"); } finally { System.out.println("блок finally procA"); } } // Возврат управления в блоке try. static void procB() { try { System.out.println("внутри procB"); return; } finally { System.out.println("блок finally procB"); } } // Нормальное выполнение блока try. 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(); } }
Вот выход программы:
внутри procA
блок finally procA
Исключение перехвачено
внутри procB
блок finally procB
внутри procC
блок finally procC
В этом примере метод procA() преждевременно прерывает выполнение в блоке try, передавая исключение. Блок finallу все равно выполняется.
В методе procB() возврат управления осуществляется в блоке try оператором return. Блок finally выполняется перед возвратом из метода procB().
В методе procC() блок try выполняется нормально, без ошибок. Однако блок finally выполняется все равно.