Справочник от Автор24
Поделись лекцией за скидку на Автор24

Обработка исключительных ситуаций

  • 👀 586 просмотров
  • 📌 519 загрузок
Выбери формат для чтения
Загружаем конспект в формате doc
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Обработка исключительных ситуаций» doc
Лекция. Обработка исключительных ситуаций Программы пишут для того, чтобы они работали, работали быстро и, самое главное, правильно. К сожалению, программы не всегда работают правильно. Причем эта проблема не сводится к уровню подготовки программиста. Другими словами, бывают ситуации, причем нередко, когда принципиально невозможно или практически затруднительно обеспечить безошибочную работу программы. В таких случаях желательно хотя бы свести к минимуму негативные последствия от возникшей ошибки. Самая большая неприятность при возникновении ошибочной ситуации состоит в том, что обычно это приводит к экстренному завершению программы. Во многих языках программирования, в том числе и в Java, предусмотрены механизмы, позволяющие «сохранить лицо» даже в достаточно сложных ситуациях. Об этих механизмах и идет речь в данной лекции. Исключительные ситуации Исключительная ситуация — это ошибка, которая возникает в результате выполнения программы. Исключение в Java — это объект, который описывает исключительную ситуацию (ошибку). При возникновении исключительной ситуации в процессе выполнения программы автоматически создается объект, описывающий эту исключительную ситуацию. Этот объект передается для обработки методу, в котором возникла исключительная ситуация. Говорят, что исключение выбрасывается в метод. По получении объекта исключения метод может обработать его или передать для обработки дальше (куда именно — другой вопрос). Исключения (объекты, описывающие исключительные ситуации) генерируются автоматически, однако их также можно генерировать «вручную», то есть специальными программными методами. На первый взгляд, такая возможность кажется лишней и ненужной, но это не так. Дальше мы увидим, что механизм обработки исключительных ситуаций, в том числе искусственное генерирование исключений, нередко позволяет сделать программный код более компактным и, если хотите, элегантным, значительно упрощая решение сложных, на первый взгляд, задач. Для того чтобы метод мог обработать исключительную ситуацию, необходимо предусмотреть программный код обработки этой ситуации — на случай ее возникновения. Во-первых, нужно выделить фрагмент кода, который должен контролироваться на предмет генерирования исключительной ситуации. Во-вторых, необходимо создать программный код, непосредственно обрабатывающий исключительную ситуацию, то есть код, который выполняется в случае возникновения исключительной ситуации. В Java для обработки исключительных ситуаций используется блок try-catch-finally. В блок try помещается программный код, который отслеживается на случай, если возникнет исключительная ситуация. Если исключительная ситуация возникает, то управление передается блоку catch. Программный код в этом блоке выполняется, только если возникает исключительная ситуация, причем не любая, а определенного типа. Аргумент, определяющий, какого типа исключительные ситуации обрабатываются в блоке catch, указывается после ключевого слова catch в круглых скобках, то есть в том же формате, что и аргумент метода. Поскольку в блоке try могут возникать исключения разных типов, для каждого из них можно предусмотреть свой блок catch. Если блоков catch несколько, при возникновении исключительной ситуации они перебираются последовательно до совпадения типа исключительной ситуации с аргументом блока catch. После блоков try и catch можно указать блок finally с кодом, который выполняется в любом случае вне зависимости от того, возникла исключительная ситуация или нет. Общая схема использования блока try-catch-finally для обработки исключительных ситуаций выглядит так: try{ // код, который генерирует исключение } catch(Тип_исключения_1 объект){ // код для обработки исключения } catch(Тип_исключения_2 объект){ // код для обработки исключения } ... finally{ // код, который выполняется обязательно } Если при исполнении программного кода в блоке try{} ошибок не возникает, после выполнения этого блока выполняется блок finally (если он имеется), затем управление передается следующей после конструкции try-catch-finally команде. При возникновении ошибки в процессе выполнения кода в блоке try выполнение кода в этом блоке останавливается и начинается поиск подходящего блока catch. Если подходящий блок найден, выполняется его программный код, после чего выполняется код блока finally (при наличии такого). На этом все — далее выполняется код, следующий после блока try-catch-finally. Может случиться, что в блоке try возникла ошибка, но подходящего блока catch для ее обработки нет. В этом случае исключение выбрасывается из метода и должно быть обработано внешним к методу программным кодом. Согласно правилам языка Java, исключения, которые не обрабатываются в методе и выбрасываются из метода, указываются в сигнатуре метода после ключевого слова throws. То есть указываются классы выбрасываемых из метода исключений. Правда, далеко не все классы выбрасываемых исключений нужно указывать — только так называемые неконтролируемые исключения. Мы рассмотрим их позже. Если возникает ошибка, обработка которой в программе не предусмотрена, используется обработчик исключительной ситуации по умолчанию. Самое трагическое последствие вызова обработчика по умолчанию состоит в том, что программа завершает работу. Есть еще одно ключевое слово, которое достаточно часто используется при обработке исключительных ситуаций, а точнее, при генерировании исключительной ситуации. Это ключевое слово throw. Классы исключений В Java существует целая иерархия классов, предназначенных для обработки исключительных ситуаций. В вершине этой иерархии находится суперкласс Throwable. У этого суперкласса есть два подкласса: Exception и Error. К классу Error относятся «катастрофические» ошибки, которые невозможно обработать в программе, например переполнение стека памяти. У класса Exception есть подкласс RuntimeException. К классу RuntimeException относятся ошибки времени выполнения программы, которые перехватываются программами пользователя. Исключения для класса RuntimeException определяются автоматически. К ним относятся, например, деление на ноль, выход за пределы массива (недопустимая индексация массива). В листинге 1 приведен пример программы, в которой происходит обработка исключительной ситуации, заключающейся в делении на ноль. Листинг 1. Обработка ошибки деления на ноль class ExceptionDemo{ public static void main(String args[]){ int a,b; // Блок контроля исключительной ситуации: try{ b=0; // Деление на ноль: a=100/b; }catch(ArithmeticException e){ // Обработка исключительной ситуации: System.out.println("Деление на ноль!"); } System.out.println("Выполнение программы продолжено!");} } Что касается алгоритма, реализованного в программе, то он прост и непритязателен. В главном методе объявляются две целочисленные переменные a и b. Переменной b присваивается нулевое значение, а переменной a — некое значение командой a=100/b. Другими словами, без всяких обиняков выполняется деление на ноль! Поэтому нет никаких сомнений в том, что при выполнении этого кода возникнет ошибка деления на ноль. Если не предусмотреть ее обработки, на команде a=100/b работа программы, фактически, прекратится, поскольку будет вызван обработчик ошибки по умолчанию, который и довершит начатый при делении на ноль декаданс. Ошибка деления на ноль относится к классу ArithmeticException, который является подклассом класса RuntimeException. Для отслеживания этой ошибки код, который ее вызывает, заключается в блок try, а для обработки ошибки после блока try размещается блок catch. Аргументом в блок catch передается объект e класса ArithmeticException (объект исключения). В данном случае при обработке ошибки напрямую объект исключения e не используется, но в принципе такая ситуация возможна и часто встречается. Код, выполняемый при обработке исключительной ситуации деления на ноль, состоит всего из одной команды: System.out.println("Деление на ноль!") То есть при делении на ноль на экран выводится сообщение Деление на ноль! Здесь важно другое — работа программы при этом не завершается. После выполнения блока catch выполняется следующая после этого блока команда System.out.println("Выполнение программы продолжено!"). В результате мы получаем два сообщения: Деление на ноль! Выполнение программы продолжено! Еще раз отметим, что в данном случае важно то, что после попытки деления на ноль программа продолжает работу. Другой пример обработки исключительной ситуации деления на ноль представлен в листинге 2. Листинг 2. Еще одно деление на ноль // Импорт класса Random: import java.util.Random; class ArithExcepionDemo{ 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(200); c=r.nextInt(100); // Возможно деление на ноль: a=10000/b/c; }catch(ArithmeticException e){ // Обработка ошибки: System.out.println("Деление на ноль!"); a=0;} System.out.println("a="+a);} } } Этот пример более реалистичен, хотя и несколько запутан. Здесь имеет место генерирование случайных чисел. Для этого создается объект класса Random. В свою очередь, чтобы класс стал доступен, необходимо его импортировать с помощью команды import java.util.Random — класс принадлежит пакету java.util. В главном методе создаются три целочисленные переменные a, b и c с нулевыми начальными значениями. Командой Random r=new Random() создается объект r класса Random. Для генерирования целого числа служит метод nextInt(), который вызывается из объекта r. Аргументом метода указывается верхняя граница диапазона генерируемых чисел. Нижняя граница диапазона генерируемых чисел равна нулю. Далее запускается цикл с достаточно большим количеством итераций. В рамках каждого цикла последовательно выполняются команды b=r.nextInt(200), c=r. nextInt(100) и a=10000/b/c. Поскольку переменные b и c получают случайные значения, в том числе это может быть ноль, то гипотетически при выполнении команды a=10000/b/c возможно деление на ноль. Поэтому соответствующие команды заключены в блок try. В блоке catch{}, предназначенном для обработки исключительной ситуации деления на ноль, выполняются команды System.out.println("Деление на ноль!") и a=0. В результате при попытке деления на ноль выводится соответствующее сообщение, а переменная a получает нулевое значение. Работа цикла при этом продолжается. В частности, значение переменной a выводится на экран. Результат выполнения программы, в силу очевидных причин, полностью привести невозможно, но один из фрагментов мог бы выглядеть так: ... a=1 Деление на ноль! a=0 a=18 a=2 ... Ранее упоминалось, что передаваемый в блок catch аргумент (объект исключения) может применяться непосредственно при обработке ошибки. Нередко используется информация об ошибке, заложенная в объект исключительной ситуации. Описание исключительной ситуации В классе Throwable переопределяется метод toString(), который, как известно, определен в общем суперклассе Object, причем переопределяется он так, что в качестве результата возвращает строку, описывающую соответствующую ошибку. Напомним, что метод toString() вызывается автоматически, например, при передаче объекта исключения методу println() в качестве аргумента. Соответствующий пример приведен в листинге 3. Листинг 3. Описание ошибки class MoreExceptionDemo{ public static void main(String args[]){ int a,b; try{ b=0; // Деление на ноль: a=100/b; }catch(ArithmeticException e){ // При обработке ошибки использован объект исключения: System.out.println("Ошибка: "+e);} System.out.println("Выполнение программы продолжено!");} } По сравнению с рассмотренным ранее особенность этого примера состоит в том, что в команде System.out.println("Ошибка: "+e) в качестве аргумента методу println() передается объект исключительной ситуации e. Результат выполнения программы в этом случае имеет такой вид: Ошибка: java.lang.ArithmeticException: / by zero Выполнение программы продолжено! В первой текстовой строке, выведенной на экран, текст после слова Ошибка: появился в результате преобразования объекта исключения e в текстовый формат. Множественный блок catch{} Как отмечалось в начале лекции, для каждого типа исключений можно предусмотреть свой блок catch для обработки. Блоки размещаются один за другим и им передаются разные аргументы (объекты исключений разных классов) в соответствии с типом обрабатываемой исключительной ситуации. В листинге 4 приведен пример программы, в которой помимо ошибки деления на ноль обрабатывается также и ошибка неверной индексации массива. Листинг 4. Несколько блоков catch import java.util.Random; class MultiCatchDemo{ public static void main(String args[]){ Random r=new Random(); int MyArray[]={0,2}; int a,b; for(int i=1;i<10;i++){ try{ a=r.nextInt(3); b=10/MyArray[a]; System.out.println(b); }catch(ArithmeticException e){ System.out.println("Деление на ноль!");} catch(ArrayIndexOutOfBoundsException e){ System.out.println("Выход за границы массива!");} Множественный блок catch{} 269 finally{System.out.println("**************");}} System.out.println("Цикл for завершен!");} } Как и в одном из предыдущих примеров, здесь используется генератор случайных чисел, для чего командой import java.util.Random импортируется класс Random, а с помощью команды Random r=new Random() создается объект r этого класса для генерирования случайных чисел. Кроме того, создается целочисленный массив MyArray из двух элементов (значения 0 и 2), также описываются две целочисленные переменные a и b. В цикле командой a=r.nextInt(3) присваивается значение переменной a — случайное целое число в диапазоне от 0 до 2 включительно. Переменная a, другими словами, может принимать значения 0, 1 и 2. Далее командой b=10/MyArray[a] присваивается значение переменной b. При выполнении этой команды могут возникать неприятности двух видов. Во-первых, если значение переменой a равно 0, то выполняется деление на ноль, поскольку элемент массива MyArray[0] равен нулю. Во-вторых, если значение переменой a равно 2, то имеет место ошибка выхода за границы массива, поскольку элемента MyArray[2] не существует. Если же значение переменной a равно 1, то значение переменной b вычисляется как 5 и выводится на экран командой System.out.println(b). Для обработки ошибки деления на ноль используется блок catch с аргументом класса ArithmeticException. В этом случае выводится сообщение о том, что произошла попытка деления на ноль. Исключительная ситуация, связанная с неправильной индексацией элементов массива, описывается исключением класса ArrayIndexOutOfBoundsException. Аргумент этого класса передается во второй блок catch. Обработка этой ошибки сводится к тому, что выводится сообщение о выходе за границы массива. Наконец, блок finally содержит команду вывода разделительной «звездной линии», которая отображается независимо от того, произошла какая-либо ошибка или нет. Результат выполнения программы мог бы выглядеть следующим образом: Выход за границы массива! ************** Деление на ноль! ************** Выход за границы массива! ************** 5 ************** Деление на ноль! ************** 5 ************** Выход за границы массива! 270 Глава 9. Обработка исключительных ситуаций ************** 5 ************** Выход за границы массива! ************** Цикл for завершен! В конце работы программы выводится сообщение о том, что работа цикла завершена. Вложенные блоки try Один блок try может размещаться внутри другого блока try. В этом случае, если во внутреннем блоке try возникает ошибка и этот блок try не содержит блока catch для ее обработки, исключение выбрасывается во внешний блок try и начинается последовательный просмотр его блоков catch на предмет обработки возникшей ошибки. Может сложиться и более нетривиальная ситуация, например, когда метод, который вызывается в блоке try, сам содержит блок try. Если в блоке try метода возникает ошибка, не обрабатываемая методом, ее перехватывает внешний блок try, в котором вызывается метод. Вообще же общий принцип обработки ситуаций при сложной схеме включения блоков try состоит в том, что при входе в очередной блок try контексты обрабатываемых этим блоком исключений записываются в стек. При возникновении ошибки этот стек начинает «раскручиваться» — контексты исключений просматриваются в обратном порядке (то есть последний занесенный в стек контекст исключения просматривается первым). В листинге 5 приведен пример использования вложенных блоков try. Листинг 5. Вложенные блоки try import java.util.Random; class NestTryDemo{ public static void main(String args[]){ Random r=new Random(); int a,b; int c[]={-1,1}; for(int i=1;i<10;i++){ try{ a=r.nextInt(3); // значение 0,1 или 2 b=100/a; // возможно деление на ноль System.out.println("b="+b); Вложенные блоки try 271 try{ if(a==1) a=a/(a-1); // деление на ноль else c[a]=200; // выход за границы массива }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Выход за границы массива: "+e);} }catch(ArithmeticException e){ System.out.println("Деление на ноль: "+e);} System.out.println("*******************************");} }} Как и ранее, в программе генерируются случайные числа (с помощью объекта r класса Random), объявляются две целочисленные переменные a и b, а также целочисленный массив c, состоящий всего из двух элементов (со значениями –1 и 1). В цикле внешнего блока try последовательное выполнение команд a=r.nextInt(3) и b=100/a может закончиться генерированием исключения, поскольку среди возможных значений переменной a есть и нулевое, что, в свою очередь, означает ошибку деления на ноль. На этот случай предусмотрен блок catch внешнего блока try. В случае ошибки выполняется команда: System.out.println("Деление на ноль: "+e) Здесь объект e класса ArithmeticException является аргументом блока catch. Если в указанном месте программы ошибка деления на ноль не возникает, значение переменной b выводится на экран (эта переменная может принимать всего два значения: 100 при значении переменной a равном 1 и 50 при значении переменной a равном 2), после чего выполняется серия команд, заключенных во внутренний блок try. Сразу отметим, что этот блок обрабатывает только исключение, связанное с выходом за границы массива (объект класса ArrayIndexOutOfBoundsException). В случае возникновения соответствующей ошибки выполняется команда: System.out.println("Выход за границы массива: "+e) Что касается самого программного кода во внутреннем блоке try, то он может вызывать исключения двух типов. В частности, там с помощью условной инструкции проверяется условие равенства значения переменной a единице. Если условие соблюдается, то при выполнении команды a=a/(a-1) происходит ошибка деления на ноль. В противном случае (то есть если значение переменной a отлично от единицы) выполняется команда c[a]=200. Обращаем внимание, что внутренний блок try выполняется, только если значение переменной a равно 1 или 2, поскольку если значение этой переменной равно 0, еще раньше возникнет ошибка деления на ноль, которая перехватывается блоком catch внешнего блока try. Поэтому если во внутреннем блоке try значение переменной a отлично от единицы, это автоматически означает, что значение переменной a равно 2. В результате при выполнении команды c[a]=200 возникает ошибка выхода за границы массива, поскольку в массиве c всего два элемента, а элемента c[2] там просто нет. Таким образом, во внутреннем блоке try обрабатывается ошибка выхода за границы диапазона. Если во внутреннем блоке try возникает ошибка деления на ноль, она обрабатывается блоком catch внешнего блока try. Результат выполнения программы мог бы быть следующим: b=50 Выход за границы массива: java.lang.ArrayIndexOutOfBoundsException: 2 ******************************* b=100 Деление на ноль: java.lang.ArithmeticException: / by zero ******************************* b=50 Выход за границы массива: java.lang.ArrayIndexOutOfBoundsException: 2 ******************************* Деление на ноль: java.lang.ArithmeticException: / by zero ******************************* b=50 Выход за границы массива: java.lang.ArrayIndexOutOfBoundsException: 2 ******************************* b=50 Выход за границы массива: java.lang.ArrayIndexOutOfBoundsException: 2 ******************************* b=100 Деление на ноль: java.lang.ArithmeticException: / by zero ******************************* b=100 Деление на ноль: java.lang.ArithmeticException: / by zero ******************************* Деление на ноль: java.lang.ArithmeticException: / by zero ******************************* Другой пример вложенных блоков try приведен в листинге 6. На этот раз в блоке try вызывается метод, у которого есть собственный блок try. Листинг 6. Метод с блоком try import java.util.Random; class MethWithTryDemo{ static void nesttry(int a){ int c[]={-1,1}; try{ if(a==1) a=a/(a-1); // деление на ноль else c[a]=200; // выход за границы массива }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Выход за границы массива: "+e);} } public static void main(String args[]){ Random r=new Random(); Искусственное генерирование исключений 273 int a,b; for(int i=1;i<10;i++){ try{ a=r.nextInt(3); // значения 0, 1 или 2 b=100/a; // возможно деление на ноль System.out.println("b="+b); nesttry(a); }catch(ArithmeticException e){ System.out.println("Деление на ноль: "+e);} System.out.println("*******************************");} }} Фактически, это та же программа, что и в предыдущем примере, только код внутреннего блока try реализован в виде статического метода nesttry(), содержащего блок try. Там, где раньше был внутренний блок try, теперь вызывается метод nesttry(). Если возникает ошибка выхода за границы массива, она обрабатывается в самом методе, а ошибка деления на ноль обрабатывается во внешнем блоке try. Результат выполнения этой программы аналогичен предыдущему. Искусственное генерирование исключений Как уже отмечалось, исключения можно генерировать «вручную», то есть создавать видимость ошибки там, где ее и в помине нет. Для генерирования исключения используется ключевое слово throw. Команда генерирования исключения имеет следующий синтаксис: throw объект_исключения; После инструкции throw необходимо указать объект исключения, то есть объект, описывающий создаваемую исключительную ситуацию. Причем предварительно этот объект нужно создать. Напомним, что объект исключения — это объект класса Throwable или его подкласса. Существует два способа создания объекта исключения. Во-первых, можно воспользоваться аргументом блока catch, во-вторых, можно создать новый объект с помощью оператора new. При этом прибегают к помощи конструктора класса соответствующего исключения. Все исключения времени выполнения программы (класса RuntimeException) имеют конструкторы без аргументов и с текстовым аргументом. В последнем случае текст, переданный конструктору при создании объекта, отображается затем при описании объекта, если последний приводится к текстовому формату (например, при передаче объекта методам print() и println()). После выполнения оператора throw поток выполнения останавливается, и следующая команда не выполнятся. Вместо этого начинается поиск подходящего для обработки сгенерированного исключения блока catch. Если такой блок не обнаруживается, используется обработчик по умолчанию. Пример программы с явным выбрасыванием исключения приведен в листинге 7. Листинг 7. Явное выбрасывание исключения class ThrowDemo{ static void demoproc(){ try{ // Создание объекта исключения: NullPointerException ExObj=new NullPointerException("Ошибка!"); throw ExObj; // выбрасывание исключения }catch(NullPointerException e){ System.out.println("Перехват исключения в методе demoproc!"); throw e; // повторное выбрасывание исключения }} public static void main(String args[]){ try{ demoproc(); }catch(NullPointerException e){ System.out.println("Повторный перехват: "+e);} System.out.println("Работа программы завершена!");} } Результат выполнения программы такой: Перехват исключения в методе demoproc! Повторный перехват: java.lang.NullPointerException: Ошибка! Работа программы завершена! В классе ThrowDemo, помимо главного методапрограммы main(), описывается метод demoproc(), в котором явно выбрасывается исключение. Для начала создается объект исключения командой: NullPointerException ExObj= new NullPointerException("Ошибка!") Точнее, это объект ExObj класса NullPointerException (ошибка операций с указателем). Имеется конструктор класса NullPointerException с текстовым аргументом "Ошибка!". Этот текст впоследствии используется при выводе на экран описания возникшей ошибки. Командой throw ExObj производится выбрасывание исключения. Поскольку все это происходит в блоке try, то начинается поиск подходящего блока catch для обработки исключения. В данном случае блок catch всего один, и это именно тот блок, который нужен. В этом блоке выводится сообщение о том, что исключение перехвачено в методе demoproc(). Делается это командой System.out.println("Перехват исключения в методе demoproc!") Однако затем командой throw e снова выбрасывается исключение. Для того чтобы обработать это исключение, нужен внешний блок try с соответствующим блоком catch для обработки исключения. Поскольку в главном методе программы метод demoproc() вызывается в блоке try и для исключения класса NullPointerException описан обработчик (выполняется команда System.out.println("Повторный перехват: "+e)), то выброшенное из метода demoproc() исключение перехватывается и обрабатывается. Хочется обратить внимание на два немаловажных момента. Во-первых, вместо явного создания в методе demoproc() объекта исключения ExObj можно было ограничиться анонимным объектом, объединив команды создания объекта исключения и его выбрасывания в одну команду вида: throw new NullPointerException("Ошибка!") Обычно так и поступают, поскольку это экономит место и время, а результат в принципе тот же. Во-вторых, сообщение программы Повторный перехват: java.lang.NullPointerException: Ошибка! возникает в результате обработки повторно выброшенного исключения вне метода demoproc(), в то время как объект исключения с текстовым параметром Ошибка! создавался в этом методе. Последовательность действий, которые приводят к такому результату, следующая. При выбрасывании исключения в методе demoproc() объект исключения (то есть объект ExObj) передается аргументом в блок catch. Аргумент в этом блоке обозначен как e, но это формальное название аргумента. Такие же формальные названия для аргументов используются при описании методов. Реально в блок передается объект ExObj. Далее в блоке catch есть команда throw e, которая означает выброс исключения, переданного аргументом в блок catch. То есть это опять объект ExObj. Поскольку это второе исключение в методе demoproc() не обрабатывается, а передается во внешний блок try для перехвата и далее в соответствующий блок catch для обработки, то аргумент внешнего блока catch — это все тот же метод ExObj, который создавался конструктором с текстовым аргументом Ошибка!. Именно это описание ошибки и появляется на экране после передачи аргументом методу println() объекта исключения. Кроме этого описания автоматически отображается трасса стека ошибки (сообщение java.lang. NullPointerException:). Выбрасывание исключений методами Ранее отмечалось, что если метод выбрасывает или может выбросить исключение, которое в методе не обрабатывается, этот факт нужно отразить при описании метода. В сигнатуре метода после ключевого слова throws перечисляются классы исключений, которые может выбрасывать метод. Причина такой предупредительности состоит в том, что внешним методам нужно сообщить, к каким неприятностям следует быть готовым при вызове данного метода. Ранее мы не использовали ключевое слово throws, хотя некоторые методы и выбрасывали исключения. Дело в том, что перечисляются далеко не все выбрасываемые методом исключения. В частности, не нужно явно указывать исключения класса Error, а также класса RuntimeException и его подклассов. Рассматривавшиеся ранее исключения как раз относились к подклассам RuntimeException. Общий синтаксис описания метода, выбрасывающего исключения (которые не обрабатываются в методе), выглядит так: тип_результата имя_метода(аргументы) throws исключение1,исключение2,...{ // тело метода } Если метод может выбрасывать несколько исключений, их классы перечисляются через запятую. Пример описания метода, выбрасывающего необрабатываемое исключение, приведен в листинге 8. Листинг 8. Метод выбрасывает исключение class ThrowsDemo{ // Описание метода: static void throwOne() throws IllegalAccessException{ System.out.println("Ошибка в методе throwOne!"); // Выбрасывание исключения: throw new IllegalAccessException("Большая ошибка!");} public static void main(String args[]){ try{ throwOne(); // Метод выбрасывает исключение }catch(IllegalAccessException e){ // Обработка исключения System.out.println("Случилась неприятность: "+e);} }} В результате выполнения этой программы получаем два сообщения: Ошибка в методе throwOne! Случилась неприятность: java.lang.IllegalAccessException: Большая ошибка! Программа достаточно простая и ее особенность лишь в том, что при описании метода throwOne() явно указано, что метод может выбрасывать исключение класса IllegalAccessException (ошибка доступа). Методом throwOne() выводится на экран сообщение об ошибке, затем выбрасывается исключение командой: throw new IllegalAccessException("Большая ошибка!") В данном случае исключение — это анонимный объект класса IllegalAccessException, для создания которого использовался конструктор с текстовым аргументом. В методе это исключение не отслеживается и не обрабатывается, о чем и свидетельствует наличие в сигнатуре метода ключевого слова throws и названия класса исключения IllegalAccessException. Отметим, что если бы это исключение в методе обрабатывалось, необходимости указывать в сигнатуре метода ключевое слово throws (и класса исключения) не было бы. В главном методе программы вызывается метод throwOne(), выбрасываемое методом исключение отслеживается и обрабатывается. Объект (анонимный) выброшенного методом исключения передается в блок catch. Контролируемые и неконтролируемые исключения Встроенные исключения в Java делятся на контролируемые и неконтролируемые. Фактически, перечислять выбрасываемые методом исключения в сигнатуре метода нужно только в том случае, если они неконтролируемые. В табл. 1 приведены некоторые контролируемые исключения в Java. Таблица 1. Контролируемые исключения в Java Исключение Описание ArithmeticException Арифметическая ошибка (например, деление на ноль) ArrayIndexOutOfBoundsException Индекс массива за пределами допустимых границ ArrayStoreException Присваивание элементу массива недопустимого значения ClassCastException Недопустимое приведение типов IllegalArgumentException Методу передан недопустимый аргумент IndexOutOfBoundsException Некоторый индекс находится за пределами допустимых для него границ NegativeArraySizeException Создание массива отрицательного размера NullPointerException Недопустимое использование нулевого указателя NumberFormatException Недопустимое преобразование текстовой строки в числовой формат StringIndexOutOfBoundsException Попытка индексирования вне пределов строки UnsupportedOperationException Недопустимая операция Ряд неконтролируемых исключений в Java приведен в табл. 2. Таблица 2. Неконтролируемые исключения в Java Исключение Описание ClassNotFoundException Класс не найден IllegalAccessException В доступе к классу отказано Таблица 2 (продолжение) Исключение Описание InstantiationException Попытка создать объект абстрактного класса или интерфейса InterruptedException Один поток прерван другим потоком NoSuchFieldException Поле не существует NoSuchMethodException Метод не существует Еще раз отметим, что даже если метод не обрабатывает и выбрасывает контролируемое исключение, отражать этот факт в сигнатуре метода не нужно. Неконтролируемые исключения указываются в сигнатуре метода, если они в этом методе не обрабатываются. Создание собственных исключений Классы встроенных исключений в Java описывают только наиболее общие ошибки, поэтому в некоторых случаях нужно или полезно описать собственное исключение. В Java такая возможность имеется. Технически создание собственного исключения сводится к созданию подкласса класса Exception, который, в свою очередь, является подклассом класса Throwable. В создаваемом подклассе ничего не нужно реализовывать. Что касается класса Exception, то он не определяет собственных методов, а наследует их из класса Throwable. Некоторые методы класса Throwable представлены в табл. 3. Таблица 3. Методы класса Throwable Метод Описание fillInStackTrace() Метод в качестве результата возвращает объект Throwable, который содержит полную трассу стека. Метод не имеет аргументов getLocalizesMessage() В качестве результата метод возвращает строку (объект класса String) с локализованным описанием исключения. Метод не имеет аргументов getMessage() Методом возвращается строка (объект класса String) с описанием исключения. Метод не имеет аргументов printStackTrace() Методом отображается трасса стека. Метод не имеет аргументов toString() Метод в качестве значения возвращает объект класса String, содержащий описание исключения. Метод не имеет аргументов В листинге 9 приведен пример программы, в которой создается пользовательский класс для исключения. Листинг 9. Программа с пользовательским классом исключения // Класс исключения: class MyException extends Exception{ private double min; private double max; private String error; // Конструктор: MyException(double x,double y,String str){ min=x; max=y; error=str;} // Переопределение метода toString(): public String toString(){ return "Произошла ошибка ("+error+"): попадание в диапазон ["+min+","+max+"]";} } class MyExceptionDemo{ // Метод выбрасывает исключение пользовательского типа: static double MyLog(double x) throws MyException{ if(x<0||x>1) return Math.log(x*(x-1)); else throw new MyException(0,1,"неверный аргумент"); } public static void main(String args[]){ double x=-1.2,y=1.2,z=0.5; try{ System.out.println("ln("+x+")="+MyLog(x)); System.out.println("ln("+y+")="+MyLog(y)); System.out.println("ln("+z+")="+MyLog(z)); }catch(MyException e){// Обработка исключения System.out.println(e);} } } Результат выполнения программы такой: ln(-1.2)=0.9707789171582248 ln(1.2)=-1.427116355640146 Произошла ошибка (неверный аргумент): попадание в диапазон [0.0,1.0] В программе описывается класс пользовательского исключения MyException, который наследует класс Exception. У класса есть три закрытых поля: поля min и max типа double и поле error, являющееся объектом класса String. Также в классе переопределяется метод toString(). В качестве результата методом возвращается текстовая строка, в которой содержится информация всех трех полей класса. В главном методе программы определяется статический метод MyLog() с аргументом типа double. В качестве результата метод возвращает значение типа double. Метод описан как способный выбрасывать исключение пользовательского класса MyException. В теле метода для значения аргумента x вычисляется натуральный логарифм по формуле: ln (x (x - 1 ) ) Если аргумент, переданный методу MyLog(), лежит вне пределов диапазона [0 ,1 ] , методом возвращается значение Math.log(x*(x-1)) (в этой команде имеет место обращение к статическому методу вычисления натурального логарифма log(), описанному в классе Math). Если же аргумент метода MyLog() попадает в диапазон [0 ,1 ] , приведенное выражение вычислено быть не может, поскольку у натурального логарифма аргумент отрицательный. В этом случае методом MyLog() генерируется и выбрасывается исключение пользовательского типа MyException. Аргументами конструктору при этом передаются границы диапазона [0 ,1 и описание ошибки (неверный аргумент). В главном методе программы выполняется попытка вычислить значение методом MyLog() и вывести его на экран. При этом отслеживается возможность появления исключения класса MyException. В случае если соответствующая ошибка возникает, выполняется ее обработка, которая состоит в том, что на экран выводится описание объекта исключения. Этот объект передается методу println() в блоке catch, а наблюдаемый при этом результат отражает способ переопределения метода toString() в классе MyException.
«Обработка исключительных ситуаций» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ

Тебе могут подойти лекции

Смотреть все 588 лекций
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot