Обработка ошибок и исключения — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Проверяемые исключения)
(RuntimeException)
Строка 32: Строка 32:
 
В некоторых случаях ситуация не столь критична. Например, нехватка памяти, вызывающая java.lang.OutOfMemoryError. Если эта ошибка произошла в момент выделения большого объема памяти – например, при создании массива, – ее можно перехватить и попытаться выделить память в меньших объемах, изменив каким-то образом алгоритм, который будет эту память использовать.
 
В некоторых случаях ситуация не столь критична. Например, нехватка памяти, вызывающая java.lang.OutOfMemoryError. Если эта ошибка произошла в момент выделения большого объема памяти – например, при создании массива, – ее можно перехватить и попытаться выделить память в меньших объемах, изменив каким-то образом алгоритм, который будет эту память использовать.
 
===RuntimeException===
 
===RuntimeException===
Эти исключения обычно возникают в результате ошибок программирования, например, ошибки разработчика или неверное использование интерфейса приложения. Например, в случае выхода за границы массива метод бросит ''OutOfBoundsException''. Теоретически приложение может поймать это исключение, но разумнее исправить ошибку.
+
Эти исключения обычно возникают в результате ошибок программирования, такие как ошибки разработчика или неверное использование интерфейса приложения. Например, в случае выхода за границы массива метод бросит OutOfBoundsException. Теоретически приложение может поймать это исключение, но разумнее исправить ошибку.
  
 
==Обработка исключений==
 
==Обработка исключений==

Версия 15:33, 23 июня 2013

Методы обработки ошибок

  1. Не обрабатывать
  2. Коды возврата. Основная идея — в случае ошибки возвращать специальное значение, которое не может быть корректным. Минусом такого подхода является необходимость проверки на наличие ошибок внутри метода и возвращаемого значения каждый раз при вызове метода. Кроме того, не всегда возможно определить тип ошибки.
  3. Использовать флаг ошибки: при возникновении ошибки устанавливать флаг в соответствующее значение. Минусы такого подхода аналогичны минусам использования кодов возврата.
  4. Можно вызвать метод обработки ошибки и возвращать то, что вернет этот метод. Но в таком случае не всегда возможно проверить корректность результата вызова основного метода.
  5. В случае ошибки просто закрыть программу. Это приведет к потере данных, также невозможно понять, в каком месте возникла ошибка.

Исключения

В Java возможна обработка ошибок с помощью исключений, которые позволяют:

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

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

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

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

Классификация исключений

Класс Java Throwable описывает все, что может быть брошено как исключение.

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

Наследники класса Exception (кроме наслеников RuntimeException) являются проверяемыми исключениями(checked exception). Как правило, это ошибки, возникшие по вине внешних обстоятельств или пользователя приложения – неправильно указали имя файла, например. Эти исключения должны обрабатываться в ходе работы программы, поэтому компилятор проверяет наличие обработчика или явного описания тех типов исключений, которые могут быть сгенерированы некоторым методом.

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

Error

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

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

RuntimeException

Эти исключения обычно возникают в результате ошибок программирования, такие как ошибки разработчика или неверное использование интерфейса приложения. Например, в случае выхода за границы массива метод бросит 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. Исходное исключение теряется.

Спецификация исключений

Спецификация исключения использует дополнительное ключевое слово throws, за которым следует список потенциальных типов исключений. Так что определение метода может выглядеть так:

void f() throws InterruptedException, IOException { //...

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

Исключения в 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-блоком.