Исключения Java
Защита от дурака — стандартная конструкция для любого приложения. Рассмотрим, как организовать её в Java.
15 сентября 2017 3 минуты 25412
Автор статьи
Илья Бубнов

Автор статьи
Илья Бубнов
https://gbcdn.mrgcdn.ru/uploads/post/1217/og_cover_image/cf0ec3cbe152ff0e4cb3936d72d59bea
Одно из стандартных требований ТЗ на разработку ПО – отсутствие ошибок и конфликтов, препятствующих нормальной работе. Путей реализации два – ограничить функциональность и возможности пользователя или создать код, который будет учитывать возможные неприятности.
В java исключением называется любая ошибка, которая возникает в ходе выполнения программы. Это может быть несоответствие типов данных, деление на ноль, обрыв связи с сервером и многое другое. Операции по их поиску и предотвращению называются обработкой исключений.
Иерархия
Прежде чем мы перейдём к практике, давайте познакомимся с видами исключений Джава и их иерархией. В основе всего лежит класс Throwable. Все возможные конфликты кода с машиной и пользователем описаны здесь. Для удобства обработки и чтения класс Throwable имеет подклассы Error и Exception. Error – критические ошибки, которые не обязательно происходят по вине пользователя, обработать их невозможно. Exception – собственно конфликты нашей программы, которые необходимо отлавливать.
Взгляните на упрощённую схему иерархии исключений java:

Как видно, блоки делятся на «два лагеря» по цветам — проверяемые и непроверяемые java исключения. Данная классификация показывает, как их воспринимает компилятор: проверяемые – учитывает, непроверяемые – игнорирует. К первому относится Exception в полном составе, кроме RuntimeException. Все остальные классы исключений – непроверяемые компилятором.
Иерархия классов исключений важна и для правильной организации кода. Допустим, у вас есть несколько блоков обработки. Тогда в начале необходимо указать низшие уровни, а в конце – высшие. В противном случае, будет запущен только первый блок, а остальные – проигнорированы.
Создание обработчика
Для обработки исключений java используются следующие операторы: try, catch, finally, throw, throws. Первые три — стандартная структура вашего блока. По шагам:
- Оператор или часть кода, в которой вам надо отыскать ошибку, помещается в блок try.
- Далее в блоке catch вы указываете, что за исключение надо ловить и как его обрабатывать.
- В блоке finally набор обязательных действий при возникновении ошибки. Обычно это запись данных, закрытие ресурсов и пр. Блок исполняется всегда, вне зависимости от срабатывания catch.
Рассмотрим структуру на примере Джава исключения:
try // код, где мы хотим отследить ошибку
>
catch (тип_исключения объект_исключения) // код обработки
>
finally // что нужно выполнить после завершения блока try
>
Если вы хотите обработать несколько исключений – просто создайте ещё один блок catch.
try // код, где мы хотим отследить ошибку
>
catch (тип_исключения_1 объект_исключения_1) // код обработки
>
catch (тип_исключения_2 объект_исключения_2) // код обработки
>
finally // что нужно выполнить после завершения блока try
>
С помощью оператора throw вы можете создавать исключения:
На практике это выглядит так:
Student stud1;
public void onClick(View view) if(stud1 == null) throw new NullPointerException(«Студента не существует»);
>
>
Включим оператор throw в наш стандартный пример с try-catch:
public void onClick(View view) if (stud1 == null) try throw new NullPointerException(«Студента не существует»);
> catch (NullPointerException e) Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
>
>
>
Как только обработка дойдёт до оператора throw, дальнейшее выполнение кода будет прекращено. Обработчик рассмотрит ближайший блок try-catch на требуемое исключение, потом следующий и так до конца кода. В случае, если вызвать ява исключение неоткуда – обработчик остановит программу.
Оператор throws используется для методов, которые содержат исключения, но их не обрабатывают.
тип имя_метода(список_параметров) throws список_исключений // код метода
>
Несколько исключений в списке необходимо перечислить через запятую. Воспользовавшись такой конструкцией, вы сообщите всем вызывающим методам о необходимости учитывать исключения.
Операторы try можно вкладывать друг в друга. При этом если вложенный обработчик не имеет своего блока catch, он осуществляет его поиск в родительском операторе. Если и там нет – блок обрабатывается системой.
Готовые и новые исключения
Далее приведём список java исключений, которые вам потребуются в работе чаще других:
- ArithmeticException — ошибки вычислений.
- NullPointerException — ссылка на пустое место.
- NegativeArraySizeException — массив отрицательной размерности.
- ArrayStoreException — присвоение элементу массива неправильного типа.
- NumberFormatException — невозможно преобразовать строку в число.
- IllegalArgumentException — неправильный аргумент при вызове метода.
- UnsupportedOperationException — указанной операции не существует.
- TypeNotPresentException — указанного типа не существует.
Все указанные типы java исключений содержатся в классе RuntimeException, а значит, их не надо указывать в блоке throws.
Естественно, система не может содержать всевозможные исключения. Некоторые придётся создавать самостоятельно. Для того, чтобы создать собственное java исключение, вам необходимо унаследовать собственный класс от Exception и переопределить требуемые методы класса Throwable. Или унаследоваться от наиболее близкого по смыслу типа. Рассмотрим на примере программы под android создание java исключения:
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
>
public void testMethod() throws StudentException System.out.println(«Возбуждаем StudentException из метода testMethod()»);
throw new StudentException(); // конструктор по умолчанию
>
public void testMethod2() throws StudentException System.out.println(«Возбуждаем StudentException из метода testMethod2()»);
throw new StudentException(«Создано во втором методе»);
>
public void onClick(View view) try testMethod();
> catch (StudentException e) e.printStackTrace();
System.out.println(«Исключение перехвачено»);
>
try testMethod2();
> catch (StudentException e) e.printStackTrace();
>
>
class StudentException extends Exception StudentException() >
StudentException(String msg) super(msg);
>
>
>
Обработка исключений – основа безопасного и качественного кода. С их помощью вы можете управлять действиями пользователя, ходом выполнения программы или добавить вариативности коду.
Исключения в Java


Когда столкнулся с темой «Исключения», то возникло много вопросов, на которые пришлось искать ответы по различным закоулкам интернета, чтобы понять в деталях, как всё это работает. В результате, я составил собственное объяснение, которое может оказаться более понятным для новичков, только-только столкнувшихся с этим явлением. В компьютерах Прерывание – это входящий сигнал для процессора, о том что происходит событие, которое требует немедленной реакции. Сигнал прерывания требует от процессора приостановить выполняемую программу с таким условием, чтобы она могла быть продолжена чуть позже, то есть компьютер должен запомнить всю информацию, связанную с выполнением программы. Подобные прерывания временны, если не носят фатальный характер. Такие прерывания могут быть вызваны, как программным кодом, так и некоторой функциональностью «железа» (например, просто нажатием клавиш на клавиатуре; таймеры например, для автоматического отключения компьютера). Количество Прерываний ограничено определённым числом, заложенным при производстве того или иного процессора, то есть под это выделяются специальные «каналы» связи, позволяющие обратиться к процессору в обход всех остальных процессов. Прерывания так же автоматически генерируются при возникновении ошибки в исполняемом программном коде (например, если встречается деление на ноль). Такие прерывания традиционно называются traps или exceptions. В таких случаях принято говорить: «Exception was thrown», то есть сработало Исключение или Исключение было запущено (брошено), то есть запрос на Прерывание с вопросом «что делать?» послан процессору. В этот момент процессор останавливает работу, запоминая точку, в которой он остановился, точнее кластер следующей ячейки, информация из которой должна быть выполнена. Запоминается вся цепочка выполненных инструкций и НЕ выполненных. После чего, процессор считывает из памяти инструкции для действий при подобной ошибке. В соответствии с этой инструкцией, он может занести новые значения в определённые кластеры, добавить некоторые цепочки действий или новый цикл (например, цикл возврата или кольцевания) и т.д., то есть в зависимости от ошибки выполняются ранее заложенные инструкции. В систему компьютера само по себе встроено много автоматических Прерываний, которые запускаются через определённое количество времени, например, для контроля запущенных на компьютере процессов или выполнения заложенных будильников, сбора входящих внешних сигналов, различных конвертеров данных. Стоит помнить, что большое количество Прерываний по ряду причин, может окончательно «повесить» систему. Ошибка в программном коде вызовет в автоматическом режиме Прерывание в процессоре, которое тот попробует обработать в соответствии с заложенными инструкциями. Но не для всех прерываний заложена их обработка или она может выдавать процедуру, которая нас не устраивает, например, просто свернёт приложение. Поэтому в программировании существует возможность организовать собственное прерывание для определённого участка кода, в котором программист потенциально видит вероятность наличия ошибки. В этом случае ошибка обработается внутри программы и не будет обращаться за инструкциями по обработке к процессору. Задание таких блоков организуется созданием Объекта «Исключение» (Exception) . Этот объект автоматически создаётся в блоке try-catch . В блоке >try производится проверка на наличие ошибки и, если она есть, программа следует в блок catch , где производятся действия предупреждающие или нивелирующие ошибку. Например, если мы вводим с клавиатуры Числа , которые должны в последствии складываться и вычитаться, то введение с клавиатуры Букв приведёт к невозможности их сложения с Числами (обозначим сумму этих двух переменных буквой S). Поэтому в команде try мы должны проверить способно ли число А, содержащее Цифры, быть сложено с числом Б, содержащим Буквы (то есть S = А + Б ), и если это невозможно, а это невозможно, то должны быть приняты определённые меры, чтобы Ошибки НЕ произошло и к процессору не полетело новое Прерывание с вопросом «что делать?». В случае отсутствия в программе Исключения, её выполнение прервётся процессором. При наличии Исключения, когда оно «поймано» командой try , управление переходит команде catch , которая может задать альтернативный вариант решения, например, не будем складывать эти два числа, а зададим S = А.
int a = 4; String b = “hello”; int S = 0; try < S = a + b; int r = 1; >catch (Exception igogo1) < S = a; >return S;
/* строка «int r = 1;» не выполняется, так как случилась ошибка и программа перенаправляет работу сразу к обработчику исключения блок catch*/ Таким образом, наличие Исключений – это возможность решить проблему внутри программы, не забрасывая её на уровень процессора. В объект «Исключение», который автоматически создаётся в блоке try при обнаружении ошибки, заносится значение типа ошибки. Назовём его «НашеИсключение» – для нашего конкретного случая с описанием нашей конкретной ошибки. Создатели языка Java, заранее создали некоторый список типовых ошибок и типовые варианты их исправления, то есть в java существует некоторая библиотека Исключений , к которой мы можем обратиться для обработки случившейся ошибки, чтобы самим не писать код обработки и поэтому НашеИсключение скорее всего уже кем-то было описано, поэтому нам надо просто знать название какого из этих исключений вставить в нашу программу для обработки кода, где потенциально может случиться сбой. Если мы ошибёмся и выберем из библиотеки неверное Исключение , то обработчик его не «поймает», ошибка не найдёт решения внутри программы и полетит запрос к процессору. Но существует путь для ленивых. Если мы не знаем название нужного нам исключения из библиотеки, то можем взять общее с названием « Exception », как в вышеописанном примере. Это Исключение способно обработать любой тип ошибки, только не способно выдать конкретную информацию о происшествии, которое мы могли бы занести в логи. Библиотека ранее написанных Исключений состоит из проверяемых (checked) и непроверяемых (unchecked) Исключений . Проверяемые – это те, которые можно поправить, не обрывая работу программы, то есть, если мы пытаемся открыть файл в папке, в которой его нет, то система даст нам об этом знать, мы можем закинуть файл в нужную папку и продолжить работу программы. То есть по факту к процессору послан запрос на Прерывание , но без вопроса: «ищи, что делать с этой проблемой. ». Мы послали Прерывание, которое сами обнаружили, с готовой инструкцией, которую процессор обработал и продолжил выполнение программы. Непроверяемые – это те ошибки, которые нельзя поправить и программа будет закрыта до своего завершения, то есть к процессору полетит запрос на Прерывание , которое в любом случае оборвёт выполнение программы. Единственный смысл прописывать такие исключения в программе – это дать пользователю понять, что произошло, так как, поймав это прерывание, мы можем вывести информационное сообщение на экран из-за чего программа свернулась. Второй причиной ловить такие прерывания является возможность занести их в логи для последующего анализа (вас хакнули, но зато вы знаете через какое место). Следствием наличия подобных библиотек является необходимость не забыть их подключить. (Список проверяемых и непроверяемых Исключений с библиотеками можно посмотреть, например, здесь) Если мы не знаем точно какую библиотеку включить или существует несколько вариантов ошибки, то можем в нескольких catch перечислить нужные Исключения. Система сама выберет правильный обработчик, если он есть в списке. Вместо конкретного Исключения можно написать общее « Exception », которое может обработать любой тип Исключения , если оно не обработалось в предыдущих блоках.
int a = 4; String b = “hello”; int S = 0; try < S = a + b; int r = 1; >catch(NullPointerException blabla2) < System.out.println("Exception handling code for the NullPointerException."); >catch (ArithmeticException ex1) < S = a; >catch(Exception uups1) < System.out.println("Exception occured"); >return S;
В случае наличия блока try Исключение создаётся автоматически. Если нам надо принудительно в какой-то момент времени вызвать Исключение , то используется команда throw . То есть мы самостоятельно создаём объект new throw … после чего, программа останавливает свою работу, посылает процессору запрос на Прерывание и переносится к разделу программы catch , откуда пытается почерпнуть инструкции по дальнейшим действиям. Создавая вручную Исключение , мы можем указать его конкретный тип из библиотеки:
throw new ArithmeticException("Access denied - You must be at least 18 years old.");
тогда обработчик будет искать блок catch , с именно этим Исключением – искать по всей программе, по всем бокам catch . После команды throw обработки Исключения, весь оставшийся код программы НЕ будет выполнен, за исключением того, который находится в блоке catch . Если обработчик в программе не найден, процессору задаётся вопрос: «решай сам, что делать» и тот прерывает программу. Вызов new throw … может производиться как в блоке >try , так и вне его (в любом месте программы)
try < /* функция или действие, в котором есть сомнения. То есть: «попробуй выполнить это, а если не получится, а, если не получится, запускай режим исключения» */ throw new CallForException(); /* Назначаем исключение, которое будет работать в случае наличия ошибки в функции, описанной выше. Здесь исключение «CallForException» - берется из библиотеки существующих исключений */ >catch (CallForException ee1) < /* Корректируем ошибку, чтобы программа не «отвалилась» или выводим сообщение об ошибке или что-то ещё */ >finally < /* этот блок работает всегда независимо от того была ошибка или нет. А если была, то сработало ли решение в catch или нет */ /* часто используется для подчистки хвостов, например, для закрытия запущенного файла или базы данных */ /* в ряде случаев блок catch вообще может быть опущен и оставлен только блок finally и наоборот finally может быть опущен и оставлен только catch */ /* Не допускается использование этого блока в ряде случаев, например, когда функция System.exit() запущена или другие системные Исключения, типа «отключение электроэнергии» и т.п. */ >
Оповещение о наличии Исключения
Ранее написанные кем-то методы могут включать в себя вызов Исключений. Просто на всякий случай программист, который писал код, предупредил последующих программистов, что в написанном им методе может случится ошибка. Так, например, метод создания файла, описанный ниже, предусматривает, что при создании файла может произойти ошибка (нет файла по заданному пути), а значит нужен будет её обработчик:
public void createFile(String path, String text) throws IOException
Но при этом самого обработчика нет, а значит теперь просто вызвать написанный метод в своей программе в обычном режиме мы не сможем. Теперь мы обязаны написать обработчик ошибки и вызывать этот метод в блоке try :
String filePath = "hello.txt"; String text = "Hello World"; try < createFile(filePath, text); >catch (IOException ex)
Собственные исключения
Существует возможность написать собственные исключения для обработки тех или иных ошибок, если нам недостаточно уже существующих библиотек. Для этого мы просто создаём класс-наследник от класса Исключения
public class StudentNotFoundException extends Exception < public StudentNotFoundException (String message) < super(message); >>
- Название нашего класса должно оканчиваться на «Exception»
- Класс должен содержать конструктор со строковой переменной, описывающей детали проблемы Исключения. В конструкторе вызывается супер-конструктор с передачей сообщения.
public class StudentManager < public Student find(String studentID) throws StudentNotFoundException < if (studentID.equals("123456")) < return new Student(); >else < throw new StudentNotFoundException( "Could not find student with ID " + studentID); >> >
Отлавливаем это Исключение кодом:
public class StudentTest < public static void main(String[] args) < StudentManager manager = new StudentManager(); try < Student student = manager.find("0000001"); >catch (StudentNotFoundException ex) < System.err.print(ex); >> >
Результатом выполнения программы будет: StudentNotFoundException: Could not find student with ID 0000001
Зачем нужно писать Исключения
В 1996 году разбилась ракета Ariane 5 из-за неверной конвертации переменной float в переменную integer. Исключений и обработчиков этой ситуации предусмотрено не было. Если при загрузке файла произошел обрыв связи с интернетом, то наличие Исключения позволит продолжить закачку, после того как соединение восстановится. При отсутствии Исключения закачку придётся начинать заново.
Использованная литература:
- Interrupt in hardware
- Исключения в Вики
- Исключения в Вики для Java
- Исключение в Вики (кинуть-поймать)
- Тип «подписи»
- Проверяемые-непроверяемые исключения
- urvanov.ru
- Исключения для начинающих на WikiBook
- блок try-catch
- Создание собственных исключений
Как перечислять исключения в java
Приложение часто отвечает на исключение, выдавая другое исключение. В действительности первое исключение вызывает второе исключение. Может быть очень полезно знать, когда одно исключение вызывает другого. Цепочечные Исключения помогают программисту сделать это.
Следующее является методами и конструкторами в Throwable та поддержка объединенные в цепочку исключения.
Throwable getCause() Throwable initCause(Throwable) Throwable(String, Throwable) Throwable(Throwable)
Throwable параметр initCause и Throwable конструкторы являются исключением, которое вызвало текущее исключение. getCause возвращает исключение, которое вызвало текущее исключение, и initCause устанавливает причину текущего исключения.
Следующий пример показывает, как использовать цепочечное исключение.
try < >catch (IOException e)
В этом примере, когда IOException пойман, новое SampleException исключение создается с исходной присоединенной причиной, и цепочка исключений бросается до следующего высокоуровневого обработчика исключений.
Доступ к Трассировочной информации Стека
Теперь давайте предположим, что высокоуровневый обработчик исключений хочет вывести трассировку стека в своем собственном формате.
Определение: трассировка стека предоставляет информацию об истории выполнения текущего потока и перечисляет имена классов и методов, которые вызвали в точке, когда исключение произошло. Трассировка стека является полезным средством отладки, которое Вы будете обычно использовать в своих интересах, когда исключение было выдано.
Следующий код показывает, как вызвать getStackTrace метод на объекте исключения.
catch (Exception cause) < StackTraceElement elements[] = cause.getStackTrace(); for (int i = 0, n = elements.length; i < n; i++) < System.err.println(elements[i].getFileName() + ":" + elements[i].getLineNumber() + ">> " + elements[i].getMethodName() + "()"); > >
Журналирование API
Следующий фрагмент кода регистрирует, где исключение произошло изнутри catch блок. Однако, вместо того, чтобы вручную анализировать трассировку стека и отправить вывод System.err() , это отправляет вывод файлу, используя средство журналирования в java.util.logging пакет.
try < Handler handler = new FileHandler("OutFile.log"); Logger.getLogger("").addHandler(handler); >catch (IOException e) < Logger logger = Logger.getLogger("package.name"); StackTraceElement elements[] = e.getStackTrace(); for (int i = 0, n = elements.length; i < n; i++) < logger.log(Level.WARNING, elements[i].getMethodName()); >>
Ваше использование этой страницы и всего материала на страницах под «Учебным баннером» Java подвергается этим официальным уведомлениям.
Авторское право © 1995, 2012 Oracle и/или его филиалы. Все права защищены.
Вопросы по Java на собеседовании (3)
Исключение — это ошибка, возникающая во время выполнения программы. Причины возникновения исключения могут разные, например :
- некорректно определены (не определены) данные;
- невозможно прочитать или создать файл;
- обрыв сетевого соединения или соединения с сервером базы данных.
Исключение в Java является объектом. Поэтому они могут не только создаваться автоматически виртуальной машиной JVM при возникновении исключительной ситуации, но и порождаться самим разработчиком.
2. Операторы исключений
Java имеет пять ключевых операторов для определения блока исключений, перехвата и возбуждения исключений :
- try — начала блока кода, в котором может возникнуть исключение, и которое следует перехватить;
- catch — начала блока кода, предназначенного для перехвата и обработки исключений (параметром catch является тип ожидаемого исключения);
- throw — оператор для генерации исключений;
- throws — ключевое слово, используемое в сигнатуре метода, и обозначающее, что метод потенциально может вызвать исключение с определенным типом;
- finally — начала дополнительного блока кода, размещаемый после последнего блока catch. Блок finally не является обязательным, но всегда получает управление.
Общая структура «перехвата» исключительной ситуации выглядит следующим образом :
try < // код программы, который может привести к ошибке >catch(Exception e ) < // код программы для обработки исключений >finally < // выполнение блока программы независимо от наличия исключения >
3. Оператор throws
Оператор throws включается в сигнатуру метода с целью обозначения возожности возникновения исключительной ситуации с определенным типом. Использовать данный оператор следует в описании тех методов, которые могут возбуждать исключения, но сами их не обрабатывают. Таким образом, оператором throws метод предупреждает другие методы, вызывающие данный, что у него могут быть вызваны необработанные исключения, чтобы вызывающие методы могли защитить себя от этих исключений.
public class TestThrow < static void method() throws IllegalAccessException < System.out.println("inside method"); // . . . throw new IllegalAccessException ("Exception in method"); >public static void main(String args[]) < try < method(); >catch(IllegalAccessException e) < System.out.println("Catch inside main : " + e.getMessage()); >> >
4. Блоки кода try/catch и try/finally
Каждый оператор try требует наличия либо catch, либо finally, либо сочетания catch и finally. Блок кода finally не является обязательным, и может отсутствовать при использовании try/catch. Оператором finally создаётся блок кода, который должен быть выполнен после завершения блока try/catch.
Если необходимо гарантировано выполнить определенный участок кода, то используется finally. Связка try/finally позволяет обеспечить выполнение блока кода независимо от того, какие исключения были возбуждены и перехвачены, даже в тех случаях, когда в методе нет соответствующего возбужденному исключению раздела catch.
Пример использования try/finally представлен здесь.
5. Может ли блок finally не выполняться?
Код блока finally не будет исполнен, если в код программы включен предшествующий блоку finally системный выход. Следующий пример демонстрирует данную ситуацию.
try < System.exit(0); >catch(Exception e) < System.err.println(e.getMessage()); >finally < // код блока >
6. Проверяемые и непроверяемые исключения
Все исключения делятся на «проверяемые» (checked) и «непроверяемые» (unchecked). Данное свойство присуще базовому классу исключения Throwable и передается по наследству (Error, Exception, RuntimeException). В исходном коде класса исключения данное свойство недоступно. Ниже представлена иерархия классов исключений.
Object | Throwable(CHECKED) / \ Error(UNCHECKED) Exception(CHECKED) | RuntimeException(UNCHECKED)
Исключения Throwable и Exception, а также все их наследники, за исключением Error и RuntimeException, являются «проверяемыми» checked исключениями. Error и RuntimeException, а также все их наследники, относятся к «непроверяемым» unchecked исключениям.
Проверка исключения на checked выполняется компилятором (compile-time checking). Непроверяемые исключения можно перехватить (catch) в момент исполнения программы (runtime checking).
Java – это язык программирования со статической типизацией, т.е. его компилятор отслеживает корректности использования типов : наличие полей и методов, checked исключения, и т.д. Следующий пример демонстрирует наличие двух ошибок программирования, определенные на этапе копиляции.
public class TestException < public static Double divide(int i1, int i2) throws Exception < if (i2 != 0) return new Double (i2 / i2); else < Throwable e = new Exception(); throw e; // Unhandled exception type Throwable >> public static void main(String[] args) < Object obj = "Hello!"; char c = obj.charAt(0); // The method charAt(int) is // undefined for the type Object >>
Сигнатура метода divide (операция деления) включает определение возможного исключения типа Exception, наследуемого от Throwable. Если делитель (i2) равен 0, то создается объект исключения типа Throwable и возбуждается исключение (throw t), которое не пропускает компилятор, т.к. тип возбуждаемого исключения не соответствует типу исключения в сигнатуре метода. Если в сигнатуре метода определить исключение типа Throwable, то ошибки не будет.
В методе main определен объект obj с инициализацией определенным значением. В следующей строке компилятор находит ошибку, связанную с отсутствием метода charAt(int) в объекте типа Object. Если выполнить приведение типа obj к String, то компилятор пропустит код : char c = ((String)ref).charAt(0).
Пример unchecked исключения представлен здесь.
7. Возбуждение исключения
Как было отмечено выше, исключение является объектом, который можно создать программно. Чтобы возбудить (выбросить) исключение используется оператор throw.
Exception e = new SQLException(); throw e;
8. Определение исключения в сигнатуре метода
В сигнатуре метода можно определить возможное проверяемое (checked) исключение. Следующий пример демонстрирует определение исключения в сигнатуре метода f1() и его перехват в конструкторе класса.
import java.io.EOFException; import java.io.FileNotFoundException; public class TestException < public TestException() < try < f1(); >catch (FileNotFoundException e) < e.printStackTrace(); >> public void f1() throws FileNotFoundException < throw new FileNotFoundException(); >public static void main(String[] args) < new TestException (); >>
9. Особенность RuntimeException
Исключение RuntimeException расширяет свойства Exception и является базовым классом для ошибок во время выполнения приложения. Данное исключение относится к необрабатываемым исключениям (unchecked). Согласно описанию класса это исключение может возникнуть во время нормальной работы JVM.
Следующий код демонстрирует пример использования непроверяемого исключения NumberFormatException (наследующего свойства RuntimeException). В функции parseInt при преобразовании строкового значения в число в режиме run-time может возникнуть исключение. Можно метод функции Integer.parseInt() «обернуть» в try/catch, а можно передать обработку исключения функции в вызывающий метод, для чего в сигнатуре определяется соответствующее исключение (throws).
public int parseInt(String s) throws NumberFormatException
10. Возбуждение исключения в методе main
Если в методе main возбудить исключение, то оно будет передано в виртуальную машину Java (JVM).
11. Множественные исключения
В сигнатуре метода можно определить несколько возможных исключений. Для этого используется оператор throws и исключения, разделенные запятыми. Следующий пример демонстрирует метод callMethods с множественными возможными исключениями :
import java.io.EOFException; import java.io.FileNotFoundException; public class TestException < public void callMethods() throws EOFException, FileNotFoundException < if (f1()) f0(); >public void f0() throws EOFException < // . throw new EOFException(); >public boolean f1() throws FileNotFoundException < // . throw new FileNotFoundException(); >>
Чтобы перехватить несколько возможных исключений можно искользовать конструкцию try с несколькими catch.
try < if (f1()) f0(); >catch (FileNotFoundException e) < e.printStackTrace(); >catch (EOFException e)
12. Последовательность нескольких блоков catch
При определение нескольких блоков catch следует руководствоваться правилом обработки исключений от «младшего» к старшему. Т.е. нельзя размещать первым блоком catch (Exception e) <. >, поскольку все остальные блоки catch() уже не смогут перехватить исключение. Помните, что Exception является базовым классом, поэтому его стоит размещать последним.
Рассмотрим следующую иерархию наследования исключений :
java.lang.Object java.lang.Throwable java.lang.Exception java.io.IOException java.io.EOFException
Cамым младшим исключением является EOFException, поэтому он должен располагаться перед IOException и Exception, если используется несколько блоков catch с данными типами исключений. Следующий код является демонстрацией данного принципа.
String x = «Hello»; try < if (!x.equals("Hello")) throw new IOException(); else throw new EOFException(); >catch (EOFException e) < System.err.println("EOFException : " + e.getMessage()); >catch (IOException e) < System.err.println("IOException : " + e.getMessage()); >catch (Exception e)
13. Поглащение исключений в блоке try. finally
Если было вызвано два исключения — одно в блоке try, а второе в finally — то, при отсутствии catch, исключение в finally «проглотит» предыдущее исключение. Следует блоки с возможными исключениями всегда обрамлять операторами try/catch, чтобы не потерять важную информацию. Следующий пример демонстрирует «поглащение» исключения в блоке try новым исключением в блоке finally.
public class TestException < public TestException() < try < System.out.println(absorbingEx()); >catch (EOFException e) < System.out.println(e.getMessage()); >catch (IOException e) < System.out.println(e.getMessage()); >> public String absorbingEx() throws IOException, EOFException < try < throw new EOFException("EOFException"); // >catch (EOFException e) < // System.out.println("catch " + e.getMessage()); >finally < throw new IOException("finally IOException"); >> public static void main(String[] args) < new TestException(); System.exit(0); >>
В результате в консоль будет выведено следующее сообщение :
finally IOException
Чтобы не «потерять» исключение, необходимо его корректно перехватить и обработать. В примере следует убрать комментарий с блока catch.
14. Исключение SQLException
Исключение SQLException связано с ошибками при работе с базой данных. Данное исключением относится к checked исключениям, и, следовательно, проверяется на этапе компиляции.
java.lang.Object java.lang.Throwable java.lang.Exception java.sql.SQLException
Споры вокруг SQLException связаны с тем, что исключение возникает во время исполнения, а обрабатывать его приходится в коде, чтобы не ругался компилятор; может быть следовало бы отнести его к unchecked run-time исключениям? Убедительный довод разработчиков данного исключения связан с тем, что необходимо программисту обработать свои возможные ошибки при работе с базой данных.
15. Ошибка Error
Ошибка Error относится к подклассу не проверяемых (unchecked) исключений, которая показывает серьезные проблемы, возникающие во время выполнения программы. Большинство из ошибок данного класса сигнализируют о ненормальном ходе выполнения программы, т.е. о возникновении критических проблем.
Согласно спецификации Java, не следует пытаться обрабатывать Error в собственной программе, поскольку они связаны с проблемами уровня JVM. Исключения такого рода возникают, если, например, закончилась память, доступная виртуальной машине.
16. Обобщение исключений
При определении в сигнатуре метода возможных исключений можно вместо нескольких проверяемых исключений указать общее (базовое) java.lang.Throwable. В этом случае, компилятор «пропустит код» и программа возможно отработает без сбоев. Например :
public void callCommonException() throws Exception < Object obj = new Object(); method(obj); >public void method(Object obj) throws NumberFormatException, IllegalArgumentException < // . >
Использование Exception или Throwable в сигнатуре метода делает почти невозможным правильное обращение с исключениями при вызове метода. Вызывающий метод получает только информацию о том, что что-то может отработать некорректно.
Перехват обобщенных исключений не позволяет «решить» проблему, т.е. «вылечить» программу, а помогает только обойти периодически возникающую проблему. Это может привести к нескольким непредвиденным ошибкам в других местах приложения.
17. Логирование исключений
Лучше всего логировать (регистрировать) исключение в месте его обработки. Это связано с тем, что именно в данном месте кода достаточно информации для описания возникшей проблемы. Кроме этого, одно и то же исключение при вызове одного и того же метода можно перехватывать в разных местах программы; регистрировать же следует в одном месте.
Иногда исключение может быть частью ожидаемого поведения. В этом случае нет необходимости его регистрировать.
