В скобках, следующих за ключевым словом catch, указываются тип исключения и переменная, ссылающаяся на объект данного типа. Когда возникает исключение, оно перехватывается соответствующим оператором catch, обрабатывающим это исключение. Как следует из приведенной выше общей формы записи, с одним блоком try может быть связано несколько операторов catch. Тип исключения определяет, какой именно оператор catch будет выполняться. Так, если тип исключения соответствует типу оператора catch, то именно он и будет выполнен, а остальные операторы catch — пропущены. При перехвате исключения переменной, указанной в скобках после ключевого слова catch, присваивается ссылка на объект_исключения.
Следует иметь в виду, что если исключение не генерируется, блок try завершается обычным образом и ни один из его операторов catch не выполняется. Выполнение программы продолжается с первого оператора, следующего за последним оператором catch. Таким образом, операторы catch выполняются только при появлении исключения.
На заметку. В версии JDK 7 внедрена новая форма оператора try, поддерживающая автоматическое управления ресурсами и называемая оператором try с ресурсами. Более подробно она описывается в главе 10 при рассмотрении потоков ввода-вывода, в том числе и тех, что связаны с файлами, поскольку потоки ввода-вывода относятся к числу ресурсов, наиболее употребительных в прикладных программах. Простой пример обработки исключений
Рассмотрим простой пример, демонстрирующий перехват и обработку исключения. Как известно, попытка обратиться за границы массива приводит к ошибке, и виртуальная машина Java генерирует соответствующее исключение ArraylndexOutOf BoundsException. Ниже приведен код программы, в которой намеренно создаются условия для появления данного исключения, которое затем перехватывается. // Демонстрация обработки исключений, class ExcDemol { public static void main (String args[]) { int nums[] = new int[4]; // Создание блока try. try { System.out.println("Before exception is generated."); // Попытка обратиться за границы массива. nums[7] = 10; System.out.println("this won't be displayed"); } // Перехват исключения в связи с обращением за границы массива. catch (ArraylndexOutOfBoundsException exc) { System.out.println("Index out-of-bounds!"); } System.out.println("After catch statement."); } }
Результат выполнения данной программы выглядит следующим образом: Before exception is generated. Index out-of-bounds! After catch statement.
Несмотря на всю простоту данного примера программы, он наглядно демонстрирует несколько важных особенностей обработки исключений. Во-первых, код, подлежащий проверке на наличие ошибок, помещается в блок try. И во-вторых, когда возникает исключение (в данном случае это происходит при попытке обратиться за границы массива), выполнение блока try прерывается и управление получает блок catch. Следовательно, явного обращения к блоку catch не происходит, но переход к нему осуществляется лишь при определенном условии, возникающем в ходе выполнения программы. Так, оператор вызова метода println (), следующий за выражением, в котором происходит обращение к несуществующему элементу массива, вообще не выполняется. По завершении блока catch выполнение программы продолжается с оператора, следующего за этим блоком. Таким образом, обработчик исключений предназначен для устранения программных ошибок, приводящих к исключительным ситуациям, а также для обеспечения нормального продолжения исполняемой программы.
Как упоминалось выше, если в блоке try не возникнут исключения, операторы в блоке catch не получат управление и выполнение программы продолжится после блока catch. Для того чтобы убедиться в этом, измените в предыдущей программе строку кода nums[7] = 10;
на следующую строку кода: nums[0] = 10;
Теперь исключение не возникнет и блок catch не выполнится.
Важно понимать, что исключения отслеживаются во всем коде в блоке try. К их числу относятся исключения, которые могут быть сгенерированы методом, вызываемым из блока try. Исключения, возникающие в вызываемом методе, перехватываются операторами в блоке catch, связанном с блоком try. Правда, это произойдет лишь в том случае, если метод не обрабатывает исключения самостоятельно. Рассмотрим в качестве примера следующую программу: /* Исключение может быть сгенерировано одним методом, а перехвачено другим. */ class ExcTest { // сгенерировать исключение static void genException() { int nums[] = new int[4]; System.out.println("Before exception is generated."); // Здесь генерируется исключение в связи с // обращением за границы массива. nums[7] = 10; System.out.println("this won't be displayed"); } } class ExcDemo2 { public static void main(String args[]) { try { ExcTest.genException() ; } //А здесь исключение перехватывается. catch (ArraylndexOutOfBoundsException exc) { System.out.println("Index out-of-bounds!"); } System.out.println("After catch statement."); } }
Выполнение этой версии программы дает такой же результат, как и при выполнении ее предыдущей версии. Этот результат приведен ниже. Before exception is generated. Index out-of-bounds! After catch statement.
Метод genException () вызывается из блока try, и поэтому генерируемое, но не перехватываемое в нем исключение перехватывается далее в блоке catch в методе main (). Если бы метод genException () сам перехватывал исключение, оно вообще не дошло бы до метода main (). Последствия неперехвата исключений
Перехват стандартного исключения Java, продемонстрированный в предыдущем примере, позволяет предотвратить завершение программы вследствие ошибки. Генерируемое исключение должно быть перехвачено и обработано. Если исключение не обрабатывается в программе, оно будет обработано виртуальной машиной Java. Но дело в том, что по умолчанию виртуальная машина Java аварийно завершает программу, выводя сообщение об ошибке и трассировку стека исключений. Допустим, в предыдущем примере попытка обращения за границы массива не отслеживается и исключение не перехватывается, как показано ниже. // Обработка ошибки средствами виртуальной машины Java, class NotHandled { public static void main(String args[]) { int nums[] = new int[4]; System.out.println("Before exception is generated."); // Попытка обращения за границы массива, nums[7] = 10; } }
При появлении ошибки, связанной с обращением за границы массива, выполнение программы прекращается и выводится следующее сообщение: Exception in thread "main" java.lang.ArraylndexOutOfBoundsException: 7 at NotHandled.main(NotHandled.java:9)
Оно полезно на этапе отладки, но пользователям программы эта информация вряд ли нужна. Именно поэтому очень важно, чтобы программы обрабатывали исключения самостоятельно и не поручали эту задачу виртуальной машине Java.
Как упоминалось выше, тип исключения должен соответствовать типу, указанному в операторе catch. В противном случае исключение не будет перехвачено. Так, в приведенном ниже примере программы делается попытка перехватить исключение, связанное с обращением за границы массива, с помощью оператора catch, в котором указан тип ArithmeticException еще одного встроенного в Java исключения. При неправильном обращении к массиву будет сгенерировано исключение ArraylndexOutOfBoundsException, не соответствующее типу, указанному в операторе catch. В результате программа будет завершена аварийно. // Эта программа не будет работать нормально! class ExcTypeMismatch { public static void main(String args[]) { int nums[] = new int[4]; try { System.out.println("Before exception is generated."); // При выполнении следующего оператора возникает // исключение ArraylndexOutOfBoundsException nums[7] = 10; System.out.println("this won’t be displayed"); } /* Исключение, связанное с обращением за границы массива, нельзя обработать с помощью оператора catch, в котором указан тип исключения ArithmeticException. */ catch (ArithmeticException exc) { System.out.println("Index out-of-bounds!"); } System.out.println("After catch statement."); } }