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

Материал из Викиконспекты
Перейти к: навигация, поиск
Определение:
Исключениями или исключительными ситуациями (состояниями) называются ошибки, возникшие в программе во время её работы.

Каждый раз, когда при выполнении программы происходит ошибка, создается объект-исключение, содержащий информацию об ошибке, включая её тип и состояние программы. После создания исключения среда выполнения пытается найти в стеке вызовов метод, который содержит код, обрабатывающий это исключение. Поиск начинается с метода, в котором произошла ошибка, и проходит через стек в обратном порядке вызова методов. Если не было найдено ни одного подходящего обработчика, выполнение программы завершается.

Таким образом, механизм обработки исключений содержит следующие операции:

  1. Создание объекта-исключения.
  2. Заполнение stack trace'а этого исключения.
  3. Stack unwinding (раскрутка стека) в поисках нужного обработчика.

Типы исключений

Проверяемые исключения

Проверяемые исключения(checked exception) — это те исключения, для которых java-машина проверяет, что они обработаны или что соответствующий метод может его бросить.

Сущность механизма проверяемых исключений состоит в добавлении следующих правил и ограничений:

  • В описании метода в явном виде перечисляются все типы исключений, которые он может сгенерировать.
  • Метод, вызывающий метод с объявленными исключениями, для каждого из этих исключений обязан либо содержать обработчик, либо, в свою очередь, указывать этот тип как генерируемый им в своём описании.
  • Компилятор проверяет наличие обработчика в теле функции или записи исключения в её заголовке.


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

Непроверяемые исключения

error

Класс Error и его подклассы предназначены для системных ошибок. Свои собственные классы-наследники для Error писать (за очень редкими исключениями) не нужно. Как правило это действительно фатальные ошибки, пытаться обработать которые довольно бессмысленно.

В некоторых случаях ситуация не столь критична. Например, нехватка памяти, вызывающая java.lang.OutOfMemoryError. Если эта ошибка произошла в момент выделения большого объема памяти – например, при создании массива, – ее можно перехватить и попытаться выделить память в меньших объемах, изменив каким-то образом алгоритм, который будет эту память использовать.

runtime exception

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

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

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

try {

 // Код, который может сгенерировать исключение

}

Сразу после блока проверки следуют после обработчики исключений, которые объявляются ключевым словом catch.

try {

 // Код, который может сгенерировать исключение

} catch(Type1 id1) {

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

} catch(Type2 id2) {

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

}

Сatch-блоки обрабатывают исключения, указанные в качестве аргумента. Тип аргумента должен быть классом, унаследованного от Throwable.

Код из блока finally выполнится в любом случае: при нормальном выходе из try, после обработки исключения или при выходе по команде return. Блок finally удобен для закрытия файлов и освобождения любых других ресурсов.

NB: Если JVM выйдет во время выполнения кода из try или catch, то finally блок может не выполниться. Также, например, если поток выполняющий try или catch код прерван, то блок finally может не выполниться, даже если приложение продолжает работать.

Код в блоке finally должен быть максимально простым: например, если внутри блока finally будет брошено какое-либо исключение или просто встретится оператор return, брошенное в блоке try исключение (если таковое было брошено) будет забыто.

import java.io.IOException;

public class ExceptionTest {

   public static void main(String[] args) {
       try {
           try {
               throw new Exception("a");
           } finally {
               throw new IOException("b");
           }
       } catch (IOException ex) {
           System.err.println(ex.getMessage());
       } catch (Exception ex) {
           System.err.println(ex.getMessage());
       }
   }

}

После того, как было брошено первое исключение - new Exception("a") - будет выполнен блок finally, в котором будет брошено исключение new IOException("b"), именно оно будет поймано и обработано. Результатом его выполнения будет вывод в консоль b. Исходное исключение теряется.

Разработка исключений

Исключения в Java7+

  • обработка нескольких типов исключений в одном catch-блоке:
catch (IOException|SQLException ex) {...}

В таких случаях параметры являются final, следовательно, нельзя присвоить им любое значение в блоке catch.

  • Try с ресурсами позволяет прямо в try блоке объявлять необходимые ресурсы, которые по завершению блока будут коректно закрыты (с помощью метода close()). Любой объект реализующий java.lang.AutoCloseable может быть использован как ресурс.

static String readFirstLineFromFile(String path) throws IOException {

   try (BufferedReader br =
                  new BufferedReader(new FileReader(path))) {
       return br.readLine();
   }

}

В приведенном примере в качестве ресурса использутся объект класса BufferedReader, который будет закрыт вне зависимосити от того, как выполнится try-блок.

Можно объявлять несколько ресурсов, разделяя их точкой с запятой.

Во время закрытия ресурсов тоже может быть брошено исключение. В try-with-resources добавленна возможность хранения "подавленных" исключений, и брошенное try-блоком исключение имеет больший приоритет, чем исключения получившиеся во время закрытия. Получить последние можно вызовом метода Throwable.getSuppressed от исключения брошенного try-блоком.