Вчера всё работало, а сегодня не работает / Код не работает как задумано
Before you can catch an exception, some code somewhere must throw one. Any code can throw an exception: your code, code from a package written by someone else such as the packages that come with the Java platform, or the Java runtime environment. Regardless of what throws the exception, it’s always thrown with the throw statement.
As you have probably noticed, the Java platform provides numerous exception classes. All the classes are descendants of the
Throwable class, and all allow programs to differentiate among the various types of exceptions that can occur during the execution of a program.
Защита от дурака — стандартная конструкция для любого приложения. Рассмотрим, как организовать её в Java.
Одно из стандартных требований ТЗ на разработку ПО – отсутствие ошибок и конфликтов, препятствующих нормальной работе. Путей реализации два – ограничить функциональность и возможности пользователя или создать код, который будет учитывать возможные неприятности.
В java исключением называется любая ошибка, которая возникает в ходе выполнения программы. Это может быть несоответствие типов данных, деление на ноль, обрыв связи с сервером и многое другое. Операции по их поиску и предотвращению называются обработкой исключений.
All Implemented Interfaces:
Direct Known Subclasses:
AnnotationFormatError, AssertionError, AWTError, CoderMalfunctionError, FactoryConfigurationError, FactoryConfigurationError, IOError, LinkageError, SchemaFactoryConfigurationError, ServiceConfigurationError, ThreadDeath, TransformerFactoryConfigurationError, VirtualMachineError
An Error is a subclass of Throwable
that indicates serious problems that a reasonable application
should not try to catch. Most such errors are abnormal conditions.
The ThreadDeath error, though a «normal» condition,
is also a subclass of Error because most applications
should not try to catch it.
A method is not required to declare in its throws
clause any subclasses of Error that might be thrown
during the execution of the method but not caught, since these
errors are abnormal conditions that should never occur.
That is, Error and its subclasses are regarded as unchecked
exceptions for the purposes of compile-time checking of exceptions.
See Java Language Specification:
11.2 Compile-Time Checking of Exceptions
Основные вопросы об исключениях в Java
1.Что такое проверяемые и непроверяемые исключения?
Если говорить коротко, то первые должны быть явно пойманы в теле метода либо объявлены в секции throws метода. Вторые вызываются проблемами, которые не могут быть решены. Например, это нулевой указатель или деление на ноль. Проверяемые исключения очень важны, ведь от других программистов, использующих ваш API, вы ожидаете, что они знают, как обращаться с исключениями. К примеру, наиболее часто встречаемое проверяемое исключение — IOException, непроверяемое — RuntimeException.
2.Почему переменные, определённые в try, нельзя использовать в catch либо finally?
Давайте посмотрим на нижеследующий код. Обратите внимание, что строку s, которая объявлена в блоке try, нельзя применять в блоке catch. То есть данный код не скомпилируется.
А всё потому, что неизвестно, где конкретно в try могло быть вызвано исключение. Вполне вероятно, что оно было вызвано до объявления объекта.
3.Почему Integer.parseInt(null) и Double.parseDouble(null) вызывают разные исключения?
Это проблема JDK. Так как они были разработаны разными людьми, то заморачиваться вам над этим не стоит:
// вызывает java.lang.NumberFormatException: null
// вызывает java.lang.NullPointerException
4.Каковы основные runtime exceptions в Java?
Вот лишь некоторые из них:
Их можно задействовать в операторе if, если условие не выполняется:
«obj не может быть равно null»
5.Возможно ли поймать в одном блоке catch несколько исключений?
Вполне. Пока классы данных исключений можно отследить вверх по иерархии наследования классов до одного и того же суперкласса, возможно применение только этого суперкласса.
6.Способен ли конструктор вызывать исключения?
Способен, ведь конструктор — это лишь особый вид метода.
//get current directory
7.Возможен ли вызов исключений в final?
В принципе, можете сделать таким образом:
Но если желаете сохранить читабельность, объявите вложенный блок try-catch в качестве нового метода и вставьте вызов данного метода в блок finally.
- Errorpublic Error(String message,
Throwable cause)Constructs a new error with the specified detail message and
cause. Note that the detail message associated with
cause is not automatically incorporated in
this error’s detail message.message — the detail message (which is saved for later retrieval
by the Throwable.getMessage() method).
cause — the cause (which is saved for later retrieval by the
Throwable.getCause() method). (A null value is
permitted, and indicates that the cause is nonexistent or
unknown.)1.4
- Errorpublic Error(Throwable cause)Constructs a new error with the specified cause and a detail
message of (cause==null ? null : cause.toString()) (which
typically contains the class and detail message of cause).
This constructor is useful for errors that are little more than
wrappers for other throwables.cause — the cause (which is saved for later retrieval by the
Throwable.getCause() method). (A null value is
permitted, and indicates that the cause is nonexistent or
unknown.)1.4
- Errorprotected Error(String message,
Throwable cause,
boolean enableSuppression,
boolean writableStackTrace)Constructs a new error with the specified detail message,
cause, suppression enabled or disabled, and writable stack
trace enabled or disabled.message — the detail message.
cause — the cause. (A null value is permitted,
and indicates that the cause is nonexistent or unknown.)
enableSuppression — whether or not suppression is enabled
or disabled
writableStackTrace — whether or not the stack trace should
be writable1.7
Описание
Throwable — базовый класс всех исключений Java. В инструкциях throw и catch можно использовать только
объекты класса Throwable и его подклассов.
Объект-исключение содержит в себе текстовое сообщение, говорящее о причине ошибки, трассировку стека вызовов на момент
создания, а также, возможно, причину (cause) — исключение более низкого уровня, завёрнутое в данное исключение.
Иерархия исключений
Все исключения в Java делятся на две большие и неравные группы. Меньшая группа наследует от класса Error и обозначает
серьёзные низкоуровневые ошибки, после которых продолжение выполнения программы обычно бессмысленно. Большая группа
наследует от класса Exception и отвечает за обычные нештатные ситуации, которые могут возникнуть в ходе выполнения
программы.
Теоретически язык Java не запрещает определить свой подкласс класса Throwable, не производный ни от Error, ни от
Exception. Компилятор будет рассматривать такую неведому зверушку как обычное проверяемое исключение (см. ниже), но
на практике так никто не поступает, да и необходимости в этом нет.
Проверяемые и непроверяемые исключения
Классы Error и RuntimeException занимают особое положение в иерархии исключений. Эти классы, а также все производные
от них, являются непроверяемыми исключениями (unchecked exceptions). Все остальные исключения являются проверяемыми
(checked exceptions). На диаграмме классов выше непроверяемые исключения выделены голубым, а проверяемые
— зелёным.
Метод, внутри которого может быть выброшено проверяемое исключение, должен либо обработать его в блоке catch, либо
передать выше и явно сказать об этом в объявлении с помощью ключевого слова throws (и тогда разбираться с ним будут
уже где-то выше). На непроверяемые исключения компилятор не налагает таких требований.
В стандартной библиотеке есть одно нарушение этого правила. Статический метод Class.newInstance передаёт во внешний
код любые исключения, в том числе проверяемые, что позволяет обойти проверки времени компиляции:
Здесь main упадёт по IOException, хотя обычно компилятор заставил бы нас объявить его как throws IOException.
Мораль в том, что при использовании рефлексии всегда нужно быть осторожными, потому что она предоставляет внеязыковой
механизм манипуляции классами и методами.
Исключения, производные от класса Error, являются непроверяемыми и обладают объединяющими свойствами:
Ловить и обрабатывать исключения, производные от Error, обычно не следует. Они выбрасываются затем, чтобы как можно
быстрее дать потоку завершиться аварийно, выполнив при этом все блоки finally при раскрутке стека.
Вот некоторые наиболее распространённые исключения типа Error:
StackOverflowError
: Переполнение стека вызовов. Обычно это указывает на бесконечную рекурсию.
OutOfMemoryError
: Переполнение кучи, причём сборщик мусора уже попытался её почистить и беспомощно развёл руками. Обычно это указывает
на утечки памяти, вызванные хранением ссылок на множество ненужных объектов в корневых переменных.
LinkageError
: Базовый класс для различных ошибок при загрузке классов, необходимых для работы выполняемого кода. Сюда относятся в
том числе NoClassDefFoundError и ExceptionInInitializerError.
NoClassDefFoundError
: JVM не может найти класс, к которому пытается обратиться код. Это может случиться, если код был скомпилирован с
зависимостью от какого-то класса или библиотеки, но запущен при отсутствии этого класса/библиотеки в classpath.
ExceptionInInitializerError
: При инициализации статического поля класса или внутри блока static выбросилось исключение, к которому можно
доступиться через метод getCause.
ThreadDeath
: В многопоточной программе чужой поток нагло и бесцеремонно завершил работу нашего потока методом Thread.stop. Если
поток завершается по этому исключению, по умолчанию сообщение об ошибке подавляется и не пишется в консоль.
AssertionError
: Нарушено базовое условие, которое в корректно работающей программе должно выполняться всегда. Исключения этого класса
бросает инструкция assert при запуске JVM с параметром -ea (enable assertions), а также библиотека JUnit при провале
тестов.
Как правило, не стоит выбрасывать исключения типа Error инструкцией throw, кроме исключения AssertionError. Его
принято выбрасывать как «невозможное» исключение, чтобы «заткнуть» выбросом исключения пути выполнения, которые заведомо
никогда не будут выполнены, но компилятор об этом не знает. Часто это приходится делать при использовании перечислимых
типов в инструкции switch:
В нашем случае у светофора всего три возможных состояния, но компилятор тем не менее требует, чтобы блок switch
обработал невозможную ситуацию default. С помощью throw new AssertionError мы затыкаем компилятор и говорим
сопровождающему код, что эта строка кода заведомо не выполнится.
Ещё один частый use case для AssertionError — блок catch для исключения, невозможного по определению.
Например, стандартные классы URLEncoder и URLDecoder имеют методы, принимающие два параметра: перекодируемую строку
и строку с именем кодировки.
= ;
= .(, );
Этот код просто так не скомпилируется, потому что метод URLDecoder.decode объявлен как
throws UnsupportedEncodingException. Однако кодировка UTF-8 гарантированно поддерживается в любой реализации JVM,
поэтому на самом деле это исключение никогда не выбросится (но компилятор, увы, об этом не знает). Нам придётся
обернуть этот код в идиому «невозможное исключение» (impossible exception):
Большинство методов, работающих с кодировками, имеют версии, принимающие вместо строки с именем кодировки объект
класса Charset и не бросающие UnsupportedEncodingException. Им можно передавать константы из класса
StandardCharsets:
К сожалению, некоторые старые классы, в том числе URLEncoder и URLDecoder, так и не были обновлены для поддержки
параметров типа Charset.
Исключения времени выполнения (RuntimeException)
Строго говоря, все исключения возникают во время выполнения, но класс RuntimeException занимает особое положение.
Во-первых, RuntimeException и все производные от него исключения являются непроверяемыми. Во-вторых,
многие из этих исключений сигнализируют об ошибках в программном коде и в корректно написанной программе не должны
возникать вообще. Такие исключения полезно выбрасывать в собственных public-методах в качестве предусловий
(preconditions), краткий смысл которых состоит в том, чтобы сказать вызывающему коду: «Ты виноват, такой ситуации вообще
не должно быть».
Вот некоторые самые важные из них:
NullPointerException
: Программа попыталась обратиться к полю или методу ссылки null, либо значение null было передано в метод, не
допускающий null в качестве параметра.
IllegalArgumentException
: Метод был вызван с недопустимым значением параметра. Например, один из конструкторов стандартного класса Color,
принимающий три целых числа RGB, выбрасывает это исключение, если переданные числа не лежат в диапазоне 0–255.
IllegalStateException
: Объект находится в состоянии, в котором выполнение операции невозможно. Например, реализация стека может выбросить
это исключение при попытке извлечь элемент из пустого стека.
IndexOutOfBoundsException
: Попытка обратиться к упорядоченной последовательности элементов (массиву, строке или списку) по индексу, лежащему
вне допустимых значений. Часто происходит при ошибке на единицу, когда в качестве индекса используется length или
size.
NoSuchElementException
: Выбрасывается итератором при попытке прочитать элемент за концом последовательности (когда hasNext() == false). В
корректной реализации итератора это исключение возможно только при ручной работе с итератором и невозможно при
использовании цикла for-each.
UnsupportedOperationException
: Вызываемая операция в принципе запрещена для этого объекта. Например, неизменяемые коллекции выбрасывают это
исключение при попытке изменить их методами set, add или remove.
ArithmeticException
: Вызвана недопустимая арифметическая операция, например, деление на ноль. Операции над числами с плавающей точкой не
выбрасывают это исключение, вместо этого при недопустимой операции они возвращают значение NaN (а при делении
ненулевого числа на ноль — бесконечность).
ClassCastException
: Попытка привести объект к несовместимому типу, например, (String) new Object().
Прочие исключения (Exception)
Исключения, производные от Exception, но не RuntimeException, являются проверяемыми. Их очень много, и сторонние
библиотеки часто добавляют свои собственные классы исключений. Вот лишь несколько примеров:
ClassNotFoundException
: Не путать с NoClassDefFoundError. Выбрасывается методом Class.forName, если класс с таким именем не найден. Это
исключение, в отличие от NoClassDefFoundError, предназначено для того, чтобы его перехватывали и обрабатывали.
CloneNotSupportedException
: По умолчанию выбрасывается protected-методом Object.clone, если объект не реализует интерфейс Cloneable. По
каким-то непонятным причинам конченые укурки, проектировавшие Java 1.0, сделали это исключение проверяемым, и при
реализации Cloneable-классов это исключение приходится подавлять.
SQLException
: Базовый класс для ошибок при работе с базами данных через JDBC API.
IOException
: Базовый класс для исключений при операциях ввода-вывода, к которым относятся операции, в том числе, с файловой
системой и сетью. У этого класса очень много подклассов, обозначающих конкретные ситуации; например,
ConnectException бросается при неудачной попытке соединения с сервером.
FileSystemException
: Подкласс IOException, являющийся базовым классом для исключений при операциях с файловой системой.
AccessDeniedException
: Попытка обратиться к файлу, на доступ к которому у пользователя нет прав — например, попытка открыть
защищённый системный файл для записи.
NoSuchFileException
: Попытка обратиться к несуществующему файлу.
Класс NoSuchFileException появился в Java 7 и является частью нового файлового API (классы Path и Files). В
старых API, спроектированных для Java 6 и ниже, можно встретить более старое исключение FileNotFoundException,
присутствовавшее ещё в Java 1.0. К сожалению, это исключение не позволяет различить ситуации «файл не найден» и «доступ
запрещён» и выбрасывается старыми API в обоих этих случаях.
Использование проверяемых и непроверяемых исключений
Тема проверяемых и непроверяемых исключений, а также правил их использования, сломала немало копий (и кода). Полного
консенсуса по этому вопросу нет, но можно пользоваться эмпирическим правилом.
Выбрасывайте проверяемые исключения только при одновременном выполнении трёх условий:
Хорошими примерами проверяемых исключений являются исключения при работе с файловой системой, сетью, базами данных,
окнами, другими процессами в системе и т.д. Все эти сущности объединяет то, что это внешние ресурсы, корректную работу
которых вызывающий код не может полностью гарантировать. Плохой пример проверяемого исключения — исключение
CloneNotSupportedException, которое может возникнуть только при логической ошибке в использовании метода clone.
Если бы класс CloneNotSupportedException был написан в наши дни, скорее всего, это исключение было бы непроверяемым.
Если хотя бы одно из этих трёх условий не выполняется, используйте непроверяемые исключения. Вот примеры ошибок, для
которых подходят непроверяемые исключения:
Создание исключения
Объекты исключений, как правило, создаются прямо в инструкции throw:
throw new SomeException(параметры);
По соглашению у большинства классов исключений есть четыре конструктора, позволяющие задать исключению сообщение и/или
причину — более низкоуровневое исключение.
SomeException(String message, Throwable cause)
Это позволяет, с одной стороны, соблюдать инкапсуляцию, не проталкивая низкоуровневые исключения вверх, а с другой
— сохранять информацию о низкоуровневых исключениях для отладочных целей.
У некоторых очень старых классов исключений, написанных до выхода Java 1.4, может не быть конструкторов с параметром
cause. Для них можно установить причину после создания объекта с помощью метода initCause:
Получение информации об исключении
Все исключения поддерживают три базовых операции:
String getMessage()
: Возвращает короткое, в одно-два предложения, сообщение об ошибке, переданное в объект исключения при его создании.
Throwable getCause()
: Возвращает исключение, послужившее причиной данного исключения, или null, если таковое не было задано. У причины,
в свою очередь, тоже может быть причина; с помощью последовательных вызовов getCause можно восстановить причинную
цепь исключений (causal chain).
void printStackTrace()void printStackTrace(PrintStream s)void printStackTrace(PrintWriter s)
: Выводит трассировку стека (stack trace) в указанный байтовый или символьный поток (по умолчанию — в
System.err). Варианты с PrintStream и PrintWriter бывают полезны, чтобы записать трассировку стека в файл или
сетевой поток, либо получить её в виде строки с помощью StringWriter.
Если у исключения есть причина, методы printStackTrace вслед за трассировкой стека самого исключения печатают
трассировку стека его причины, затем — причины причины и так далее.
По умолчанию для всех исключений, кроме ThreadDeath, при завершении потока по необработанному исключению трассировка
стека записывается в System.err с помощью printStackTrace. Это относится и к главному потоку при аварийном
завершении метода main.
Как пример, вот такая неправильная программа
при выполнении выдаст:
Exception Class
Most programs throw and catch objects that derive from the Exception class. An Exception indicates that a problem occurred, but it is not a serious system problem. Most programs you write will throw and catch Exceptions as opposed to Errors.
The Java platform defines the many descendants of the Exception class. These descendants indicate various types of exceptions that can occur. For example, IllegalAccessException signals that a particular method could not be found, and NegativeArraySizeException indicates that a program attempted to create an array with a negative size.
One Exception subclass, RuntimeException, is reserved for exceptions that indicate incorrect use of an API. An example of a runtime exception is NullPointerException, which occurs when a method tries to access a member of an object through a null reference. The section
Unchecked Exceptions — The Controversy discusses why most applications shouldn’t throw runtime exceptions or subclass RuntimeException.
Процесс
Для самого процесса используются элементы управления (см. изображение выше, выделено зеленым прямоугольником) и немного из дополнительно (см. изображение выше, выделено оранжевым прямоугольником)
Show Execution Point (Alt+F10) — переносит в файл и текущую линию отлаживаемого скрипта. Например если файлов много, решили посмотреть что в других вкладках, а потом забыли где у вас отладка 🙂
Step Over (F8) — делает один шаг не заходя внутрь функции. Т.е. если на текущей линии есть какая-то функция, а не просто переменная со значением, то при клике данной кнопки, отладчик не будет заходить внутрь неё.
Step Into (F7) — делает шаг. Но в отличие от предыдущей, если есть вложенный вызов (например функция), то заходит внутрь неё.
Step Out (Shift+F8) — выполняет команды до завершения текущей функции. Удобна, если случайно вошли во вложенный вызов и нужно быстро из него выйти, не завершая при этом отладку.
Rerun (Ctrl+F5) — Перезапустить отладку
Resume Program(F9) — Продолжает выполнения скрипта с текущего момента. Если больше нет других точек останова, то отладка заканчивается и скрипт продолжает работу. В ином случае работа прерывается на следующей точке останова.
Stop (Ctrl+F2) — Завершить отладку
View Breakpoints (Ctrl+Shift+F8) — Посмотреть все установленные брейкпойнты
Mute Breakpoints — Отключить брейкпойнты.
Итак, в текущем коде видно значение входного параметра:
Если теперь нажмем F8, то попадем внутрь цикла for и нажимая теперь F8 пока не окончится цикл, можно будет наблюдать на каждой итерации, как значение num и sum постоянно изменяются. Тем самым мы можем проследить шаг за шагом весь процесс изменения любых переменных и значений на любом этапе, который интересует.
Дальнейшие нажатия F8 переместит линию кода на строки 31, 32 и, наконец, 36.
Дополнительно
Если нажать на View Breakpoints в левой панели, то можно не только посмотреть все брейкпойнты, но в появившемся окно можно еще более тонко настроить условие, при котором на данной отметке надо остановиться. В методе выше, например, нужно остановиться только когда sum превысит значение 20.
Это удобно, если останов нужен только при определённом значении, а не всегда (особенно в случае с циклами).
Больше информации об отладке можно посмотреть в http://learn.javajoy.net/debug-intellij-idea, а также в официальной документации к IDE
Используемые ключевые слова
При обработке исключений в Java применяются следующие ключевые слова:
— try – служит для определения блока кода, в котором может произойти исключение;
— catch – необходим для определения блока кода, где происходит обработка исключения;
— finally – применяется для определения блока кода, являющегося необязательным, однако при его наличии он выполняется в любом случае вне зависимости от результата выполнения блока try.
Кроме того:
1. Для возбуждения исключения используем throw.
2. Для предупреждения в сигнатуре методов о том, что метод может выбросить исключение, применяем throws.
Давайте на примере посмотрим, как используются ключевые слова в Java-программе:
//метод считывает строку с клавиатуры
//предупреждаем с помощью throws,
// что метод может выбросить исключение MyException
//в блок try заключаем код, в котором может произойти исключение, в данном
// случае компилятор нам подсказывает, что метод readLine() класса
// BufferedReader может выбросить исключение ввода/вывода
// в блок catch заключаем код по обработке исключения IOException
// в блоке finally закрываем поток чтения
// при закрытии потока тоже возможно исключение, например, если он не был открыт, поэтому “оборачиваем” код в блок try
// пишем обработку исключения при закрытии потока чтения
// мы решили, что пустая строка может нарушить в дальнейшем работу нашей программы, например, на результате этого метода нам надо вызывать метод substring(1,2), поэтому мы вынуждены прервать выполнение программы с генерацией своего типа исключения MyException с помощью throw
«String can not be empty!»
Зачем нам механизм исключений?
Для понимания опять приведём пример из обычного мира. Представьте, что на какой-нибудь автодороге имеется участок с аварийным мостом, на котором ограничена грузоподъёмность. И если по такому мосту проедет грузовик со слишком большой массой, мост разрушится, а в момент этого ЧП ситуация для шофёра станет, мягко говоря, исключительной. И вот, дабы такого не произошло, дорожные службы заранее устанавливают на дороге соответствующие предупреждающие знаки. И тогда водитель, посмотрев на знак, сравнит массу своего авто со значением разрешённой грузоподъёмности и примет соответствующее решение, например, поедет по другой дороге.
То есть мы видим, что из-за правильных действий дорожной службы шоферы крупногабаритных транспортных средств:
1) получили возможность заранее изменить свой путь;
2) были предупреждены об опасности;
3) были предупреждены о невозможности проезжать по мосту при определённых условиях.
Вот как наш жизненный пример соотносится с применением исключения на Java:
Исходя из вышесказанного, мы можем назвать одну из причин применения исключений в Java. Заключается она в возможности предупреждения исключительной ситуации для её последующего разрешения и продолжения работы программы. То есть механизм исключений позволит защитить написанный код от неверного применения пользователем путём валидации входящих данных.
Что же, давайте ещё раз побудем дорожной службой. Чтобы установить знак, мы ведь должны знать места, где водителей ТС могут ждать различные неприятности. Это первое. Далее, нам ведь надо заготовить и установить знаки. Это второе. И, наконец, надо предусмотреть маршруты объезда, позволяющие избежать опасности.
Иногда бывает, что мы не можем предусмотреть «запасной аэродром» либо специально желаем предоставить право его выбора юзеру. Но всё равно мы должны как минимум предупредить пользователя об опасности. Иначе он превратится в разъярённого шофёра, который ехал долго, не встретил ни одного предупреждающего знака и в итоге добрался до аварийного моста, проехать по которому не представляется возможным.
Что касается программирования на Java, то мы, когда пишем свои классы и методы, далеко не всегда можем предвидеть контекст их применения другими программистами в своих программах, а значит, не можем со стопроцентной вероятностью предвидеть правильный путь для разрешения исключительных ситуаций. Но предупредить коллег о возможной исключительной ситуации мы всё-таки должны, и это не что иное, как правило хорошего тона.
Выполнить это правило в Java нам как раз и помогает механизм исключений с помощью throws. Выбрасывая исключение, мы, по сути, объявляем общее поведение нашего метода и предоставляем пользователю метода право написания кода по обработке исключения.
Throwable Class and Its Subclasses
The objects that inherit from the Throwable class include direct descendants (objects that inherit directly from the Throwable class) and indirect descendants (objects that inherit from children or grandchildren of the Throwable class).
The figure below illustrates the class hierarchy of the Throwable class and its most significant subclasses. As you can see, Throwable has two direct descendants:
Error and
Exception.
The Throwable class.
Создание обработчика
Для обработки исключений java используются следующие операторы: try, catch, finally, throw, throws. Первые три — стандартная структура вашего блока. По шагам:
Рассмотрим структуру на примере Джава исключения:
Если вы хотите обработать несколько исключений – просто создайте ещё один блок catch.
С помощью оператора throw вы можете создавать исключения:
На практике это выглядит так:
Включим оператор throw в наш стандартный пример с try-catch:
Как только обработка дойдёт до оператора throw, дальнейшее выполнение кода будет прекращено. Обработчик рассмотрит ближайший блок try-catch на требуемое исключение, потом следующий и так до конца кода. В случае, если вызвать ява исключение неоткуда – обработчик остановит программу.
Оператор throws используется для методов, которые содержат исключения, но их не обрабатывают.
Несколько исключений в списке необходимо перечислить через запятую. Воспользовавшись такой конструкцией, вы сообщите всем вызывающим методам о необходимости учитывать исключения.
Операторы try можно вкладывать друг в друга. При этом если вложенный обработчик не имеет своего блока catch, он осуществляет его поиск в родительском операторе. Если и там нет – блок обрабатывается системой.
В процессе исполнения программы исключение генерируется Java Virtual Machine либо вручную посредством оператора throw. В таком случае в памяти происходит создание объекта исключения, выполнение основного кода прерывается, а встроенный в JVM обработчик исключений пробует найти способ обработать это самое исключение.
The throw Statement
All methods use the throw statement to throw an exception. The throw statement requires a single argument: a throwable object. Throwable objects are instances of any subclass of the Throwable class. Here’s an example of a throw statement.
The pop method checks to see whether any elements are on the stack. If the stack is empty (its size is equal to 0), pop instantiates a new EmptyStackException object (a member of java.util) and throws it. The
Creating Exception Classes section in this chapter explains how to create your own exception classes. For now, all you need to remember is that you can throw only objects that inherit from the java.lang.Throwable class.
Note that the declaration of the pop method does not contain a throws clause. EmptyStackException is not a checked exception, so pop is not required to state that it might occur.
Debugging (Отладка)
В чем заключается процесс отладки? Что это такое?
Процесс отладки состоит в том, что мы останавливаем выполнения скрипта в любом месте, смотрим, что находится в переменных, в функциях, анализируем и переходим в другие места; ищем те места, где поведение отклоняется от правильного.
Заметка: Отладка производится как правило в IDE (Интегрированная среда разработки). Что это такое можно чуть подробнее ознакомиться в вопросе
Какие есть способы предупреждения ошибок, их нахождения и устранения?
В данном случае будет рассмотрен пример с Intellij IDEA, но отладить код можно и в любой другой IDE.
Иерархия исключений в Java
Когда возникают ошибки при выполнении программы, исполняющая среда Java Virtual Machine обеспечивает создание объекта нужного типа, используя иерархию исключений Java — речь идёт о множестве возможных исключительных ситуаций, которые унаследованы от класса Throwable — общего предка. При этом исключительные ситуации, которые возникают в программе, делят на 2 группы:
1. Ситуации, при которых восстановление нормальной дальнейшей работы невозможно.
2. Ситуации с возможностью восстановления.
К первой группе можно отнести случаи, при которых возникают исключения, которые унаследованы из класса Error. Это ошибки, возникающие во время выполнения программы при сбое работы Java Virtual Machine, переполнении памяти либо сбое системы. Как правило, такие ошибки говорят о серьёзных проблемах, устранение которых программными средствами невозможно. Данный вид исключений в Java относят к неконтролируемым исключениям на стадии компиляции (unchecked). К этой же группе относятся и исключения-наследники класса Exception, генерируемые Java Virtual Machine в процессе выполнения программы — RuntimeException. Данные исключения тоже считаются unchecked на стадии компиляции, а значит, написание кода по их обработке необязательно.
Что касается второй группы, то к ней относят ситуации, которые можно предвидеть ещё на стадии написания приложения, поэтому для них код обработки должен быть написан. Это контролируемые исключения (checked). И в большинстве случаев Java-разработчики работают именно с этими исключениями, выполняя их обработку.
Прежде чем мы перейдём к практике, давайте познакомимся с видами исключений Джава и их иерархией. В основе всего лежит класс Throwable. Все возможные конфликты кода с машиной и пользователем описаны здесь. Для удобства обработки и чтения класс Throwable имеет подклассы Error и Exception. Error – критические ошибки, которые не обязательно происходят по вине пользователя, обработать их невозможно. Exception – собственно конфликты нашей программы, которые необходимо отлавливать.
Взгляните на упрощённую схему иерархии исключений java:
Как видно, блоки делятся на «два лагеря» по цветам — проверяемые и непроверяемые java исключения. Данная классификация показывает, как их воспринимает компилятор: проверяемые – учитывает, непроверяемые – игнорирует. К первому относится Exception в полном составе, кроме RuntimeException. Все остальные классы исключений – непроверяемые компилятором.
Иерархия классов исключений важна и для правильной организации кода. Допустим, у вас есть несколько блоков обработки. Тогда в начале необходимо указать низшие уровни, а в конце – высшие. В противном случае, будет запущен только первый блок, а остальные – проигнорированы.
Что называют исключением. Исключения в мире программирования
В программировании исключением называют возникновение ошибки (ошибок) и различных непредвиденных ситуаций в процессе выполнения программы. Исключения могут появляться как в итоге неправильных действий юзера, так и из-за потери сетевого соединения с сервером, отсутствии нужного ресурса на диске и т. п. Также среди причин исключений — ошибки программирования либо неверное использование API.
При этом в отличие от «человеческого мира», программное приложение должно чётко понимать, как поступать в подобной ситуации. И вот как раз для этого в Java и существует механизм исключений (exception).
Подготовка
Достаточно иметь в наличии IDE, например Intellij IDEA
Запуск
Для начала в левой части панели с кодом на любой строке можно кликнуть ЛКМ, тем самым поставив точку останова (breakpoint — брейкпойнт). Это то место, где отладчик автоматически остановит выполнение Java, как только до него дойдёт. Количество breakpoint’ов не ограничено. Можно ставить везде и много.
В данном случае, т.к. функция вызывается сразу на той же странице, то при нажатии кнопки Debug — отладчик моментально вызовет метод, выполнение «заморозится» на первом же брейкпойнте. В ином случае, для активации требуется исполнить действие, при котором произойдет исполнение нужного участка кода (клик на кнопку в UI, передача POST запроса с данными и прочие другие действия)
Готовые и новые исключения
Далее приведём список java исключений, которые вам потребуются в работе чаще других:
Все указанные типы java исключений содержатся в классе RuntimeException, а значит, их не надо указывать в блоке throws.
Естественно, система не может содержать всевозможные исключения. Некоторые придётся создавать самостоятельно. Для того, чтобы создать собственное java исключение, вам необходимо унаследовать собственный класс от Exception и переопределить требуемые методы класса Throwable. Или унаследоваться от наиболее близкого по смыслу типа. Рассмотрим на примере программы под android создание java исключения:
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
Обработка исключений – основа безопасного и качественного кода. С их помощью вы можете управлять действиями пользователя, ходом выполнения программы или добавить вариативности коду.
Обработка исключения
В процессе возбуждения исключения в try обработчик исключения ищется в блоке catch, который следует за try. При этом если в catch присутствует обработчик данного вида исключения, происходит передача управления ему. Если же нет, JVM осуществляет поиск обработчика данного типа исключения, используя для этого цепочку вызова методов. И так происходит до тех пор, пока не находится подходящий catch. После того, как блок catch выполнится, управление переходит в необязательный блок finally. Если подходящий блок catch найден не будет, Java Virtual Machine остановит выполнение программы, выведя стек вызовов методов под названием stack trace. Причём перед этим выполнится код блока finally при наличии такового.
Рассмотрим практический пример обработки исключений:
«Exception: s is null!»
«Inside method print: »
«Exception was processed. Program continues»
«Inside bloсk finally»
А теперь глянем на результаты работы метода main:
Блок finally чаще всего используют, чтобы закрыть открытые в try потоки либо освободить ресурсы. Но при написании программы уследить за закрытием всех ресурсов возможно не всегда. Чтобы облегчить жизнь разработчикам Java, была предложена конструкция try-with-resources, автоматически закрывающая ресурсы, открытые в try. Используя try-with-resources, мы можем переписать наш первый пример следующим образом:
А благодаря появившимся возможностям Java начиная с седьмой версии, мы можем ещё и объединять в одном блоке перехват разнотипных исключений, делая код компактнее и читабельнее:
Предупреждаем о неприятностях
Если мы не планируем обрабатывать исключение в собственном методе, но желаем предупредить пользователей метода о возможной исключительной ситуации, мы используем, как это уже было упомянуто, ключевое слово throws. В сигнатуре метода оно означает, что при некоторых обстоятельствах метод может выбросить исключение. Это предупреждение становится частью интерфейса метода и даёт право пользователю на создание своего варианта реализации обработчика исключения.
После упоминания ключевого слова throws мы указываем тип исключения. Как правило, речь идёт о наследниках класса Exception Java. Но так как Java — это объектно-ориентированный язык программирования, все исключения представляют собой объекты.
When a dynamic linking failure or other hard failure in the Java virtual machine occurs, the virtual machine throws an Error. Simple programs typically do not catch or throw Errors.
Итоги
Итак, применение исключений в Java повышает отказоустойчивость программы благодаря использованию запасных путей. Кроме того, появляется возможность отделить код обработки исключительных ситуаций от логики основного кода за счёт блоков catch и переложить обработку исключений на пользователя кода посредством throws.