Spec-Zone.ru › Java Language Specification 17

Глава 11. Исключения

Оглавление

11.1. Виды и причины исключений
11.1.1. Виды исключений
11.1.2. Причины исключений
11.1.3. Асинхронные исключения
11.2. Проверка исключений во время компиляции
11.2.1. Анализ выражений на наличие исключений
11.2.2. Анализ операторов на наличие исключений
11.2.3. Проверка на наличие исключений
11.3. Обработка исключений во время выполнения

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

Пример такого нарушения — попытка обратиться к элементу массива по индексу, выходящему за границы массива. Некоторые языки программирования и их реализации реагируют на такие ошибки, прерывая выполнение программы; другие языки позволяют реализации реагировать произвольным или непредсказуемым образом. Ни один из этих подходов не совместим с целями разработки платформы Java SE: обеспечение переносимости и надёжности.

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

Говорят, что исключение выбрасывается из точки его возникновения и перехватывается в точке, в которую передаётся управление.

Программы также могут явно выбрасывать исключения, используя throw операторы (§14.18).

Явное использование throw операторов предоставляет альтернативу устаревшему стилю обработки ошибок с помощью возврата необычных значений, таких как целочисленное значение -1, где отрицательное значение не ожидалось бы. Опыт показывает, что такие необычные значения слишком часто игнорируются или не проверяются вызывающими сторонами, что приводит к программам, которые не являются надёжными, демонстрируют нежелательное поведение или то и другое.

Каждое исключение представляется экземпляром класса Throwable или одного из его подклассов (§11.1). Такой объект может использоваться для передачи информации из точки возникновения исключения в обработчик, который его перехватывает. Обработчики устанавливаются с помощью catch блоков try операторов (§14.20).

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

Механизм исключений платформы Java SE интегрирован с её моделью синхронизации (§17.1), таким образом, мониторы разблокируются, когда synchronized операторы (§14.19) и вызовы synchronized методов (§8.4.3.6, §15.12) завершаются внезапно.

11.1. Виды и причины исключений

11.1.1. Виды исключений

Исключение представляется экземпляром класса Throwable (прямого подкласса Object) или одного из его подклассов.

Классы Throwable и все его подклассы в совокупности являются классами исключений.

Классы Exception и Error являются прямыми подклассами Throwable:

  • Exception является суперклассом всех исключений, из которых обычные программы могут попытаться восстановиться.

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

    Классы RuntimeException и все его подклассы в совокупности являются классами исключений времени выполнения.

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

    Классы Error и все его подклассы в совокупности являются классами ошибок.

Классы исключений, не требующих проверки — это классы исключений времени выполнения и классы ошибок.

Классы исключений, требующих проверки — это все классы исключений, кроме исключений, не требующих проверки. То есть классы исключений, требующих проверки, это Throwable и все его подклассы, кроме RuntimeException и его подклассов, и Error и его подклассов.

Программы могут использовать существующие классы исключений API платформы Java SE в инструкциях throw или определять дополнительные классы исключений как подклассы Throwable или любого из его подклассов, в зависимости от ситуации. Для использования проверки исключений на этапе компиляции (§11.2) обычно рекомендуется определять большинство новых классов исключений как классы исключений, требующих проверки, то есть как подклассы Exception, которые не являются подклассами RuntimeException.

Класс Error является отдельным подклассом Throwable, отличным от Exception в иерархии классов, что позволяет программам использовать выражение "} catch (Exception e) {" (§11.2.3) для перехвата всех исключений, из которых возможно восстановление, не перехватывая ошибки, из которых обычно восстановление невозможно.

Обратите внимание, что подкласс Throwable не может быть обобщённым (§8.1.2).

11.1.2. Причины исключений

Исключение выбрасывается по одной из трёх причин:

  • Была выполнена инструкция throw (§14.18).

  • Виртуальная машина Java синхронно обнаружила аномальное условие выполнения, а именно:

    • Вычисление выражения нарушает стандартную семантику языка программирования Java (§15.6), например, целочисленное деление на ноль.

    • Произошла ошибка при загрузке, связывании или инициализации части программы (§12.2, §12.3, §12.4); в этом случае выбрасывается экземпляр подкласса LinkageError.

    • Внутренняя ошибка или ограничение ресурсов предотвращает виртуальную машину Java от реализации семантики языка программирования Java; в этом случае выбрасывается экземпляр подкласса VirtualMachineError.

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

  • Произошло асинхронное исключение (§11.1.3).

11.1.3. Асинхронные исключения

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

Асинхронные исключения возникают только в результате:

  • Вызова метода (устаревшего) stop класса Thread или ThreadGroup.

    (Устаревшие) методы stop могут вызываться одним потоком для воздействия на другой поток или на все потоки в заданной группе потоков. Они асинхронны, потому что могут возникнуть в любой точке выполнения других потоков.

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

    Обратите внимание, что StackOverflowError, подкласс VirtualMachineError, может выбрасываться синхронно при вызове метода (§15.12.4.5), а также асинхронно из-за выполнения метода native или ограничений ресурсов виртуальной машины Java. Аналогично, OutOfMemoryError, другой подкласс VirtualMachineError, может быть выброшен синхронно во время создания экземпляра класса (§15.9.4, §12.5), создания массива (§15.10.2, §10.6), инициализации класса (§12.4.2) и преобразования в условный тип (§5.1.7), а также асинхронно.

Платформа Java SE допускает небольшое, но ограниченное выполнение кода до момента выбрасывания асинхронного исключения.

Асинхронные исключения редки, но для создания качественного машинного кода необходимо правильное понимание их семантики.

Указанная выше задержка допускается для оптимизированного кода, чтобы обнаружить и выбросить эти исключения в точках, где их практично обработать, соблюдая семантику языка программирования Java. Простой реализацией может быть проверка на наличие асинхронных исключений в каждой точке перехода управления. Поскольку программа имеет конечный размер, это предоставляет ограничение на общую задержку при обнаружении асинхронного исключения. Поскольку асинхронное исключение не возникнет между переходами управления, генератор кода имеет некоторую гибкость в переупорядочивании вычислений между переходами управления для повышения производительности. Статья Polling Efficiently on Stock Hardware Марка Фели, Proc. 1993 Conference on Functional Programming and Computer Architecture, Копенгаген, Дания, стр. 179-187, рекомендуется для дальнейшего прочтения.

11.2. Проверка исключений на этапе компиляции

Язык программирования Java требует, чтобы программа содержала обработчики исключений, которые проверяются на этапе компиляции, которые могут возникнуть при выполнении метода или конструктора (§8.4.6, §8.8.5). Эта проверка на этапе компиляции наличия обработчиков исключений предназначена для уменьшения количества исключений, которые не обрабатываются должным образом. Для каждого исключения, которое может возникнуть в результате выполнения метода или конструктора, в строке throws для метода или конструктора необходимо указать класс этого исключения или один из его суперклассов (§11.2.3).

Классы исключений, которые проверяются на этапе компиляции (§11.1.1), указанные в строке throws, являются частью соглашения между разработчиком и пользователем метода или конструктора. Строка throws переопределяемого метода не может указывать, что этот метод может привести к выбрасыванию каких-либо исключений, которые проверяются на этапе компиляции, если переопределяемый метод не может, согласно строке throws, выбросить такое исключение (§8.4.8.3). Если задействованы интерфейсы, то один метод может быть переопределен несколькими переопределяющими объявлениями. В этом случае переопределяющее объявление должно иметь строку throws, совместимую со всеми переопределяемыми объявлениями (§9.4.1).

Классы исключений, которые не проверяются на этапе компиляции (§11.1.1), освобождены от проверки на этапе компиляции.

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

Классы исключений времени выполнения освобождены от проверки, поскольку, по мнению разработчиков языка программирования Java, необходимость объявлять такие исключения не будет существенно способствовать установлению правильности программ. Многие операции и конструкции языка программирования Java могут привести к исключениям во время выполнения. Информация, доступная компилятору Java, и уровень анализа, который выполняет компилятор, обычно недостаточны для подтверждения того, что такие исключения во время выполнения не могут произойти, даже если это очевидно для программиста. Требование объявления таких классов исключений просто станет раздражающим для программистов.

Например, некоторый код может реализовать циклическую структуру данных, которая по построению никогда не может включать ссылки null; программист может быть уверен, что NullPointerException не может произойти, но компилятор Java будет затрудняться в его доказательстве. Технология теоретического доказательства, необходимая для установления таких глобальных свойств структур данных, выходит за рамки этой спецификации.

Мы говорим, что оператор или выражение может выбросить класс исключения E, если, согласно правилам в §11.2.1 и §11.2.2, выполнение оператора или выражения может привести к тому, что будет выброшено исключение класса E.

Мы говорим, что блок catch может перехватить свои перехватываемые классы исключений:

  • Перехватываемый класс исключения в блоке uni-catch — это объявленный тип его параметра исключения (§14.20).

  • Перехватываемые классы исключений в блоке multi-catch — это альтернативы в объединении, которое обозначает тип его параметра исключения.

11.2.1. Анализ исключений выражений

Выражение создания экземпляра класса (§15.9) может выбросить исключение класса E, если выполняется одно из следующих условий:

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

  • Некоторые выражения списка аргументов могут выбросить E; или

  • E — это один из типов исключений типа вызова выбранного конструктора (§15.12.2.6); или

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

Выражение вызова метода (§15.12) может выбросить исключение класса E, если выполняется одно из следующих условий:

  • Выражение вызова метода имеет вид Primary . [TypeArguments] Identifier, и выражение Primary может выбросить E; или

  • Некоторые выражения списка аргументов могут выбросить E; или

  • E — это один из типов исключений типа вызова выбранного метода (§15.12.2.6).

Выражение лямбда-выражения (§15.27) не может выбросить никаких классов исключений.

Выражение switch (§15.28) может выбросить исключение класса E, если выполняется одно из следующих условий:

  • Выражение селектора может выбросить E; или

  • Некоторые выражения правила переключателя, блок правила переключателя, выражение throw правила переключателя или группа операторов с метками переключателя в блоке переключателя могут выбросить E.

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

Обратите внимание, что выражение ссылки на метод (§15.13) вида Primary :: [TypeArguments] Identifier может выбросить исключение, если подвыражение Primary может выбросить исключение. В отличие от этого, лямбда-выражение ничего не может выбросить и не имеет непосредственных подвыражений, на которых можно выполнить анализ исключений. Это тело лямбда-выражения, содержащее выражения и операторы, которые могут выбросить классы исключений.

11.2.2. Анализ исключений в операторах

Оператор §14.18 с выражением, бросающим исключение статического типа E и не являющимся конечным или эффективно конечным параметром исключения, может бросить исключение типа E или любой класс исключения, который может бросить выражение.

Например, оператор throw new java.io.FileNotFoundException(); может бросить только java.io.FileNotFoundException. Формально, он не может "бросить" подкласс или надкласс java.io.FileNotFoundException.

Оператор throw, выражение которого бросает исключение, являющееся конечным или эффективно конечным параметром исключения блока обработки исключений C, может бросить исключение типа E, если:

  • E — класс исключения, который может бросить блок try оператора try, который объявляет C; и

  • E совместим по присваиванию с любым из классов исключений, которые можно перехватить в C; и

  • E не совместим по присваиванию ни с одним из классов исключений, которые можно перехватить в блоках обработки исключений, объявленных слева от C в том же операторе try.

Оператор try (§14.20) может бросить исключение типа E, если выполняется одно из следующих условий:

  • Блок try может бросить исключение E, или выражение, используемое для инициализации ресурса (в операторе try-с-ресурсами), может бросить исключение E, или автоматическое вызов метода close() ресурса (в операторе try-с-ресурсами) может бросить исключение E, и E не совместим по присваиванию с каким-либо классом исключения, который может перехватывать любой блок обработки исключений оператора try, и либо блок finally отсутствует, либо блок finally может завершиться нормально; или

  • Некоторый блок catch оператора try может бросить исключение E и либо блок finally отсутствует, либо блок finally может завершиться нормально; или

  • Блок finally присутствует и может бросить исключение E.

Оператор явного вызова конструктора (§8.8.7.1) может бросить исключение типа E, если выполняется одно из следующих условий:

  • Некоторые выражения в списке параметров вызова конструктора могут бросить исключение E; или

  • E определён как класс исключения блока обработки исключений вызываемого конструктора (§15.12.2.6).

Оператор switch (§14.11) может бросить исключение типа E, если выполняется одно из следующих условий:

  • Выражение-селектор может бросить исключение E; или

  • Некоторые выражения, блоки, операторы throw или группы операторов, помеченных метками, в блоке оператора switch могут бросить исключение E.

Любой другой оператор S может бросить исключение типа E, если выражение или оператор, немедленно содержащийся в S, может бросить E.

11.2.3. Проверка исключений

Ошибка компиляции, если тело метода или конструктора может бросить исключение класса E, когда E — класс проверяемого исключения и E не является подклассом какого-либо класса, объявленного в блоке обработки исключений метода или конструктора.

Ошибка компиляции, если тело лямбда-выражения может бросить исключение класса E, когда E — класс проверяемого исключения и E не является подклассом какого-либо класса, объявленного в блоке обработки исключений целевого типа функции, к которому относится лямбда-выражение.

Ошибка компиляции, если инициализатор переменной класса (§8.3.2) или статический инициализатор (§8.7) именованного класса или интерфейса может бросить проверяемый класс исключения.

Ошибка компиляции, если инициализатор переменной экземпляра (§8.3.2) или инициализатор экземпляра (§8.6) именованного класса может бросить проверяемый класс исключения, если у именованного класса нет ни одного явно объявленного конструктора, и класс исключения или один из его предков не объявлен в блоке обработки исключений каждого конструктора.

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

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

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

Компилятор Java рекомендуется выдавать предупреждение, если блок обработки исключений может перехватить проверяемый класс исключения E1, а соответствующий блок catch может бросить проверяемый класс исключения E2, где E2 <: E1, и предшествующий блок обработки исключений оператора try может перехватить проверяемый класс исключения E3, где E2 <: E3 <: E1.

Пример 11.2.3-1. Перехват проверяемых исключений

import java.io.*;

class StaticallyThrownExceptionsIncludeSubtypes {
    public static void main(String[] args) {
        try {
            throw new FileNotFoundException();
        } catch (IOException ioe) {
            // "catch IOException" catches IOException 
            // and any subtype.
        }

        try {
            throw new FileNotFoundException();
              // Statement "can throw" FileNotFoundException.
              // It is not the case that statement "can throw"
              // a subtype or supertype of FileNotFoundException.
        } catch (FileNotFoundException fnfe) {
            // ... Handle exception ...
        } catch (IOException ioe) {
            // Legal, but compilers are encouraged to give
            // warnings as of Java SE 7, because all subtypes of
            // IOException that the try block "can throw" have 
            // already been caught by the prior catch clause.
        }

        try {
            m();
              // m's declaration says "throws IOException", so
              // m "can throw" IOException. It is not the case
              // that m "can throw" a subtype or supertype of
              // IOException (e.g. Exception).
        } catch (FileNotFoundException fnfe) {
            // Legal, because the dynamic type of the exception 
            // might be FileNotFoundException.
        } catch (IOException ioe) {
            // Legal, because the dynamic type of the exception
            // might be a different subtype of IOException.
        } catch (Throwable t) {
            // Can always catch Throwable.
        }
    }

    static void m() throws IOException {
        throw new FileNotFoundException();
    }
}

По вышеуказанным правилам, каждая альтернатива в блоке обработки исключений (§14.20) должна уметь перехватывать исключение, которое бросает блок try и не перехвачено предыдущими блоками обработки исключений. Например, второй блок обработки исключений ниже вызовет ошибку компиляции, так как анализ исключений определит, что SubclassOfFoo уже перехвачен первым блоком обработки исключений:

try { ... }
catch (Foo f) { ... }
catch (Bar | SubclassOfFoo e) { ... }

11.3. Обработка исключений во время выполнения

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

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

Вызывающий оператор или выражение зависит от места их расположения:

  • Если внутри метода, то вызывающим является выражение вызова метода (§15.12), которое было выполнено для вызова метода.

  • Если внутри конструктора, инициализатора экземпляра или инициализатора переменной экземпляра, то вызывающим является выражение создания экземпляра класса (§15.9) или вызов метода newInstance, который был выполнен для создания объекта.

  • Если внутри статического инициализатора или инициализатора переменной static, то вызывающим является выражение, использующее класс или интерфейс, чтобы вызвать его инициализацию (§12.4).

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

Аналогично, catch блок перехватит любой объект исключения, который является instanceof (§15.20.2) одним из его обрабатываемых классов исключений.

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

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

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

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

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

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

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

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

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

Точные правила прерывистого завершения и перехвата исключений подробно описаны в спецификации каждого оператора в §14 (Блоки, операторы и шаблоны) и для выражений в §15 (Выражения) (особенно §15.6).

Пример 11.3-1. Выбрасывание и перехват исключений

Следующая программа объявляет класс исключений TestException. Метод main класса Test вызывает метод thrower четыре раза, что приводит к выбросу исключений в трёх из четырёх случаев. Оператор try в методе main перехватывает каждое исключение, выброшенное в методе. Вне зависимости от того, завершается ли вызов 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, который является классом исключений, обрабатываемых во время компиляции (§11.1.1). Отсутствие throws блока привело бы к ошибке компиляции.

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


© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API