Исключения и их обработка

Исключениями или исключительными ситуациями (состояниями) называются ошибки, возникшие в программе во время её работы.

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

Иерархия классов исключений:

Исключения делятся на несколько классов, но все они имеют общего предка — класс Throwable. Его потомками являются подклассы Exception и Error.

Исключения (Exceptions) являются результатом проблем в программе, которые в принципе решаемы и предсказуемы. Например, произошло деление на ноль в целых числах.

Ошибки (Errors) представляют собой более серьёзные проблемы, которые, согласно спецификации Java, не следует пытаться обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM. Например, исключения такого рода возникают, если закончилась память, доступная виртуальной машине. Программа дополнительную память всё равно не сможет обеспечить для JVM.

В Java все исключения делятся на три типа: контролируемые исключения (checked) и неконтролируемые исключения (unchecked), к которым относятся ошибки (Errors) и исключения времени выполнения (RuntimeExceptions, потомок класса Exception).

Контролируемые исключения представляют собой ошибки, которые можно и нужно обрабатывать в программе, к этому типу относятся все потомки класса Exception (но не RuntimeException).

Обработка исключения может быть произведена с помощью операторов try…catch, либо передана внешней части программы. Например, метод может передавать возникшие в нём исключения выше по иерархии вызовов, сам его не обрабатывая.

Неконтролируемые исключения не требуют обязательной обработки, однако, при желании, можно обрабатывать исключения класса RuntimeException.

Откомпилируем и запустим такую программу:

class Main { 
     public static void main(String[] args) { 
         int a = 4; 
         System.out.println(a/0); 
     } 
}

В момент запуска на консоль будет выведено следующее сообщение:

Exception in thread "main" java.lang.ArithmeticException: / by zero 
        at Main.main(Main.java:4)

Из сообщения виден класс случившегося исключения — ArithmeticException. Это исключение можно обработать:

class Main { 
     public static void main(String[] args) { 
         int a = 4; 
         try { 
              System.out.println(a/0); 
         } catch (ArithmeticException e) { 
              System.out.println("Произошла недопустимая арифметическая операция"); 
         } 
     } 
}

Теперь вместо стандартного сообщения об ошибке будет выполняться блок catch, параметром которого является объект e соответствующего исключению класса (самому объекту можно давать любое имя, оно потребуется в том случае, если мы пожелаем снова принудительно выбросить это исключение, например, для того, чтобы оно было проверено каким-то ещё обработчиком).

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

Одному try может соответствовать сразу несколько блоков catch с разными классами исключений.

import java.util.Scanner; 
class Main { 
    public static void main(String[] args) { 
     int[] m = {-1,0,1}; 
        Scanner sc = new Scanner(System.in); 
        try { 
            int a = sc.nextInt();     
            m[a] = 4/a; 
            System.out.println(m[a]); 
        } catch (ArithmeticException e) { 
            System.out.println("Произошла недопустимая арифметическая операция"); 
        } catch (ArrayIndexOutOfBoundsException e) { 
            System.out.println("Обращение по недопустимому индексу массива"); 
        } 
    } 
}

Если запустив представленную программу, пользователь введётся с клавиатуры 1 или 2, то программа отработает без создания каких-либо исключений.

Если пользователь введёт 0, то возникнет исключение класса ArithmeticException, и оно будет обработано первым блоком catch.

Если пользователь введёт 3, то возникнет исключение класса ArrayIndexOutOfBoundsException (выход за приделы массива), и оно будет обработано вторым блоком catch.

Если пользователь введёт нецелое число, например, 3.14, то возникнет исключение класса InputMismatchException (несоответствие типа вводимого значение), и оно будет выброшено в формате стандартной ошибки, поскольку его мы никак не обрабатывали.

Можно, однако, добавить обработчик для класса Exception, поскольку этот класс родительский для всех остальных контролируемых исключений, то он будет перехватывать любые из них (в том числе, и InputMismatchException).

import java.util.Scanner; 
class Main { 
    public static void main(String[] args) { 
        int[] m = {-1,0,1}; 
        int a = 1; 
        Scanner sc = new Scanner(System.in);        
        try { 
            a = sc.nextInt();     
            m[a-1] = 4/a;
            System.out.println(m[a]); 
        } catch (ArithmeticException e) { 
            System.out.println("Произошла недопустимая арифметическая операция"); 
        } catch (ArrayIndexOutOfBoundsException e) { 
            System.out.println("Обращение по недопустимому индексу массива");       
        } catch (Exception e) { 
            System.out.println("Произошло ещё какое-то исключение"); 
        } 
    } 
}

Поскольку исключения построены на иерархии классов и подклассов, то сначала надо пытаться обработать более частные исключения и лишь затем более общие. То есть поставив первым (а не третьим) блок с обработкой исключения класса Exception, мы бы никогда не увидели никаких сообщений об ошибке, кроме «Произошло ещё какое-то исключение» (все исключения перехватились бы сразу этим блоком и не доходили бы до остальных).

Необязательным добавлением к блокам try…catch может быть блок finally. Помещенные в него команды будут выполняться в любом случае, вне зависимости от того, произошло ли исключение или нет. При том, что при возникновении необработанного исключения оставшаяся после генерации этого исключения часть программы — не выполняется. Например, если исключение возникло в процессе каких-то длительных вычислений, в блоке finally можно показать или сохранить промежуточные результаты.