Исключительные ситуации


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

Программы пишут для того, чтобы они работали, работали быстро и, самое главное, правильно. К сожалению, программы не всегда работают правильно. Причем эта проблема не сводится к уровню подготовки программиста. Другими словами, бывают ситуации, причем нередко, когда принципиально невозможно или практически затруднительно обеспечить безошибочную работу программы. В таких случаях желательно хотя бы свести к минимуму негативные последствия от возникшей ошибки.

Самая большая неприятность при возникновении ошибочной ситуации состоит в том, что обычно это приводит к экстренному завершению программы. Во многих языках программирования, в том числе и в 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 аргумент (объект исключения) может применяться непосредственно при обработке ошибки. Нередко используется информация об ошибке, заложенная в объект исключительной ситуации.



Дата добавления: 2016-06-22; просмотров: 5790;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.015 сек.