Общие сведения об исключениях
Исключения представляют собой объекты, которые позволяют обработать исключительную ситуацию. Исключительная ситуация— это ошибка, происходящая во время выполнения программы (так называемая ошибка времени выполнения). Основные типы исключений (Exception) в java.
Исключительные ситуации можно обрабатывать с помощью блока try/catch/finally.
Рассмотрим, как он работает. В блок try помещается код, в котором может содержаться исключительная ситуация. Он работает либо до возникновения исключительной ситуации, либо при успешном выполнении до конца блока try.
В блоке catch выполняются некие операции при возникновении исключительной ситуации определенного типа (в параметрах блока — в круглых скобках — указывается тип возможного исключения — т.е. класс, который обрабатывает исключительную ситуацию).
Блок catch может делать все, что угодно: вызывать необходимый класс-исключение при помощи создания его объектов с использованием ключевого слова throw (это фактически искусственная передача выполнения нужному нам блоку catch), а может и сам производить какие-либо действия.
В блоке finally выполняются определенные восстановительные операции. Он обычно предназначен для освобождения памяти, выделенной под объект. Восстановительные операции будут выполняться независимо от того, возникло исключение (теперь мы будем именно так называть исключительные ситуации) в блоке try или нет.
Блок finally может отсутствовать.
Конструкция try/catch/finally может быть вложенной. Покажем использование этой конструкции на примере (листинг 5.1).
Листинг 5.1.
Пример использования конструкции try/catch/finally
class MyException extends Exception { public void MyException() { System.out.println("Возникло исключение!!!"); } } class Toys { public static short numbers = 0; public static short newNumber() { return ++numbers; } public short number; public String name, size; public void setParam(String name, String size) { try { this.name = name; this.size = size; if (this.number != 0) { throw new MyException(); // Вызов метода создания объекта MyException } this.number = Toys.newNumber(); } catch (MyException e) { System.err.println("Error!"); new MyException().MyException(); } catch (Exception e) { // Блок обработки всех остальных исключений блока try. } finally { System.out.println("Итого: \nИмя игрушки: " + this.name + "\nРазмер игрушки: " + this.size + "\nНомер игрушки: " + this.number); } } public static void main(String[] args) { Toys car = new Toys(); try { car.setParam("Car", "Large"); // Какие-то действия над объектом car. car.setParam("Car", "Small"); // Вот это приведет к возникновению исключительной ситуации. } catch (Exception e) { // ... } } }
Запомните важное правило: если необходимо, чтобы были отдельные обработчики исключения для базового класса и его производных, всегда в начале нужно помещать обработчик для производных классов. Если же сделать наоборот, то всегда будет выполняться обработчик для базового класса, так как он обрабатывает исключения своего типа и производных классов, а это противоречит желанию создать отдельный обработчик для производного класса.
Естественно, вы заметили, что мы создали класс-исключение как производный от класса Exception.
Подробнее создание классов-исключений мы рассмотрим позднее, а пока давайте рассмотрим использование стандартного класса ArithmeticException (листинг 5.2).
Листинг 5.2.
Пример использования стандартного класса ArithmeticException
class Program { public static void main(String[] args) { int a = 10; try { a = a / Integer.parseInt(args[0]); System.out.println("10 / " + args[0] + " = " + a); } catch (ArithmeticException e) { System.out.println("Error!"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Выходза пределы массива"); // А если пользователь не ввел значения в коммандной строке? } finally { System.out.println("That\'s all!!!"); } } }
Классы-исключения в большинстве своем наследуются от производного класса Exception. Можно использовать явное (прямое) обращение к объекту исключений, чтобы вызвать необходимые действия. Напомним о классе из первого примера, который позволяет обрабатывать исключение.
class MyException extends Exception { public void MyException() { System.out.println ("Возникло исключение!!!"); } }
Если необходимо, чтобы с помощью некоего класса обрабатывались какие-либо ошибки в методе, надо после круглых скобок, где указываются параметры, написать ключевое слово throws, а после него— имя класса, который должен обрабатывать какую-либо ошибку.
Если же нужно использовать несколько классов, лучше всего создать базовый класс — и тогда метод будет обрабатывать ошибки с помощью базового класса и всех его производных. Искусственно создать исключения можно с помощью оператора throw. Приведем пример использования класса MyException (листинг 5.3).
Листинг 5.3.
Пример использования класса MyException
class Toys { public static short numbers = 0; public static short newNumber() { return ++numbers; } public short number; public String name, size; public void setParam(String name, String size) { try { this.name = name; this.size = size; if (this.number != 0) { throw new MyException(); // Вызов метода создания объекта MyException } this.number = Toys.newNumber(); } catch (MyException e) { System.err.println("Error!"); new MyException().MyException(); } catch (Exception e) { // Блок обработки всех остальных исключений блока try. } finally { System.out.println("Итого: \nИмя игрушки: " + this.name + "\nРазмер игрушки: " + this.size + "\nНомер игрушки: " + this.number); } } }
Так как мы пытаемся вторично вызвать статический метод создания номера данного объекта, вызовем класс-исключение, который выдает несколько строк и продолжает выполнение программы, не выполнив код приращения номера.
Хотя в языке Java предусмотрен стандартный класс-исключение для ошибок при попытке деления на 0, сейчас мы создадим собственный класс-исключение, который будет обрабатывать подобные ошибки (листинг 5.4).
Листинг 5.4.
Создание класса для обработки ошибок
class MyDivByZero extends Exception { public void MyDivByZero() { System.out.println("Что такое? Вы решили делить на 0?"); } } class Arithmetic { public static double myDelete(double a, double b) throws MyDivByZero { double c; try { if (b == 0.0) { throw new MyDivByZero(); } c = a / b; } catch (MyDivByZero e) { c = 0.0; System.exit(0); } return c; } public static void main(String[] args) { try { System.out.println("5.1, разделить на 1.2 будет " + myDelete(5.1, 1.2)); System.out.println("3.6, разделить на 0 будет " + myDelete(3.6, 0)); System.out.println("7.8, разделить на 3.9 будет " + myDelete(7.8, 3.9)); } catch (MyDivByZero e) { // ... } } }
В данном случае мы сами разделили на 0, и проблемы с обработкой исключения не возникло (это всего лишь ошибка программиста — он ее исправит). А вдруг необходимо попросить пользователя ввести число и он введет 0? Тогда-то и будет проблема.