Спецификация Java 11: 11.3. Обработка исключения во время выполнения

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

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

Вызывающий оператор или выражение зависит от того, где он встречается:

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

Может ли конкретное предложение catch обрабатывать исключение, определяется путем сравнения класса объекта, который был выдан, с классами перехватываемых исключений в предложении catch. Предложение catch может обрабатывать исключение, если один из его перехватываемых классов исключений является классом исключения или суперклассом класса исключения.

Точно так же предложение catch перехватит любой объект исключения, который является instanceof одного из его классов исключений, которые можно перехватить.

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

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

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

Если не удается найти предложение catch, которое может обрабатывать исключение, текущий поток (поток, обнаруживший исключение) завершается. Перед завершением все предложения finally выполняются, а неперехваченное исключение обрабатывается в соответствии со следующими правилами:

  • Если текущий поток имеет установленный обработчик неперехваченных исключений, то этот обработчик выполняется.
  • В противном случае метод uncaughtException вызывается для ThreadGroup, который является родительским для текущего потока. Если ThreadGroup и его родительские ThreadGroups не переопределяют uncaughtException, то вызывается метод uncaughtException обработчика по умолчанию.

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

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

Если предложение finally выполняется из-за внезапного завершения блока try, а само предложение finally завершается внезапно, то причина внезапного завершения блока try отбрасывается, и оттуда распространяется новая причина внезапного завершения.

Пример выбрасывания и перехвата исключений

Следующая программа объявляет класс исключения TestException. Основной метод класса Test вызывает метод thrower четыре раза, в результате чего исключения генерируются три из четырех раз. Оператор try в методе main перехватывает каждое исключение, генерируемое thrower. Независимо от того, завершается ли вызов thrower нормально или внезапно, выводится сообщение с описанием того, что произошло.

class TestException extends Exception {
    TestException()         { super(); }
    TestException(String s) { super(s); }
}

class Test {
    public static void main(String[] args) {
        for (String arg : args) {
            try {
                thrower(arg);
                System.out.println("Test \"" + arg +
                                   "\" didn't throw an exception");
            } catch (Exception e) {
                System.out.println("Test \"" + arg +
                                   "\" threw a " + e.getClass() +
                                   "\n    with message: " +
                                   e.getMessage());
            }
        }
    }
    static int thrower(String s) throws TestException {
        try {
            if (s.equals("divide")) {
                int i = 0;
                return i/i;
            }
            if (s.equals("null")) {
                s = null;
                return s.length();
            }
            if (s.equals("test")) {
                throw new TestException("Test message");
            }
            return 0;
        } finally {
            System.out.println("[thrower(\"" + s + "\") done]");
        }
    }
}

Если мы выполним программу, передав ей аргументы:

divide null not test

она производит вывод:

[thrower("divide") done]
Test "divide" threw a class java.lang.ArithmeticException
    with message: / by zero
[thrower("null") done]
Test "null" threw a class java.lang.NullPointerException
    with message: null
[thrower("not") done]
Test "not" didn't throw an exception
[thrower("test") done]
Test "test" threw a class TestException
    with message: Test message

Объявление thrower метода должно иметь предложение throws, потому что оно может генерировать экземпляры TestException, который является проверенным классом исключения. Если бы предложение throws было опущено, возникла бы ошибка времени компиляции.

Обратите внимание, что предложение finally выполняется при каждом вызове метода thrower, независимо от того, возникает ли исключение, как показано в выводе «[thrower (...) done]», которое происходит для каждого вызова.


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки