Java. Исключения (excetions). Исключительная ситуация. Ключевые слова try, catch, finally. Примеры

Исключения (excetions). Исключительная ситуация. Ключевые слова try, catch, finally. Примеры


Содержание


1. Что такое исключительная ситуация?

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

  • попытки деления на нуль
  • попытки обращения к элементу массива, номер индекса которого выходит за пределы объявленного;
  • попытки взять корень из отрицательного числа;
  • попытки открыть файл по имени, которого нет на диске;
  • другие случаи.

Правильная обработка исключений есть важным элементом написания профессиональных программ на Java.

 

2. Понятие исключения в языке Java

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

В языке программирования Java, исключение – это специальный объект, описывающий исключительную ситуацию, которая возникла в некоторой части программного кода. Объект представляющий исключение, генерируется в момент возникновения исключительной ситуации. После возникновения критической (исключительной) ситуации исключение перехватывается и обрабатывается. Таким образом, возникает понятие генерирования исключения. Генерирование исключения – процесс создания объекта, который описывает данное исключение.

 

3. Какими способами могут генерироваться исключения?

В языке Java исключения могут генерироваться одним из двух способов:

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

 

4. В каких случаях вызывается стандартный обработчик исключений Java?

Стандартный обработчик исключений Java вызывается в случаях, когда программа:

  • не использует блок try…catch для обработки и перехвата исключительной ситуации вообще;
  • содержит блок try…catch, однако в этом блоке данный тип исключения не перехватывается.

Если программа содержит собственный код try…catch для обработки данной исключительной ситуации, тогда стандартный обработчик исключений не вызывается.

 



5. Какие ключевые слова используются для обработки исключений в Java?

Для обработки исключений в Java используются следующие ключевые слова:

  • try;
  • catch;
  • throw;
  • throws;
  • finally.

 

6. Какое назначение конструкции try… catch…finally? Общая форма

В языке Java можно вручную перехватывать исключения (исключительные ситуации) и соответствующим образом обрабатывать их. Важно, чтобы при возникновении исключительной ситуации программа не прекратила свое выполнение.

Для отслеживания, генерирования и обработки исключений в языке программирования Java используется конструкция try…catch…finally, которая имеет следующую общую форму

try {
    // блок кода, в котором отслеживаются и генерируются исключения
    // ...
}
catch (type_exception_1 objEx1) {
    // обработчик исключения типа type_exception_1
    // ...
}
catch (type_exception_2 objEx2) {
    // обработчик исключения типа type_exception_2
    // ...
}

...

catch (type_exception_N objEx) {
    // обработчик исключения типа type_exception_N
    // ...
}
finally {
    // блок кода, который должен быть обязательно выполнен
    // после завершения блока try
    // ...
}

В блоке try размещаются операторы программы, которые нужно проконтролировать и, в случае возникновения исключительной ситуации, сгенерировать исключение. Внутри блока try могут быть вызваны различные методы, способные сгенерировать то или иное исключение. Однако, обработчик исключения будет только один.

В блоке catch размещается программный код, который обрабатывает перехваченное исключение (обработчик исключения). Код в блоке catch реализует выполнение соответствующих действий, когда произошла исключительная ситуация в блоке try.   Блоков catch может быть несколько. Если генерируется исключение, механизм обработки исключений ищет первый из обработчиков исключений, аргумент которого соответствует текущему типу исключения. После этого он входит в блок catch, и, в результате исключение считается обработанным. Только один соответствующий блок catch обрабатывается.

В блоке finally указывается код, который должен быть обязательно выполнен после завершения блока try. Блок операторов finally выполняется независимо от того, будет ли сгенерировано исключение или нет. Если исключение сгенерировано, блок операторов finally выполняется даже при условии, что ни один из операторов catch не совпадает с этим исключением.

Операторы try и catch составляют единое целое. Оператор finally может отсутствовать.

 

7. Пример генерирования исключения и его перехват стандартным обработчиком исключений Java

Если в программе отсутствует собственный блок try…catch перехвата исключения, будет вызван стандартный обработчик исключения. В примере демонстрируется вызов стандартного обработчика.

Объявляется класс DemoExceptions. С целью демонстрации в классе объявляется метод DivNumbers(). Метод возвращает число типа double, которое есть результатом деления двух целых значений переменных a и b. Эти переменные есть входными параметрами метода.

Если b = 0 то при делении a на b может возникнуть исключительная ситуация «деление на нуль» типа ArithmeticException. В программе нет кода, который явным образом перехватывает и обрабатывает эту ситуацию. Поэтому, вызывается стандартный обработчик Java.

Текст класса DemoExceptions следующий:

// класс реализующий метод,
// в котором может возникнуть исключительная ситуация
class DemoExceptions {

    // метод, в котором может возникнуть деление на 0
    double DivNumbers(int a, int b) {

        // при делении на 0 будет вызван стандартный обработчик Java
        double res;
        res = a/b;
        return res;
    }
}

Если в другом классе использовать метод DivNumbers() следующим образом

public class Train01 {

    public static void main(String[] args) {
        DemoExceptions d = new DemoExceptions(); // создать экземпляр класса
        double res;

        // вызвать метод DivNumbers()
        res = d.DivNumbers(2, 0); // 2/0 => деление на ноль
        System.out.println("res = " + res);
    }
}

то будет вызванный стандартный обработчик Java, который выведет следующее сообщение

Exception in thread "main" java.lang.ArithmeticException: / by zero
at DemoExceptions.DivNumbers(Train01.java:10)
at Train01.main(Train01.java:82)

В сообщении указывается тип класса ArithmeticException. Это подкласс производный от класса RunTimeException. Этот подкласс описывает арифметические ошибки «деление на 0», «взятие корня из отрицательного числа» и т.п..

 

8. Пример перехвата и обработки исключения с помощью оператора try…catch

В примере реализован класс DemoExceptions, в котором объявляется метод с именем DivNumbers2(). Метод DivNumbers2() делит входящий параметр a на входящий параметр b. В методе DivNumbers2() продемонстрирован перехват исключения деления на ноль с помощью блока try…catch.

Текст класса DemoExceptions следующий

// класс реализующий метод,
// в котором может возникнуть исключительная ситуация
class DemoExceptions {

    // метод, в котором обработано деление на 0,
    // блок try..catch
    double DivNumbers2(int a, int b) {
        double res=0; // переменная res обязательно должна быть инициализирована

        try {
            res = a/b; // если b=0, то генерируется исключение
        }
        catch (ArithmeticException e) {
            System.out.println("Деление на 0.");
        }

        return res;
    }
}

Теперь, при использовании метода DivNumbers2() в другом классе

public class Train01 {

    public static void main(String[] args) {
        DemoExceptions d = new DemoExceptions(); // создать экземпляр класса
        double res;

        // вызвать метод DivNumbers2()
        res = d.DivNumbers2(2, 0); // 2/0 => деление на ноль

        System.out.println("res = " + res);
    }
}

система выдаст следующий результат после запуска программы на выполнение

Деление на 0.
res = 0.0

Как видно из вышеприведенного кода, в методе DivNumbers2() будет вызван собственный обработчик, который реализован в операторе try…catch. В блок try помещается программный код, который нужно проверить

...
res = a/b;
...

Если возникает исключение (b=0), то управление сразу передается из блока try в блок catch. В блоке catch происходит обработка исключения с выводом соответствующего сообщения

...
catch (ArithmeticException e) {
    System.out.println("Деление на 0.");
}
...

Итак, если b=0:

  • генерируется исключение типа ArithmeticException;
  • происходит переход к обработке исключения (блок catch);
  • в блоке catch выводится соответствующее сообщение;
  • после блока catch выполнение программы не останавливается. В результате переменная res получает значение 0.

Можно изменить вывод собственного сообщения об исключении на стандартное сообщение. В этом случае нужно изменить текст в блоке catch на следующий

...
catch (ArithmeticException e) {
    System.out.println(e);
    //System.out.println("Деление на 0.");
}
...

После изменений, результат выполнения программы будет такой

java.lang.ArithmeticException: / by zero
res = 0.0

 

9. Пример перехвата и обработки исключения блоком try…catch…finally. Демонстрация работы блока операторов finally

В примере продолжено развитие двух предшествующих пунктов 7, 8. Пример демонстрирует назначение блока finally в операторе try…catch…finally.

Объявляется класс DemoExceptions. В классе продемонстрирован перехват исключительной ситуации деления на 0. Этот перехват реализован в методе DivNumbers3() класса. Для перехвата и обработки исключительной ситуации, метод DivNumbers3() использует оператор try…catch…finally.

Текст класса DemoExceptions следующий

// класс, который реализует метод,
// в котором может возникнуть исключительная ситуация
class DemoExceptions {

    // метод, в котором обработано деление на 0,
    // блок try..catch..finally
    double DivNumbers3(int a, int b) {
        double res; // не обязательно инициализировать, поскольку есть блок finally
        try {
            res = a/b;
        }
        catch (ArithmeticException e) {
            System.out.println("Деление на 0.");
        }
        finally {
            res = 0; // вызовется обязательно
        }
        return res;
    }
}

В методе DivNumbers3() объявляется переменная res, что есть результатом деления a на b. Поскольку оператор try…catch содержит блок finally, то строка

...
finally {
    res = 0; // вызовется обязательно
}
...

будет вызываться всегда. И перед оператором

...
return res;
...

переменная res будет всегда инициализирована. Поэтому компилятор Java не требует инициализации переменной в начале кода. Как известно, чтобы возвратить значение переменной оператором return, эта переменная должна быть обязательно инициализирована. Итак, блок finally вызывается всегда.

Использование метода DivNumbers3() в другом классе может быть следующим

public class Train01 {

    public static void main(String[] args) {
        DemoExceptions d = new DemoExceptions();
        double res;
        res = d.DivNumbers3(2, 0); // 2/0 => деление на ноль
        System.out.println("res = " + res);
    }
}

Результат выполнения программы

Деление на 0.
res = 0.0

 

10. Как реализовать вывод описания исключения? Пример

Если нужно вывести описание исключения e, то можно изменить вывод в обработчике catch следующим образом (см. пункт 8)

...
catch (ArithmeticException e) {
    // перехватить ошибку деления на 0
    System.out.println("Исключение: " + e);
}
...

В результате программа будет выводить следующий результат

Исключение: java.lang.ArithmeticException: / by zero

 

11. Пример перехвата нескольких исключительных ситуаций (несколько операторов catch)

Бывают случаи, когда в одном фрагменте кода может возникнуть несколько исключений. Для обработки нескольких исключительных ситуаций в блоке try…catch можно использовать несколько операторов catch. Каждый из операторов catch используется для перехвата отдельного типа исключения.

Реализация класса DivArrays следующая

// несколько операторов catch
// класс, который делит поэлементно массивы
class DivArrays {
    double[] A1;
    double[] A2;

    // конструктор класса
    DivArrays(int n1, double[] _A1, int n2, double[] _A2) {
        // создать массивы
        A1 = new double[n1];
        for (int i=0; i<n1; i++)
            A1[i] = _A1[i];

        A2 = new double[n2];
        for (int i=0; i<n2; i++)
            A2[i] = _A2[i];
    }

    // метод, который делит поэлементно массив A1 на массив A2
    // метод возвращает массив типа double
    double[] Division() {
        double[] A3;
        int n3;

        // установить наибольший из размеров
        if (A1.length > A2.length)
            n3 = A1.length;
        else
            n3 = A2.length;

        A3 = new double[n3];

        // цикл по i, в цикле обрабатывается исключительная ситуация
        for (int i=0; i<n3; i++) {
            try {
                // сгенерировать исключение, если деление на 0
                if (A2[i]==0.0)
                    throw new ArithmeticException();
                A3[i] = A1[i]/A2[i];
            }
            catch (ArithmeticException e) {
                // перехватить деление на 0
                A3[i] = 0; // просто установить в A3 значение 0
            }
            catch (ArrayIndexOutOfBoundsException e) {
                // перехватить выход индекса за границы массива
                // это случай, когда длины массивов отличаются
                A3[i] = -1;
            }
        }
        return A3;
    }
}

Использование класса в другом программном коде

public class Train03 {

    public static void main(String[] args) {
        double[] A1 = { 2.5, 2.0, 1.5, 0.5 };
        double[] A2 = { 1.0, 0.0, 2.0 };
        double[] A3;

        DivArrays dA = new DivArrays(4,A1,3,A2);

        A3 = dA.Division();

        // вывести массив d
        for (int i=0; i<A3.length; i++)
            System.out.println("A[" + i + "] = " + A3[i]);
    }
}

Результат выполнения программы

A[0] = 2.5
A[1] = 0.0
A[2] = 0.75
A[3] = -1.0

 


Связанные темы