Исключение — это нештатная ситуация, ошибка во время выполнения программы. Самый простой пример — деление на ноль. Можно вручную отслеживать возникновение подобных ошибок, а можно воспользоваться специальным механизмом исключений, который упрощает создание больших надёжных программ, уменьшает объём необходимого кода и повышает уверенность в том, что в приложении не будет необработанной ошибки.
В методе, в котором происходит ошибка, создаётся и передаётся специальный объект. Метод может либо обработать исключение самостоятельно, либо пропустить его. В любом случае исключение ловится и обрабатывается. Исключение может появиться благодаря самой системе, либо вы сами можете создать его вручную. Системные исключения возникают при неправильном использовании языка 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:
Подобная конструкция не упустит ни одного исключения, поэтому её следует размещать в самом конце списка обработчиков, во избежание блокировки следующих за ней обработчиков исключений.
Основные правила обработки исключений
Используйте исключения для того, чтобы:
- обработать ошибку на текущем уровне (избегайте перехватывать исключения, если не знаете, как с ними поступить)
- исправить проблему и снова вызвать метод, возбудивший исключение
- предпринять все необходимые действия и продолжить выполнение без повторного вызова действия
- попытаться найти альтернативный результат вместо того, который должен был бы произвести вызванный метод
- сделать все возможное в текущем контексте и заново возбудить это же исключение, перенаправив его на более высокий уровень
- сделать все, что можно в текущем контексте, и возбудить новое исключение, перенаправив его на более высокий уровень
- завершить работу программы
- упростить программу (если используемая схема обработки исключений делает все только сложнее, значит, она никуда не годится)
- добавить вашей библиотеке и программе безопасности
категория Java | |
дата | 04.05.2014 |
автор | vovanok |
голосов | 84 |
Исключение — это проблема(ошибка) возникающая во время выполнения программы. Исключения могут возникать во многих случаях, например:
- Пользователь ввел некорректные данные.
- Файл, к которому обращается программа, не найден.
- Сетевое соединение с сервером было утеряно во время передачи данных.
И т.п.
Обработка исключительных ситуаций (exception handling) — механизм языков программирования, предназначенный для описания реакции программы на ошибки времени выполнения и другие возможные проблемы (исключения), которые могут возникнуть при выполнении программы и приводят к невозможности (бессмысленности) дальнейшей отработки программой её базового алгоритма.
Синтаксис
- try — данное ключевое слово используется для отметки начала блока кода, который потенциально может привести к ошибке.
- catch — ключевое слово для отметки начала блока кода, предназначенного для перехвата и обработки исключений.
- finally — ключевое слово для отметки начала блока кода, которое является дополнительным. Этот блок помещается после последнего блока ‘catch’. Управление обычно передаётся в блок ‘finally’ в любом случае.
- throw — служит для генерации исключений.
- throws — ключевое слово, которое прописывается в сигнатуре метода, и обозначающее что метод потенциально может выбросить исключение с указанным типом.
Здесь в методе getAreaValue мы бросаем исключение IllegalArgumentException с помощью ключевого слова throw. В данном случае в сигнатуре метода отсутствует throws IllegalArgumentException, это не сделано потому что исключение IllegalArgumentException является не проверяемым, о них мы ещё поговорим.
Общий вид конструкции для "поимки" исключительной ситуации выглядит следующим образом:
В нашем случае для площади прямоугольника:
Здесь мы поймали IllegalArgumentException и залогировали данное событие. Дело в том что "починить" такую поломку мы не можем, не будем же мы угадывать что хотел пользователь :). По этому мы пробрасываем данное исключение дальше с помощью "throw e;". Такое часто можно встретить на серверах приложений(веб-серверах).
finally
Иногда требуется гарантировать, что определенный участок кода будет выполняться независимо от того, какие исключения были возбуждены и перехвачены. Для создания такого участка кода используется ключевое слово finally. Даже в тех случаях, когда в методе нет соответствующего возбужденному исключению раздела catch, блок finally будет выполнен до того, как управление перейдет к операторам, следующим за разделом try. У каждого раздела try должен быть по крайней мере или один раздел catch или блок finally. Блок finally очень удобен для закрытия файлов и освобождения любых других ресурсов, захваченных для временного использования в начале выполнения метода. Ниже приведен пример класса с двумя методами, завершение которых происходит по разным причинам, но в обоих перед выходом выполняется код раздела finally.
В этом примере в методе procA из-за возбуждения исключения происходит преждевременный выход из блока try, но по пути «наружу» выполняется раздел finally. Другой метод procB завершает работу выполнением стоящего в try-блоке оператора return, но и при этом перед выходом из метода выполняется программный код блока finally. Ниже приведен результат, полученный при выполнении этой программы.
Иерархия исключений
Все классы обрабатывающие ошибки являются наследниками класса java.lang.Throwable. Только объекты этого класса или его наследников могут быть "брошены" JVM при возникновении какой-нибудь исключительной ситуации, а также только эти объекты могут быть "брошены" во время выполнения программы с помощью ключевого слова throw.
Прямыми наследниками класса Throwable являются Error и Exception.
Error — это подкласс, который показывает серьезные проблемы возникающие во время выполнения приложения. Большинство из этих ошибок сигнализируют о ненормальном ходе выполнения программы, т.е. о каких-то критических проблемах. Эти ошибки не рекомендуется отмечать в методах посредством throws-объявления, поэтому они также очень часто называются не проверяемые (unchecked).
Источник
При программировании на Java основное внимание следует уделять иерархии Exception. Эта иерархия также разделяется на две ветви: исключения, производные от класса RuntimeException, и остальные. Исключения типа RuntimeException возникают вследствие ошибок программирования. Все другие исключения являются следствием непредвиденного стечения обстоятельств, например, ошибок ввода-вывода, возникающих при выполнении вполне корректных программ.
Рассмотрим основные классы исключений.
IndexOutOfBoundsException — выбрасывается, когда индекс некоторого элемента в структуре данных(массив/коллекция) не попадает в диапазон имеющихся индексов.
Создание своих классов исключений
Хотя встроенные исключения Java обрабатывают большинство частых ошибок, вероятно, вам потребуется создать ваши собственные типы исключений для обработки ситуаций, специфичных для ваших приложений. Это достаточно просто сделать: просто определите подкласс Exception (который, разумеется, является подклассом Throwable). Ваши подклассы не обязаны реализовывать что-либо — важно само их присутствие в системе типов, которое позволит использовать их как исключения.
Обработка нескольких исключений
Одному блоку try может соответствовать сразу несколько блоков catch с разными классами исключений.
Это удобно, если обработка ошибок не отличается.
Конструкция try-with-resources
Наследование методов бросающих исключения
Можно лишь сужать класс исключения:
Как бросить проверяемое исключение не обрабатывая его (хак)
Нет ничего невозможного. С помощью рефлексии и внутреннего API языка java можно творить магию :).
Естественно так писать нельзя , но знать как это делается все же интересно.
sun.misc.Unsafe — API позволяющее выполнять с классами, методами и полями действия, недопустимые стандартными средствами языка. Идея заключается в получении системного объекта Unsafe.
В примере используется рефлексия для получения объекта Unsafe так как другими средствами это сделать проблематично. У класса Unsafe приватный конструктор. А если попытаться вызвать статический метод getUnsafe() то будет брошено исключение SecurityException.
Заключение
Изначально я хотел сделать себе шпаргалку по иерархии классов исключений и не планировал писать статью. Но получилось так, что шпаргалка выросла в статью 🙂
Надеюсь она поможет кому-нибудь перед собеседованием, или просто вспомнить/углубить знания 🙂 Спасибо за внимание!
Если Вам понравилась статья, проголосуйте за нее
Голосов: 84 Голосовать
рассмотрим следующую простую программу. Программа имеет два файла:
Как вы можете видеть в первом классе, я добавил комментарий ("// throw exception"), где я хотел бы бросить исключение. Я должен определить свой собственный класс исключений или есть какой-то общий класс исключений в Java я могу использовать?
8 ответов
Вы можете создать свой собственный класс исключений:
вы можете использовать IllegalArgumentException:
Ну есть много исключений, чтобы бросить, но вот как вы бросаете исключение:
также Да, вы можете создавать свои собственные исключения.
Edit: также обратите внимание на исключения. Когда вы бросаете исключение (как указано выше), и вы ловите исключение: String что вы предоставляете в исключении, можно получить доступ к броску getMessage() метод.
это действительно зависит от того, что вы хотите сделать с этим исключением после того, как поймаете его. Если вам нужно дифференцировать свое исключение, вам нужно создать свой пользовательский Exception . В противном случае вы могли бы просто throw new Exception("message goes here");
самый простой способ сделать это было бы что-то вроде:
однако следующие строки будут недоступны в вашем коде. Итак, у нас есть два пути:
- throw generic exception в нижней части метода.
- бросьте пользовательское исключение, если вы не хотите делать 1.
Java имеет большое количество встроенных исключений для различных сценариев.
в этом случае, вы должны выбросить IllegalArgumentException , так как проблема в том, что вызывающий передал плохой параметр.
вы можете определить свой собственный класс исключений, расширяющий java.ленг.Исключение (это для проверенного исключения-те, которые должны быть пойманы) или расширение java.ленг.RuntimeException — эти исключения не должны быть пойманы. Другое решение-просмотреть Java API и найти соответствующее исключение, описывающее вашу ситуацию: в этом конкретном случае я думаю, что лучшим будет IllegalArgumentException .
Это зависит от того, вы можете создать более общее исключение или более конкретное исключение. Для более простых методов достаточно более общих исключений. Если метод является сложным, то создание более конкретного исключения будет надежным.