Meet Me

Java SE‎ > ‎

Исключения

posted Sep 17, 2011, 6:54 AM by val ryzhuk

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

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

Каждое исключение представляется экземпляром класса Throwable или одним из его подклассов; такой объект может использоваться для того, чтобы нести информацию с места, в котором исключение происходит к обработчику, который это исключение перехватывает. Обработчики устанавливаются предложениями catch операторов try(§14.18). В процессе генерирования исключения, виртуальная машина языка Ява завершает преждевременно, один за другим, любые выражения, операторы, вызовы метода и конструктора, статические инициализаторы и поле инициализированных выражений, которые начали, но не закончили выполнение в текущем потоке. Этот процесс продолжается до тех пор, пока обработчик не найдет, что указывает на то, что это обрабатывается как частичное исключение присваиванием имен класса исключения или суперкласса класса исключения. Если такой обработчик не найден, тогда метод uncaughtException (§20.21.31) вызывается для ThreadGroup, который является породителем нитевого потока, таким образом весь объем работы производится для того, чтобы избежать ситуации необработанного исключения.

Механизм исключений Ява объединен с моделью синхронизации Ява (§17), так , что блокировки реализованы какsynchronized-операторы (§14.17) и вызовы synchronized-методов, завершаемых преждевременно (§8.4.3.5,§15.11).

Эта глава описывает различные причины исключений (§11.1). Здесь подробно говорится о том как проверяются исключения во время компиляции(§11.2) и обрабатываются во время выполнения (§11.3). Разобранный пример (§11.4)сопровождается объяснением иерархии исключительных ситуаций и стандартных классов исключений (§11.5).

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

Исключение генерируется по одной из трех причин:

  • Виртуальной машиной языка Ява было обнаружено ошибочное состояние. Такие состояния возникают потому, что:

 

o    формула оценки выражения нарушает нормальную семантику языка Ява, например, как установлено в итоге §15.5 - целое число делится на ноль.

o    ошибка происходит при загрузке или компоновке части Ява-программы (§12.2, §12.3)

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

  • Эти исключения не генерируются в произвольном месте программы, а генерируются там, где они определяются как возможный результат формулы оценки выражения или выполнения оператора.
  • Оператор throw (§14.16) был выполнен в Ява- коде.
  • Асинхронное исключение произошло потому, что:

 

o    был вызван метод stop класса Thread (§20.20.16)

o    в виртуальной машине произошла внутренняя ошибка (§11.5.2.2)

Исключения представлены экземплярами класса Throwable и его подклассов. Все вместе эти классы- классы исключений

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

Во время компиляции язык Ява проверяет то, что Ява- программа содержит обработчики для проверяемых исключений, в результате анализа которых они могут заканчиваться выполнением метода или конструктора. Для каждого проверяемого исключения, которое является возможным результатом, предложения trows метода (§8.4.4)или конструктора (§8.6.4) должны упоминать класс этого исключения или один из суперклассов класса исключения. Эта проверка времени компиляции при наличии обработчиков исключения предназначена для уменьшения числа исключений, которые не обработаны должным образом.

Классы непроверяемых исключений - это класс RuntimeException и его подклассы, а также класс Error и его подклассы. Все оставшиеся классы исключений - классы проверяемых исключений. Стандартный интерфейс прикладного программирования (API) языка Ява определяет количество классов исключений, проверяемых и непроверяемых. Дополнительные классы исключений, проверяемые и непроверяемые, могут быть объявлены самими программистами. Для изучения исключительной ситуации класса иерархии и классов исключений, определенных стандартным интерфейсом прикладного программирования (API) и виртуальной машиной языка Ява cм. §11.5.

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

Инициализаторы-переменные для полей (§8.3.2) и статические инициализаторы(§8.5) не должны заканчиваться проверяемым исключением; иначе происходит ошибка времени компиляции.

11.2.1 Почему ошибки не контролируются

Непроверяемые классы исключений, которые являются классами ошибок (Error и его подклассы), свободны от проверки времени компиляции, потому что они могут происходить во многих местах программы, и возвращение из них трудно или невозможно. Ява-программа, объявляющая такие исключения, была бы бессмысленно хаотична.

11.2.2 Почему не контролируются исключения времени выполнения

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

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

11.3 Обработка исключения

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

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

Источник вызова оператора или выражения зависит от того, где это происходит:

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

Обрабатывает ли данное предложение catch данное исключение определяется сравнением класса объекта, который был сгенерирован, и объявленного типа параметра предложения catch. Предложение catchобрабатывает исключение, если тип его параметра - это класс исключения или суперкласс класса исключения. Иначе говоря, предложение catch будет захватывать любой объект исключения, который является instanceof(§15.19.2) объявленного типа параметра.

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

Если предложение catch, обрабатывающее исключение, не может быть найдено, тогда текущий поток (поток, который столкнулся с исключением) заканчивается, но только после выполнения всех finally-предложений и дляThreadGroup вызывается метод uncaughtException (§20.21.31), то есть для родителя текущего потока.

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

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

11.3.1 Исключения точны

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

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

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

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

  • Вызова stop методов класса Thread (§20.20.15,§20.20.16) или ThreadGroup (§20.21.8,§20.21.9)
  • Внутренней ошибки (InternalError) (§11.5.2.2) в виртуальной машине языка Ява

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

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

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

Статья Polling Efficiently в Stock Hardware, автор Mark Feeley, Proc. 1993 Конференция по Функциональному программированию и архитектуре компьютера, Копенгаген, Дания, стр. 179-187, рекомендуется для дальнейшего чтения.

Подобно всем исключениям, асинхронные исключения - точны (§11.3.1).

11.4 Примеры исключений

Рассмотрим следующий пример:

 

class TestException extends Exception {

 

        TestException() { super(); }

 

 

        TestException(String s) { super(s); }

 

}

 

class Test {

 

        public static void main(String[] args) {

               for (int i = 0; i < args.length; i++) {

 

                       try {

                               thrower(args[i]);

                               System.out.println("Test \"" + args[i] +

                                      "\" didn't throw an exception");

                       } catch (Exception e) {

                               System.out.println("Test \"" + args[i] +

                                      "\" 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


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

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

Заметьте, что finally -предложение выполняется при каждом вызове thrower, вне зависимости происходит или нет исключение, как видно из "[thrower (...) done]" вывод которого происходит при каждом вызове.

11.5 Иерархия исключений

Возможные исключения Ява-программ организованы в иерархии классов, содержащейся в классе Throwable (§ 11.5,§ 20.22), непосредственном подклассе Object. Классы Exсeption и Error - непосредственные подклассы классаThrowable. Класс RuntimeException - непосредственный подкласс класса Exсeption.

Классы исключений, объявленные стандартными пакетами java.lang, java.util, java.io и java.net, называются стандартными классами исключений.

Ява-программы могут использовать ранее существующие классы исключений в операторах throw, или определять дополнительные классы исключений, как подклассы класса Throwable или любого из его подклассов. Чтобы воспользоваться преимуществами Ява-системы проверки времени компиляции для обработчиков исключений, обычно определяют большинство классов новых исключений как классы проверяемых исключений и как подклассы классаExсeption,которые не являются подклассами RuntimeException.

11.5.1 Классы Exception и RuntimeException

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

11.5.1.1 Стандартные исключения времени выполнения

Класс RuntimeException - подкласс класса Exception. Подклассы класса RuntimeException - классы непроверяемых исключений.

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

  • ArithmeticException: возникла исключительная арифметическая ситуация, такая как операция целочисленного деления (§ 15.16.2) с нулевым знаменателем.
  • ArrayStoreException: была сделана попытка сохранить в массиве компонент тип значения которого не совместим с типом компонент массива(§ 10.10, § 15.25.1).
  • ClassCastException: была сделана попытка привести (§ 5.4, § 15.15) ссылку на объект несоответствующего типа.
  • IllegalArgumentException: метод передал недействительный или несоответствующий аргумент или вызывает несоответствующий объект. Подклассами этого класса являются:

 

o    IllegalThreadStateException: поток не был в соответствующем режиме для требуемого действия.

o    NumberFormatException: была сделана попытка конвертировать строку в числовое выражение, но строка не имела соответствующего формата.

 

  • IllegalMonitorStateException: поток попытался работать с другими потоками   (§ 20.1.6, § 20.1.7, §20.1.8) или давать сведения (§ 20.1.9, § 20.1.10) другим нитям, имея дело с объектами, которые она не блокировала.
  • IndexOutOfBoundsException: индекс ( массива, строки или вектора) или поддиапазон,заданный с помощью двух значений индекса или индексом и длиной, были вне диапазона.
  • NegativeArraySizeException: была сделана попытка, создать массив с отрицательной длиной (§ 15.9).
  • NullPointerException: была сделана попытка использовать пустую ссылку в случае, где требовалась ссылка на объект.
  • SecurityException: было обнаружено нарушение безопасности (§ 20.17).

Пакет java.util определяет следующие дополнительные стандартные непроверяемые исключения времени выполнения:

  • java.util.EmptyStackException: была сделана попытка доступа к элементу пустого стека.
  • java.util.NoSuchElementException: была сделана попытка доступа к элементу пустого вектора.

 

11.5.1.2 Стандартные проверяемые исключения

Все стандартные подклассы класса Exception, за исключением класса RuntimeException, являются классами проверяемых исключений.

Пакет java.lang определяет следующие стандартные исключения, которые, как все другие классы пакетаjava.lang, неявно импортированы, и поэтому на них можно ссылаться с помощью их простых имен:

  • ClassNotFoundException: класс или интерфейс с указанным именем не был найден (§ 20.3.8).
  • CloneNotSupportedException: для копирования объекта, был вызван метод сlone(§ 20.1.5) классаObject, но класс этого объекта не реализует интерфейс Cloneable.
  • IllegalAccessException: была сделана попытка загрузить класс с помощью строки, дающей его полное квалифицированное имя, но в настоящее время метод не имеет доступа к определению указанного класса, потому что класс не является классом с типом доступа public и находится в другом пакете.
  • InstantiationException: была сделана попытка создать экземпляр класса, используя метод newInstance класса Class, но создание экземпляра названного объекта класса невозможно, потому что это - интерфейс, класс типа abstract , или массив.
  • InterruptedException: текущий поток ожидал, а другой поток прервал текущий поток, используя методinterrupt класса Thread (§ 20.20.31).

Пакет java.io определяет следующие дополнительные стандартные исключения:

  • java.io.IOException: требуемая операция ввода - вывода не могла закончиться нормально. Подклассами этого класса являются:

o    java.io.EOFException: Был достигнут конец файла до нормального завершения операции ввода.

o    java.io.FileNotFoundException: файл с именем, указанным строкой или путем не был найден в пределах файловой системы.

o    java.io.InterruptedIOException: текущий поток ожидал завершения опреации ввода - вывода, а другой поток прервал текущий поток, используя метод interrupt класса Thread(§ 20.20.31).

o    java.io.UTFDataFormatException: требуемое преобразование строки к Ява-модифицированному UTF-8-формату или из него не могло быть закончено (§ 22.1.15, § 22.2.14), потому что строка была слишком длинной или подразумеваемые UTF-8-данные не были результатом кодирования Unicode-строки в UTF-8-формат.

Стандартный пакет java.net определяет следующие дополнительные подклассы java.io.IOException: иjava.net.MalformedURLException: Строка, которая предусматривалась как URL, или как часть URL, имела несоответствующий формат или определяла неизвестный протокол.

o    Java.net.ProtocolException: какой-то аспект протокола сети выполнялся неправильно.

o    Java.net.SocketException: действие,использующее сокет, не могло быть закончено нормально.

o    Java.net.UnknownHostException:не найден сетевой адрес.

o    Java.net.UnknownServiceException:сетевое соединение не может поддерживать требуемый сервис.

11.5.2 Класс Error

Класс Error и его стандартные подклассы - это исключения, при возникновении которых обычные программы, как ожидается, восстановятся. Класс Error является отдельным подклассом Throwable, отличным от классаException в иерархии классов, он позволяет программам использоватьследующую запись:

 

} catch (Exception e) {

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

Пакет java.lang определяет все классы ошибок, описанные здесь. Эти классы, как и все друге классы пакетаjava.lang, неявно импортированы и поэтому на них можно ссылаться с помощью их простых имен.

11.5.2.1 Ошибки загрузки и компоновки

Виртуальная машина языка Ява генерирует объект, который является экземпляром подкласса LinkageError, когда происходит ошибка загрузки, компоновки, подготовки, контроля или инициализации:

  • Процесс загрузки описан в § 12.2. Ошибки ClassFormatError, ClassCircularityError, иNoClassDefFoundError описаны в указанном параграфе.
  • Процесс компоновки описан в § 12.3. Ошибки компоновки описаны в указанном параграфе. Эти ошибки включают IllegalAccessError, InstantiationError, NoSuchFieldError, и NoSuchMethodError, все они являются подклассами класса IncompatibleClassChangeError, а также классаUnsatisfiedLinkError.
  • Процесс контроля классов описан в § 12.3.1. Ошибка неудачи конторля VerifyError описана в этом параграфе.
  • Процесс подготовки класса описан в § 12.3.2. Ошибка подготовки, описанная в этом параграфе -AbstractMethodError.
  • Процесс инициализации класса описан в § 12.4. Виртуальная машина языка Ява выдаст ошибкуExceptionInInitializerError, если выполнение статического инициализатора или инициализатора для статического поля даст результат в виде исключения, которое не является классом Error или подклассомError.

11.5.2.2 Ошибки виртуальной машины

Виртуальная машина языка Ява генерирует объект, который является экземпляром подкласса классаVirtualMachineError, когда внутренняя ошибка или ограничение ресурсов не допускает реализации семантики языка Ява. Эта спецификация языка и виртуальной машины языка Ява определяет следующие ошибки виртуальной машины:

  • InternalError: в виртуальной машине языка Ява произошла внутренняя ошибка, из-за ошибки в программном обеспечении, реализующем виртуальную машину, в основном программном обеспечении операционной системы, или ошибки в аппаратных средствах ЭВМ. Эта ошибка выдается асинхронно, когда она обнаруживается, и может происходить в любой части Ява-программы.
  • OutOfMemoryError: виртуальная машина языка Ява использовала всю виртуальную или физическую память, и устройство автоматического хранения было неспособно предоставить столько памяти, чтобы удовлетворить запрос на создание объекта.
  • StackOverflowError: виртуальная машина языка Ява использовала весь стек для потока, обычно это случается в результате того, что поток делает неограниченное число рекурсивных вызовов из-за ошибки в написании программы.
  • UnknownError: исключение или ошибка произошла, но по каким-то причинам, виртуальная машина языка Ява неспособна сообщить о фактической причине исключения или ошибки.

Может быть разработана сложная Ява-программа, чтобы создать ошибочную ситуацию OutOfMemoryError и попытаться исправить ее, возможно, осторожно удаляя ссылки на объекты.

Мы исследуем усовершенствования Ява-системы для упрощения обработки ситуаций нехватки памяти. Одна возможность состоит в том, что можно было бы автоматически приостановить поток, который столкнулся с ошибкойOutOfMemoryError и позволить обратиться к другому потоку для обработки ошибочной ситуации. Такая техника могла бы также позволить Ява-программе исправить ошибку StackOverflowError, если это переполнение не является результатом не завершения рекурсии. Приветствуются предложения для других подходов.
Comments