- Исключения, try . catch
- Ключевые слова исключений try, catch, throw, throws, finally
- Типы исключений
- Неперехваченные исключения
- Перехват исключений try/catch
- Несколько разделов catch
- Вложенные операторы try
- Возбуждение исключений throw
- Объявление об исключении throws
- Ключевое слово finally
- Наследование исключений
- Множественные исключения
- Исключения
- Несколько исключений
- Вложенные операторы try
- Оператор throw
- Оператор throws
- Оператор finally
- Встроенные исключения Java
- Создание собственных классов исключений
- Перехват произвольных исключений
- Основные правила обработки исключений
Исключения, try . catch
В данной статье рассматривается используемый в Java механизм обработки исключений. Исключение в Java — это объект, который описывает исключительное состояние, возникшее в каком-либо участке программного кода. Когда возникает исключительное состояние, создается объект класса Exception. Этот объект пересылается в метод, обрабатывающий данный тип исключительной ситуации. Исключения могут возбуждаться и для того, чтобы сообщить о некоторых нештатных ситуациях.
Ключевые слова исключений try, catch, throw, throws, finally
Механизм исключительных ситуаций в Java поддерживается пятью ключевыми словами:
Ниже приведена общая форма блока обработки исключений.
Типы исключений
В вершине иерархии исключений стоит класс Throwable, который наследуется от Object. Каждый из типов исключений является подклассом Throwable. Два непосредственных наследника класса Throwable делят иерархию подклассов исключений на две различные ветви. Иерархия классов представлена на рисунке.
Класс Ехception используется для описания исключительных ситуации, которые должны перехватываться программным кодом пользователя. Класс Error предназначен для описания исключительных ситуаций, которые при обычных условиях не должны перехватываться в пользовательской программе.
Неперехваченные исключения
Объекты-исключения автоматически создаются исполняющей средой Java в результате возникновения определенных исключительных ситуаций. Пример программы, в которой создаем исключительную ситуацию при делении на нуль.
В консоль будет выведено следующее сообщение.
Следует обратить внимание на тот факт, что типом возбужденного исключения был не Exception и не Throwable. Это подкласс класса Exception, а именно: ArithmeticException, поясняющий, какая ошибка возникла при выполнении программы.
Изменим класс добавлением статического метода subroutine, в котором создадим такую же исключительную ситуацию.
Сообщение выполнения программы показывает, как обработчик исключений исполняющей системы Java выводит содержимое всего стека вызовов.
Перехват исключений try/catch
Для защиты программного кода от исключений необходимо использовать связанные блоки с ключевыми словами try catch; catch помещается сразу же после try-блока. В блоке catch задается тип исключения, которое необходимо обработать.
Целью большинства хорошо сконструированных catch-разделов должна быть обработка возникшей исключительной ситуации и приведение переменных программы в некоторое разумное состояние — такое, чтобы программу можно было продолжить так, будто никакой ошибки и не было (в нашем примере выводится предупреждение — division by zero).
Несколько разделов catch
В отдельных случаях блок программного кода может вызвать исключения различных типов. Для того, чтобы локализовать обработку подобных ситуаций, можно использовать несколько catch-разделов для одного try-блока. Блоки наиболее специализированных классов исключений должны идти первыми, поскольку ни один подкласс не будет достигнут, если поставить его после суперкласса.
В следующем примере перехватывается два различных типа исключений, причем за этими двумя специализированными обработчиками следует раздел catch общего назначения, перехватывающий все подклассы класса Throwable.
Данный пример, запущенный без параметров, вызывает возбуждение исключительной ситуации деления на нуль. Если в командной строке будет определен один или несколько параметров, тем самым установив ‘а’ в значение больше нуля, то будет возбуждено исключение выхода индекса за границы массива ArrayIndexOutOfBounds. Ниже приведены результаты работы этой программы, запущенной и тем и другим способом.
Вложенные операторы try
Операторы try можно вкладывать друг в друга. Если у оператора try низкого уровня нет раздела catch, соответствующего возбужденному исключению, стек будет развернут на одну ступень выше, и в поисках подходящего обработчика будут проверены разделы catch внешнего оператора try. Пример вложения двух операторов try catch друг в друга посредством вызова метода.
Возбуждение исключений throw
Программа может явно вызывать исключение, используя оператор throw. После выполнения оператора throw процесс выполнения программы приостанавливается и последующие операторы не выполняются. JVM просматривает ближайший блоки try . catch, соответствующий типу исключения, для «передачи управления». Если подходящий блок не будет найден, то обработчик исключений остановит программу и «распечатает» при этом состояние стека вызовов.
Пример исключения, в котором сначала создается объект-исключение, затем оператор throw возбуждает исключительную ситуацию, после чего то же исключение возбуждается повторно — на этот раз уже кодом перехватившего его в первый раз раздела catch.
Результат выполнения программы приведен ниже.
Объявление об исключении throws
Если метод может возбуждать исключения, которые сам не обрабатывает, то он должен объявить об этом, чтобы вызывающие его другие методы могли защитить себя от этих исключений. Для задания списка исключений, которые могут возбуждаться методом, используется ключевое слово throws.
Если метод в явном виде (т.е. с помощью оператора throw) возбуждает исключение, тип класса исключений должен быть указан в операторе throws в объявлении этого метода. Принимая данное положение во внимание синтаксис определения метода должен быть описан следующим образом:
Результат работы примера:
Ключевое слово finally
В случае, когда необходимо гарантировано выполнить определенный участок кода необходимо использовать ключевое слово finally. Использование связи try. finally позволяет обеспечить выполнение кода независимо от того, какие исключения были возбуждены и перехвачены, даже в тех случаях, когда в методе нет соответствующего возбужденному исключению раздела catch.
У каждого раздела try должен быть по крайней мере или один раздел catch или блок finally. Блок finally очень удобен для закрытия файлов и освобождения любых других ресурсов, захваченных для временного использования в начале выполнения метода.
Ниже приведен пример класса с двумя методами, завершение которых происходит по разным причинам, но в обоих перед выходом выполняется код раздела finally.
В тестовом примере в методе methodA возбуждается исключение. Но перед преждевременным выходом из блока try, выполняется раздел finally. Во втором методе methodB завершается работа в try-блоке оператором return, но и при этом перед выходом из метода выполняется программный код блока finally. Результат работы тестового примера:
Обработка исключений в Java предоставляет исключительно мощный механизм для управления сложными программами. Ключевые слова try, throw, catch позволяют выполнять обработку ошибок и разных нештатных ситуаций в программе.
Наследование исключений
catch — полиморфная конструкция, т.е. catch по типу parent перехватывает исключения любого типа, которые является Parent’ом.
В результате в консоли увидим
Error и Exception из параллельных веток наследования от Throwable, поэтому catch по одному «брату» не может поймать другого «брата».
Результат выполения программы
Множественные исключения
Объявление исключений в методе может быть множественным. Пример :
Источник
Исключения
Исключение — это нештатная ситуация, ошибка во время выполнения программы. Самый простой пример — деление на ноль. Можно вручную отслеживать возникновение подобных ошибок, а можно воспользоваться специальным механизмом исключений, который упрощает создание больших надёжных программ, уменьшает объём необходимого кода и повышает уверенность в том, что в приложении не будет необработанной ошибки.
В методе, в котором происходит ошибка, создаётся и передаётся специальный объект. Метод может либо обработать исключение самостоятельно, либо пропустить его. В любом случае исключение ловится и обрабатывается. Исключение может появиться благодаря самой системе, либо вы сами можете создать его вручную. Системные исключения возникают при неправильном использовании языка Java или запрещённых приёмов доступа к системе. Ваши собственные исключения обрабатывают специфические ошибки вашей программы.
Вернёмся к примеру с делением. Деление на нуль может предотвратить проверкой соответствующего условия. Но что делать, если знаменатель оказался нулём? Возможно, в контексте вашей задачи известно, как следует поступить в такой ситуации. Но, если нулевой знаменатель возник неожиданно, деление в принципе невозможно, и тогда необходимо возбудить исключение, а не продолжать исполнение программы.
Существует пять ключевых слов, используемых в исключениях: try, catch, throw, throws, finally. Порядок обработки исключений следующий.
Операторы программы, которые вы хотите отслеживать, помещаются в блок try. Если исключение произошло, то оно создаётся и передаётся дальше. Ваш код может перехватить исключение при помощи блока catch и обработать его. Системные исключения автоматически передаются самой системой. Чтобы передать исключение вручную, используется throw. Любое исключение, созданное и передаваемое внутри метода, должно быть указано в его интерфейсе ключевым словом throws. Любой код, который следует выполнить обязательно после завершения блока try, помещается в блок finally
Схематически код выглядит так:
Существует специальный класс для исключений Trowable. В него входят два класса Exception и Error.
Класс Exception используется для обработки исключений вашей программой. Вы можете наследоваться от него для создания собственных типов исключений. Для распространённых ошибок уже существует класс RuntimeException, который может обрабатывать деление на ноль или определять ошибочную индексацию массива.
Класс Error служит для обработки ошибок в самом языке Java и на практике вам не придётся иметь с ним дело.
Прежде чем научиться обрабатывать исключения, нам (как и нормальному любопытному коту) хочется посмотреть, а что происходит, если ошибку не обработать. Давайте разделим число котов в вашей квартире на ноль, хотя мы и знаем, что котов на ноль делить нельзя!
Я поместил код в обработчик щелчка кнопки. Когда система времени выполнения Java обнаруживает попытку деления на ноль, она создаёт объект исключения и передаёт его. Да вот незадача, никто не перехватывает его, хотя это должны были сделать вы. Видя вашу бездеятельность, объект перехватывает стандартный системный обработчик Java, который отличается вредных характером. Он останавливает вашу программу и выводит сообщение об ошибке, которое можно увидеть в журнале LogCat:
Как видно, созданный объект исключения принадлежит к классу ArithmeticException, далее системный обработчик любезно вывел краткое описание ошибки и место возникновения.
Вряд ли пользователи вашей программы будут довольны, если вы так и оставите обработку ошибки системе. Если программа будет завершаться с такой ошибкой, то скорее всего вашу программу просто удалят. Посмотрим, как мы можем исправить ситуацию.
Поместим проблемный код в блок try, а в блоке catch обработаем исключение.
Теперь программа аварийно не закрывается, так как мы обрабатываем ситуацию с делением на ноль.
В данном случае мы уже знали, к какому классу принадлежит получаемая ошибка, поэтому в блоке catch сразу указали конкретный тип. Обратите внимание, что последний оператор в блоке try не срабатывает, так как ошибка происходит раньше строчкой выше. Далее выполнение передаётся в блок catch, далее выполняются следующие операторы в обычном порядке.
Операторы try и catch работают совместно в паре. Хотя возможны ситуации, когда catch может обрабатывать несколько вложенных операторов try.
Если вы хотите увидеть описание ошибки, то параметр e и поможет увидеть ёго.
По умолчанию, класс Trowable, к которому относится ArithmeticException возвращает строку, содержащую описание исключения. Но вы можете и явно указать метод e.toString.
Несколько исключений
Фрагмент кода может содержать несколько проблемных мест. Например, кроме деления на ноль, возможна ошибка индексации массива. В таком случае вам нужно создать два или более операторов catch для каждого типа исключения. Причём они проверяются по порядку. Если исключение будет обнаружено у первого блока обработки, то он будет выполнен, а остальные проверки пропускаются и выполнение программы продолжается с места, который следует за блоком try/catch.
В примере мы добавили массив с тремя элементами, но обращаемся к четвёртому элементу, так как забыли, что отсчёт у массива начинается с нуля. Если оставить значение переменной zero равным нулю, то сработает обработка первого исключения деления на ноль, и мы даже не узнаем о существовании второй ошибки. Но допустим, что в результате каких-то вычислений значение переменной стало равно единице. Тогда наше исключение ArithmeticException не сработает. Но сработает новое добавленное исключение ArrayIndexOutOfBoundsException. А дальше всё пойдёт как раньше.
Тут всегда нужно помнить одну особенность. При использовании множественных операторов catch обработчики подклассов исключений должные находиться выше, чем обработчики их суперклассов. Иначе, суперкласс будет перехватывать все исключения, имея большую область перехвата. Иными словами, Exception не должен находиться выше ArithmeticException и ArrayIndexOutOfBoundsException. К счастью, среда разработки сама замечает непорядок и предупреждает вас, что такой порядок не годится. Увидев такую ошибку, попробуйте перенести блок обработки исключений ниже.
Вложенные операторы try
Операторы try могут быть вложенными. Если вложенный оператор try не имеет своего обработчика catch для определения исключения, то идёт поиск обработчика catch у внешнего блока try и т.д. Если подходящий catch не будет найден, то исключение обработает сама система (что никуда не годится).
Оператор throw
Часть исключений может обрабатывать сама система. Но можно создать собственные исключения при помощи оператора throw. Код выглядит так:
Вам нужно создать экземпляр класса Throwable или его наследников. Получить объект класса Throwable можно в операторе catch или стандартным способом через оператор new.
Мы могли бы написать такой код для кнопки:
Мы объявили объект класса Cat, но забыли его проинициализировать, например, в onCreate(). Теперь нажатие кнопки вызовет исключение, которое обработает система, а в логах мы можем прочитать сообщение об ошибке. Возможно, вы захотите использовать другое исключение, например, throw new UnsupportedOperationException(«Котик не инициализирован»);.
В любом случае мы передали обработку ошибки системе. В реальном приложении вам нужно обработать ошибку самостоятельно.
Поток выполнения останавливается непосредственно после оператора throw и другие операторы не выполняются. При этом ищется ближайший блок try/catch соответствующего исключению типа.
Перепишем пример с обработкой ошибки.
Мы создали новый объект класса NullPointerException. Многие классы исключений кроме стандартного конструктора по умолчанию с пустыми скобками имеют второй конструктор с строковым параметром, в котором можно разместить подходящую информацию об исключении. Получить текст из него можно через метод getMessage(), что мы и сделали в блоке catch.
Теперь программа не закроется аварийно, а будет просто выводить сообщения в всплывающих Toast.
Оператор throws
Если метод может породить исключение, которое он сам не обрабатывает, он должен задать это поведение так, чтобы вызывающий его код мог позаботиться об этом исключении. Для этого к объявлению метода добавляется конструкция throws, которая перечисляет типы исключений (кроме исключений Error и RuntimeException и их подклассов).
Общая форма объявления метода с оператором throws:
В фрагменте список_исключений можно указать список исключений через запятую.
Создадим метод, который может породить исключение, но не обрабатывает его. А в щелчке кнопки вызовем его.
Если вы запустите пример, то получите ошибку. Исправим код.
Мы поместили вызов метода в блок try и вызвали блок catch с нужным типом исключения. Теперь ошибки не будет.
Оператор finally
Когда исключение передано, выполнение метода направляется по нелинейному пути. Это может стать источником проблем. Например, при входе метод открывает файл и закрывает при выходе. Чтобы закрытие файла не было пропущено из-за обработки исключения, был предложен механизм finally.
Ключевое слово finally создаёт блок кода, который будет выполнен после завершения блока try/catch, но перед кодом, следующим за ним. Блок будет выполнен, независимо от того, передано исключение или нет. Оператор finally не обязателен, однако каждый оператор try требует наличия либо catch, либо finally.
Встроенные исключения Java
Существуют несколько готовых системных исключений. Большинство из них являются подклассами типа RuntimeException и их не нужно включать в список throws. Вот небольшой список непроверяемых исключений.
- ArithmeticException — арифметическая ошибка, например, деление на нуль
- ArrayIndexOutOfBoundsException — выход индекса за границу массива
- ArrayStoreException — присваивание элементу массива объекта несовместимого типа
- ClassCastException — неверное приведение
- EnumConstantNotPresentException — попытка использования неопределённого значения перечисления
- IllegalArgumentException — неверный аргумент при вызове метода
- IllegalMonitorStateException — неверная операция мониторинга
- IllegalStateException — некорректное состояние приложения
- IllegalThreadStateException — запрашиваемая операция несовместима с текущим потоком
- IndexOutofBoundsException — тип индекса вышел за допустимые пределы
- NegativeArraySizeException — создан массив отрицательного размера
- NullPointerException — неверное использование пустой ссылки
- NumberFormatException — неверное преобразование строки в числовой формат
- SecurityException — попытка нарушения безопасности
- StringIndexOutOfBounds — попытка использования индекса за пределами строки
- TypeNotPresentException — тип не найден
- UnsupportedOperationException — обнаружена неподдерживаемая операция
Список проверяемых системных исключений, которые можно включать в список throws.
- ClassNotFoundException — класс не найден
- CloneNotSupportedException — попытка клонировать объект, который не реализует интерфейс Cloneable
- IllegalAccessException — запрещен доступ к классу
- InstantiationException — попытка создать объект абстрактного класса или интерфейса
- InterruptedException — поток прерван другим потоком
- NoSuchFieldException — запрашиваемое поле не существует
- NoSuchMethodException — запрашиваемый метод не существует
- ReflectiveOperationException — исключение, связанное с рефлексией
Создание собственных классов исключений
Система не может предусмотреть все исключения, иногда вам придётся создать собственный тип исключения для вашего приложения. Вам нужно наследоваться от Exception (напомню, что этот класс наследуется от Trowable) и переопределить нужные методы класса Throwable. Либо вы можете наследоваться от уже существующего типа, который наиболее близок по логике с вашим исключением.
- final void addSuppressed(Throwable exception) — добавляет исключение в список подавляемых исключений (JDK 7)
- Throwable fillInStackTrace() — возвращает объект класса Throwable, содержащий полную трассировку стека.
- Throwable getCause() — возвращает исключение, лежащее под текущим исключение или null
- String getLocalizedMessage() — возвращает локализованное описание исключения
- String getMessage() — возвращает описание исключения
- StackTraceElement[] getStackTrace() — возвращает массив, содержащий трассировку стека и состояний из элементов класса StackTraceElement
- final Throwable[] getSuppressed() — получает подавленные исключения (JDK 7)
- Throwable initCause(Throwable exception) — ассоциирует исключение с вызывающим исключением. Возвращает ссылку на исключение.
- void printStackTrace() — отображает трассировку стека
- void printStackTrace(PrintStream stream) — посылает трассировку стека в заданный поток
- void printStackTrace(PrintWriter stream) — посылает трассировку стека в заданный поток
- void setStackTrace(StackTraceElement elements[]) — устанавливает трассировку стека для элементов (для специализированных приложений)
- String toString() — возвращает объект класса String, содержащий описание исключения.
Самый простой способ — создать класс с конструктором по умолчанию.
Мы создали собственный класс HungryCatException, в методе testMethod() его возбуждаем, а по нажатию кнопки вызываем этот метод. В результате наше исключение сработает.
Создать класс исключения с конструктором, который получает аргумент-строку, также просто.
Ещё вариант. Добавим также метод toString().
Теперь класс содержит два конструктора. Во втором конструкторе используется конструктор родительского класса с аргументом String, вызываемый ключевым словом super.
Перехват произвольных исключений
Можно создать универсальный обработчик, перехватывающий любые типы исключения. Осуществляется это перехватом базового класса всех исключений Exception:
Подобная конструкция не упустит ни одного исключения, поэтому её следует размещать в самом конце списка обработчиков, во избежание блокировки следующих за ней обработчиков исключений.
Основные правила обработки исключений
Используйте исключения для того, чтобы:
- обработать ошибку на текущем уровне (избегайте перехватывать исключения, если не знаете, как с ними поступить)
- исправить проблему и снова вызвать метод, возбудивший исключение
- предпринять все необходимые действия и продолжить выполнение без повторного вызова действия
- попытаться найти альтернативный результат вместо того, который должен был бы произвести вызванный метод
- сделать все возможное в текущем контексте и заново возбудить это же исключение, перенаправив его на более высокий уровень
- сделать все, что можно в текущем контексте, и возбудить новое исключение, перенаправив его на более высокий уровень
- завершить работу программы
- упростить программу (если используемая схема обработки исключений делает все только сложнее, значит, она никуда не годится)
- добавить вашей библиотеке и программе безопасности
Источник