Java каким методом вывести причину исключения. Дайте определение понятию “исключение”. написать тут про errorcodes и раскрыть списочег

Скачать на Телефон 19.05.2019
Скачать на Телефон

В данной статье рассматривается используемый в Java механизм обработки исключений. Исключение в Java - это объект, который описывает исключительное состояние, возникшее в каком-либо участке программного кода. Когда возникает исключительное состояние, создается объект класса Exception . Этот объект пересылается в метод, обрабатывающий данный тип исключительной ситуации. Исключения могут возбуждаться и для того, чтобы сообщить о некоторых нештатных ситуациях.

Ключевые слова исключений try, catch, throw, throws, finally

Механизм исключительных ситуаций в Java поддерживается пятью ключевыми словами:

  • catch,
  • throw,
  • throws,
  • finally.

Ниже приведена общая форма блока обработки исключений.

Try { // блок кода } catch ( е) { // обработчик исключений типа ExceptionType1 } catch ( е) { // обработчик исключений типа ExceptionType2 } finally { // ... }

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

В вершине иерархии исключений стоит класс Throwable , который наследуется от Object . Каждый из типов исключений является подклассом Throwable. Два непосредственных наследника класса Throwable делят иерархию подклассов исключений на две различные ветви. Иерархия классов представлена на рисунке.

Класс Ехception используется для описания исключительных ситуации, которые должны перехватываться программным кодом пользователя. Класс Error предназначен для описания исключительных ситуаций, которые при обычных условиях не должны перехватываться в пользовательской программе.

Неперехваченные исключения

Объекты-исключения автоматически создаются исполняющей средой Java в результате возникновения определенных исключительных ситуаций. Пример программы, в которой создаем исключительную ситуацию при делении на нуль.

Package samples; class TestException { public static void main(String args) { int d = 0; int a = 42 / d; System.out.println ("a = " + a); } }

В консоль будет выведено следующее сообщение.

Exception in thread "main" java.lang.ArithmeticException: / by zero at samples.TestException.main(TestException.java:8)

Следует обратить внимание на тот факт, что типом возбужденного исключения был не Exception и не Throwable . Это подкласс класса Exception , а именно: ArithmeticException , поясняющий, какая ошибка возникла при выполнении программы.

Изменим класс добавлением статического метода subroutine , в котором создадим такую же исключительную ситуацию.

Package samples; public class TestException { static void subroutine() { int d = 0; int a = 10 / d; System.out.println ("a = " + a); } public static void main(String args) { TestException.subroutine(); } }

Сообщение выполнения программы показывает, как обработчик исключений исполняющей системы Java выводит содержимое всего стека вызовов.

Exception in thread "main" java.lang.ArithmeticException: / by zero at samples.TestException.subroutine(TestException.java:8) at samples.TestException.main(TestException.java:14)

Перехват исключений try/catch

Для защиты программного кода от исключений необходимо использовать связанные блоки с ключевыми словами try catch ; catch помещается сразу же после try-блока. В блоке catch задается тип исключения, которое необходимо обработать.

Class TestException { public static void main(String args) { try { int d = 0; int a = 42 / d; } catch (ArithmeticException e) { System.out.println("division by zero"); } } }

Целью большинства хорошо сконструированных catch -разделов должна быть обработка возникшей исключительной ситуации и приведение переменных программы в некоторое разумное состояние - такое, чтобы программу можно было продолжить так, будто никакой ошибки и не было (в нашем примере выводится предупреждение - division by zero).

Несколько разделов catch

В отдельных случаях блок программного кода может вызвать исключения различных типов. Для того, чтобы локализовать обработку подобных ситуаций, можно использовать несколько catch -разделов для одного try-блока . Блоки наиболее специализированных классов исключений должны идти первыми, поскольку ни один подкласс не будет достигнут, если поставить его после суперкласса.

В следующем примере перехватывается два различных типа исключений, причем за этими двумя специализированными обработчиками следует раздел catch общего назначения, перехватывающий все подклассы класса Throwable .

Class MultiCatch { static int c = { 1 }; public static void main(String args) { try { int a = args.length; System.out.println("a = " + String.valueOf(a)); int b = 23 / a; c = 33; } catch (ArithmeticException e) { System.out.println("ArithmeticException: " + e.getMessage()); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("ArrayIndexOutOfBoundsException: " + e.getMessage()); } } }

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

А = 0 div by 0: java.lang.ArithmeticException: / by zero a = 1 array index oob: java.lang.ArrayIndexOutOfBoundsException:33

Вложенные операторы try

Операторы try можно вкладывать друг в друга. Если у оператора try низкого уровня нет раздела catch , соответствующего возбужденному исключению, стек будет развернут на одну ступень выше, и в поисках подходящего обработчика будут проверены разделы catch внешнего оператора try. Пример вложения двух операторов try catch друг в друга посредством вызова метода.

Class MultiNest { static int c = { 1 }; static void checkArray() { try { c = 33; } catch(ArrayIndexOutOfBoundsException e) { System.out.println "ArrayIndexOutOfBoundsException: " + e.getMessage()); } } public static void main(String args) { try { int a = args.length(); System.out.println("a = " + a); int b = 23 / a; checkArray(); } catch (ArithmeticException e) { System.out.println("ArithmeticException: " + e.getMessage()); } } }

Возбуждение исключений throw

Программа может явно вызывать исключение, используя оператор throw . После выполнения оператора throw процесс выполнения программы приостанавливается и последующие операторы не выполняются. JVM просматривает ближайший блоки try ... catch , соответствующий типу исключения, для "передачи управления". Если подходящий блок не будет найден, то обработчик исключений остановит программу и "распечатает" при этом состояние стека вызовов.

Пример исключения, в котором сначала создается объект-исключение, затем оператор throw возбуждает исключительную ситуацию, после чего то же исключение возбуждается повторно - на этот раз уже кодом перехватившего его в первый раз раздела catch .

Class TestThrow { static void method() { try { throw new NullPointerException("Exception in method"); } catch (NullPointerException e) { System.out.println(e.getMessage()); throw e; } } public static void main(String args) { try { method(); } catch(NullPointerException e) { System.out.println("Catch inside main: " + e.getMessage()); } } }

Результат выполнения программы приведен ниже.

Exception in method Catch inside main: Exception in method

Объявление об исключении throws

Если метод может возбуждать исключения, которые сам не обрабатывает, то он должен объявить об этом, чтобы вызывающие его другие методы могли защитить себя от этих исключений. Для задания списка исключений, которые могут возбуждаться методом, используется ключевое слово throws .

Если метод в явном виде (т.е. с помощью оператора throw ) возбуждает исключение, тип класса исключений должен быть указан в операторе throws в объявлении этого метода. Принимая данное положение во внимание синтаксис определения метода должен быть описан следующим образом:

Public class TestThrow { static void method() throws IllegalAccessException { try { System.out.println("inside method"); throw new IllegalAccessException ("Exception in method"); } catch (NullPointerException e) { System.out.println(e.getMessage()); } } public static void main(String args) { try { method(); } catch(IllegalAccessException e) { System.out.println("Catch inside main: " + e.getMessage()); } } }

Результат работы примера:

Inside method Catch inside main: Exception in method

Ключевое слово finally

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

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

Ниже приведен пример класса с двумя методами, завершение которых происходит по разным причинам, но в обоих перед выходом выполняется код раздела finally .

Public class TestFinally { static void methodA() { try { System.out.println("inside methodA"); throw new RuntimeException("Exception in methodA"); } finally { System.out.println("finally inside methodA"); } } static void methodB() { try { System.out.println("inside methodB"); return; } finally { System.out.println("finally inside methodB"); } } public static void main(String args) { try { methodA(); } catch (Exception e) { System.out.println("Catch exception iinside main"); } methodB(); } }

В тестовом примере в методе methodA возбуждается исключение. Но перед преждевременным выходом из блока try , выполняется раздел finally . Во втором методе methodB завершается работа в try -блоке оператором return , но и при этом перед выходом из метода выполняется программный код блока finally . Результат работы тестового примера:

Inside methodA finally inside methodA Catch exception iinside main inside methodB finally inside methodB

Обработка исключений в Java предоставляет исключительно мощный механизм для управления сложными программами. Ключевые слова try, throw, catch позволяют выполнять обработку ошибок и разных нештатных ситуаций в программе.

Наследование исключений

catch - полиморфная конструкция, т.е. catch по типу parent перехватывает исключения любого типа, которые является Parent"ом.

Public class TestException { public static void main(String args) { try { System.err.print("level 0"); throw new RuntimeException(); System.err.print("level 1"); } catch (Exception e) { // catch Exception ПЕРЕХВАТ RuntimeException System.err.print("level 2"); } System.err.println("level 3"); } }

В результате в консоли увидим

Level 0 level 2 level 3

Error и Exception из параллельных веток наследования от Throwable , поэтому catch по одному «брату» не может поймать другого «брата».

Public class TestError { public static void main(String args) { try { System.err.println("level 0"); if (true) { throw new Error(); } System.err.println("level 1"); } catch (Exception e) { System.err.println("level 2"); } System.err.println("level 3"); } }

Результат выполения программы

Level 0 Exception in thread "main" java.lang.Error at TestError.main(TestFinally.java:8)

Множественные исключения

Объявление исключений в методе может быть множественным. Пример:

Import java.io.EOFException; import java.io.FileNotFoundException; public class MultiException { // объявляем исключения public static void main(String args) throws EOFException, FileNotFoundException { if (System.currentTimeMillis() % 2 == 0) { throw new EOFException(); } else { throw new FileNotFoundException(); } } }

Любая программа будет работать стабильно только в том случае, если её исходный код отлажен, и в нем отсутствуют условия, которые могут вызывать непредвиденные ситуации. Процесс отлова возможных сбоев выполняется на стадии программирования. Для этого разработчик учитывает все предполагаемые исходы и пытается ограничить действие ошибки таким образом, чтобы она не смогла нарушить работу программы или привести к её краху.

Когда может понадобиться обработка исключений

В Java исключения могут быть вызваны в результате неправильного ввода данных пользователем, отсутствия необходимого для работы программы ресурса или внезапного отключения сети. Для комфортного использования созданного разработчиком приложения, необходимо контролировать появление внештатных ситуаций. Потребитель не должен ждать завершения работы зависшей программы, терять данные в результате необработанных исключений или просто часто появляющихся сообщений о том, что что-то пошло не так.

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

Что необходимо учитывать? Язык Java обладает своим встроенным функционалом обработки исключений. Конечно же большой процент ошибок отлавливается ещё на стадии компиляции, когда система автоматически сообщит о том, что использовать её дальше невозможно. Но существует и такой вид исключений, который возникает во время работы программы. Разработчик должен суметь предвидеть это и спроектировать код таким образом, чтобы он не вызвал ошибки, а обработал её особым способом или передал управление в другую ветку.

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

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

Самым простым примером, при котором можно получить исключение — это деление. Несмотря на всю его простоту, в выражении, в качестве делителя, может оказаться ноль, что приведёт к ошибке. Хорошо, если его появление можно предсказать ранее и предотвратить. Но такой вариант доступен не всегда, поэтому отлов исключения нужно организовать непосредственно при возникновении «деления на ноль».

В Java механизм обработки перехвата ошибки выглядит так:

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

Простейший пример создания ошибки может выглядеть таким образом:

throw new NullPointerException();

Здесь переменная a проверяется на инициализацию, т.е. не равна ли ссылка на объект null. В случае, если такая ситуация возникла и нужна особая обработка, выбрасывается исключение с помощью throw new NullPointerException().

Немного подробностей о ключевых словах

При работе с исключениями часто приходится использовать ключевые слова Java для обозначения того или иного действия. В данном языке программирования их пять:

  • Try. уже встречалось и означает оно переход в участок кода, который может сгенерировать исключение. Блок ограничивается фигурными скобками {}.
  • Catch. Перехватывает нужный тип исключения и обрабатывает его соответствующим образом.
  • Finally. Данное ключевое слово является дополнительным и служит для выполнения некоего участка кода, который необходим в любом случае, даже если ни одно исключение не перехвачено. Добавляется непосредственно после блока try.
  • Throw — позволяет создавать исключения Java в любом месте кода.
  • Throws — ключевое слово, которое ставится в сигнатуре метода. Оно означает, что последующий код может выбросить исключение Java указанного типа. Такая метка служит сигналом для разработчиков, что нужно иметь в виду — метод может сработать не так, как от него ожидают.

Отлов с помощью try

Выброс в Java исключения, естественно предполагает, что оно будет особым образом обработано. Удобнее всего это сделать, если участок кода отгорожен в некий блок. Который возможно содержит исключение. При выполнении такого кода виртуальная машина найдёт непредвиденную ситуацию, поймёт, что находится в критическом блоке и передаст управление в участок с обработкой.

В Java код заворачивается в специальный блок try, внутри которого может быть сгенерировано исключение. Таким образом, в него помещается сразу несколько непредвиденных ситуаций, которые будут отловлены в одном месте, не расползаясь по коду.

Самый типичный код с блоком обработки выглядит так:

//Здесь будет определён код, который может породить исключение

} catch (Тип_исключения_1 идентификатор_1) {

} catch (Тип_исключения_2 идентификатор_2) {

//Здесь происходит обработка исключения, согласно его типу и условиям;

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

Finally

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

В данном участке представлены несколько блоков catch с придуманными методами отлова исключений. К примеру, код, содержащийся в try порождает непредвиденную ситуацию типа Cold. Тогда в консоль будут выведены выражения «Caught cold!» и «Is that something to cheer about?». То есть блок finally выполняется в любом случае.

На самом деле способ избежать запуска finally существует. Связан он с завершением работы виртуальной машины. Найти, как это реализовать, можно на просторах сети Интернет.

Ключевое слово throw

Throw генерирует исключение. Его синтаксис выглядит так:

throw new NewException();

Здесь создаётся новое исключение с типом NewException(). В качестве типа могут использоваться уже входящие в стандартные библиотеки Java классы и определённые ранее разработчиком собственного производства.

Такая конструкция входит в описание какого-либо метода, вызов которого затем должен происходить в рамках блока try, для того, чтобы была возможность его перехватить.

Ключевое слово throws

Что делать, если в процессе разработки возникла ситуация, когда метод может сгенерировать исключение, но не в состоянии правильно обработать. Для этого в сигнатуре метода указывается слово throws и тип возможного исключения.

Эта метка является своеобразным указателем для клиентских разработчиков о том, что метод не способен обработать своё же исключение. К тому же, если тип ошибки является проверяемым, то компилятор заставит явно это указать.

Try с ресурсами

В Java версии 7 разработчики включили такое важное нововведение, как обработка блока try с ресурсами.

Многие создаваемые объекты в Java, после их использования должны быть закрыты для экономии ресурсов. Раньше приходилось это учитывать и останавливать такие экземпляры вручную. Теперь же в них появился интерфейс AutoClosable. Он помогает автоматически закрывать уже использованные объекты, помещённые в блок try. Благодаря такому подходу писать код стало удобней, в его читаемость значительно повысилась.

Собственные классы исключений Java

Создатели описываемого языка программирования учли многие аспекты при проектировании типов непредвиденных ситуаций. Однако, все варианты исхода событий предотвратить не получится, поэтому в Java реализована возможность определения своих собственных исключений, подходящих именно под нужды конкретного кода.

Простейший способ создания — унаследовать от наиболее подходящего к контексту объекта.

Здесь произошло наследование от Exception, класса, который используется для определения собственных исключений. В MyException имеются два конструктора — один по умолчанию, второй — с аргументом msg типа String.

Затем в public классе FullConstructors реализован метод f, сигнатура которого содержит throws MyException. Это ключевое слово означает, что f может выбросить исключение Java типа MyException. Далее в теле метода производится вывод текстовой информации в консоль и собственно сама генерация MyException, посредством throw.

Второй метод немного отличается от первого тем, что при создании исключения, ему передается строковый параметр, который будет отражён в консоли при отлове. В main видно, что f() и g() помещены в блок проверки try, а ключевое слово catch настроено на отлов MyException. Результатом обработки будет вывод сообщения об ошибке в консоль:

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

Архитектура исключений

Как и все объекты в Java, исключения также наследуются и имеют иерархическую структуру. Корневым элементом всех ошибок, выбрасываемых в этом языке программирования является класс java.lang.Throwable. От него наследуются два вида — Error и Exception.

Error — оповещает о критических ошибках и представляет собой непроверяемые исключения Java. Перехват и обработка таких данных в большинстве случаев происходит на стадии разработки и не нуждается во внедрении в код конечного приложения.

Наиболее часто используемым классом для создания и анализа исключений служит Exception. Который, в свою очередь, делится на несколько веток, в том числе RuntimeException. К RuntimeException относятся исключения времени выполнения, то есть происходящие во время работы программы. Все унаследованные от него классы являются непроверяемыми.

Часто встречаемые исключения

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

  • ArithmeticException. Сюда входят ошибки связанные с арифметическими операциями. Самый яркий пример — деление на ноль.
  • ArrayIndexOutOfBoundsException — обращение к номеру элемента массива, который превышает общую его длину.
  • ArrayStoreException — попытка присвоить элементу массива несовместимого типа.
  • ClassCastException — попытка неправильного приведения одного типа к другому.
  • IllegalArgumentException — использование неправильного аргумента в вызове метода.
  • NegativeArraySizeException — исключение при создании массива отрицательного размера.
  • NullPointerException — неправильное использование ссылки на null.
  • NumberFormatException — возникает при неверном преобразовании строки в число.
  • UnsupportedOperationException — операция не поддерживается.

Данные примеры представляют собой непроверяемые типы исключений Java. А вот так выглядят проверяемые:

  • ClassNotFoundException — класс не обнаружен.
  • IllegalAcccessException — ограничение доступа к классу.
  • InterruptedException — прерывание работы потока.
  • NoSuchFieldException — не существует требуемое поле.

Интерпретация исключений

Говоря о часто встречаемых исключениях нужно отметить, что их интерпретация в ходе разработки, может быть воспринята неправильно. Далее идёт небольшой список, поясняющий более подробно, когда может возникнуть непредвиденная ситуация.

NullPointerException. Самым первым случаем, когда возникает исключение, является обращение к ссылке на объект, которая равна null. Также это распространяется на методы нулевого экземпляра класса. NullPointerException может быть брошен и в случае получения длины массива равной null. Избежать таких ситуаций поможет периодическая проверка объектов на null.

ArrayIndexOutOfBoundsException. Любая программа не может существовать без использования массивов. Соответственно, частое обращение к ним может порождать и ошибки. Возникает исключение, когда разработчик пытается обратиться к элементу массива, который отсутствует в списке индексов. Например, запрашиваемое значение выше длины или меньше нуля. Очень часто появляется в результате того, что счёт в массиве начинается с нуля.

Выводы

Обработка исключений Java — мощный инструмент среды, который значительно облегчает работу программиста и позволяет ему создавать чистый и лишенный ошибок код. От того, насколько плавно и стабильно функционирует приложение, зависит статус и репутация компании-разработчика.

Конечно, в более или менее простых программах отследить внештатные ситуации гораздо проще. А вот в больших автоматизированных комплексах на несколько сотен тысяч строк такое возможно только в результате проведения длительной отладки и тестирования.

За Java исключения, ошибки от которых возникают в некоторых приложениях, отдельные компании предлагают вознаграждение при их нахождении энтузиастами. Особенно ценятся те, которые вызывают нарушение политики безопасности программного комплекса.

Последнее обновление: 30.10.2015

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

В языке Java предусмотрены специальные средства для обработки подобных ситуаций. Одним из таких средств является конструкция try...catch...finally . При возникновении исключения в блоке try управление переходит в блок catch, который может обработать данное исключение. Если такого блока не найдено, то пользователю отображается сообщение о необработанном исключении, а дальнейшее выполнение программы останавливается. И чтобы подобной остановки не произошло, и надо использовать блок try..catch. Например:

Int numbers = new int; numbers=45; System.out.println(numbers);

Так как у нас массив numbers может содержать только 3 элемента, то при выполнении инструкции numbers=45 консоль отобразит исключение, и выполнение программы будет завершено. Теперь попробуем обработать это исключение:

Try{ int numbers = new int; numbers=45; System.out.println(numbers); } catch(Exception ex){ ex.printStackTrace(); } System.out.println("Программа завершена");

При использовании блока try...catch вначале выполняются все инструкции между операторами try и catch. Если в блоке try вдруг возникает исключение, то обычный порядок выполнения останавливается и переходит к инструкции сatch. Поэтому когда выполнение программы дойдет до строки numbers=45; , программа остановится и перейдет к блоку catch

Выражение catch имеет следующий синтаксис: catch (тип_исключения имя_переменной) . В данном случае объявляется переменная ex , которая имеет тип Exception . Но если возникшее исключение не является исключением типа, указанного в инструкции сatch, то оно не обрабатывается, а программа просто зависает или выбрасывает сообщение об ошибке.

Но так как тип Exception является базовым классом для всех исключений, то выражение catch (Exception ex) будет обрабатывать практически все исключения. Обработка же исключения в данном случае сводится к выводу на консоль стека трассировки ошибки с помощью метода printStackTrace() , определенного в классе Exception.

После завершения выполнения блока catch программа продолжает свою работу, выполняя все остальные инструкции после блока catch.

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

Try{ int numbers = new int; numbers=45; System.out.println(numbers); } catch(Exception ex){ ex.printStackTrace(); } finally{ System.out.println("Блок finally"); } System.out.println("Программа завершена");

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

В Java имеется множество различных типов исключений, и мы можем разграничить их обработку, включив дополнительные блоки catch:

Int numbers = new int; try{ numbers=45; numbers=Integer.parseInt("gfd"); } catch(ArrayIndexOutOfBoundsException ex){ System.out.println("Выход за пределы массива"); } catch(NumberFormatException ex){ System.out.println("Ошибка преобразования из строки в число"); }

Если у нас возникает исключение определенного типа, то оно переходит к соответствующему блоку catch.

Оператор throw

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

Package firstapp; import java.util.Scanner; public class FirstApp { public static void main(String args) { try{ Scanner in = new Scanner(System.in); int x = in.nextInt(); if(x>=30){ throw new Exception("Число х должно быть меньше 30"); } } catch(Exception ex){ System.out.println(ex.getMessage()); } System.out.println("Программа завершена"); } }

Здесь для создания объекта исключения используется конструктор класса Exception, в который передается сообщение об исключении. И если число х окажется больше 29, то будет выброшено исключение и управление перейдет к блоку catch.

В блоке catch мы можем получить сообщение об исключении с помощью метода getMessage() .

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

Все исключения в Java являются объектами. Поэтому они могут порождаться не только автоматически при возникновении исключительной ситуации, но и создаваться самим разработчиком.

Иерархия классов исключений:

Исключения делятся на несколько классов, но все они имеют общего предка — класс Throwable. Его потомками являются подклассы Exception и Error.

Исключения (Exceptions) являются результатом проблем в программе, которые в принципе решаемы и предсказуемы. Например, произошло деление на ноль в целых числах.

Ошибки (Errors) представляют собой более серьёзные проблемы, которые, согласно спецификации Java, не следует пытаться обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM. Например, исключения такого рода возникают, если закончилась память, доступная виртуальной машине. Программа дополнительную память всё равно не сможет обеспечить для JVM.

В Java все исключения делятся на три типа: контролируемые исключения (checked) и неконтролируемые исключения (unchecked), к которым относятся ошибки (Errors) и исключения времени выполнения (RuntimeExceptions, потомок класса Exception).

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

Обработка исключения может быть произведена с помощью операторов try…catch, либо передана внешней части программы. Например, метод может передавать возникшие в нём исключения выше по иерархии вызовов, сам его не обрабатывая.

Неконтролируемые исключения не требуют обязательной обработки, однако, при желании, можно обрабатывать исключения класса RuntimeException.

Откомпилируем и запустим такую программу:

Class Main { public static void main(String args) { int a = 4; System.out.println(a/0); } }

В момент запуска на консоль будет выведено следующее сообщение:

Exception in thread "main" java.lang.ArithmeticException: / by zero at Main.main(Main.java:4)

Из сообщения виден класс случившегося исключения — ArithmeticException. Это исключение можно обработать:

Class Main { public static void main(String args) { int a = 4; try { System.out.println(a/0); } catch (ArithmeticException e) { System.out.println("Произошла недопустимая арифметическая операция"); } } }

Теперь вместо стандартного сообщения об ошибке будет выполняться блок catch, параметром которого является объект e соответствующего исключению класса (самому объекту можно давать любое имя, оно потребуется в том случае, если мы пожелаем снова принудительно выбросить это исключение, например, для того, чтобы оно было проверено каким-то ещё обработчиком).

В блок try при этом помещается тот фрагмент программы, где потенциально может возникнуть исключение.

Одному try может соответствовать сразу несколько блоков catch с разными классами исключений.

Import java.util.Scanner; class Main { public static void main(String args) { int m = {-1,0,1}; Scanner sc = new Scanner(System.in); try { int a = sc.nextInt(); m[a] = 4/a; System.out.println(m[a]); } catch (ArithmeticException e) { System.out.println("Произошла недопустимая арифметическая операция"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Обращение по недопустимому индексу массива"); } } }

Если запустив представленную программу, пользователь введётся с клавиатуры 1 или 2, то программа отработает без создания каких-либо исключений.

Если пользователь введёт 0, то возникнет исключение класса ArithmeticException, и оно будет обработано первым блоком catch.

Если пользователь введёт 3, то возникнет исключение класса ArrayIndexOutOfBoundsException (выход за приделы массива), и оно будет обработано вторым блоком catch.

Если пользователь введёт нецелое число, например, 3.14, то возникнет исключение класса InputMismatchException (несоответствие типа вводимого значение), и оно будет выброшено в формате стандартной ошибки, поскольку его мы никак не обрабатывали.

Можно, однако, добавить обработчик для класса Exception, поскольку этот класс родительский для всех остальных контролируемых исключений, то он будет перехватывать любые из них (в том числе, и InputMismatchException).

Import java.util.Scanner; class Main { public static void main(String args) { int m = {-1,0,1}; int a = 1; Scanner sc = new Scanner(System.in); try { a = sc.nextInt(); m = 4/a; System.out.println(m[a]); } catch (ArithmeticException e) { System.out.println("Произошла недопустимая арифметическая операция"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Обращение по недопустимому индексу массива"); } catch (Exception e) { System.out.println("Произошло ещё какое-то исключение"); } } }

Поскольку исключения построены на иерархии классов и подклассов, то сначала надо пытаться обработать более частные исключения и лишь затем более общие. То есть поставив первым (а не третьим) блок с обработкой исключения класса Exception, мы бы никогда не увидели никаких сообщений об ошибке, кроме «Произошло ещё какое-то исключение» (все исключения перехватились бы сразу этим блоком и не доходили бы до остальных).

Необязательным добавлением к блокам try…catch может быть блок finally. Помещенные в него команды будут выполняться в любом случае, вне зависимости от того, произошло ли исключение или нет. При том, что при возникновении необработанного исключения оставшаяся после генерации этого исключения часть программы — не выполняется. Например, если исключение возникло в процессе каких-то длительных вычислений, в блоке finally можно показать или сохранить промежуточные результаты.

2010, Алексей Николаевич Костин. Кафедра ТИДМ математического факультета МПГУ.

Эта статья посвящается очень важному вопросу программирования - исключительным ситуациям и ошибкам (exceptions and errors).

В языке Java исключения (Exceptions) и ошибки (Errors) являются объектами. Когда метод вызывает, еще говорят "бросает" от слова "throws", исключительную ситуацию, он на самом деле работает с объектом. Но такое происходит не с любыми объектами, а только с теми, которые наследуются от Throwable.

Упрощенную диаграмму классов ошибок и исключительний вы можете увидеть на следующем рисунке:

RuntimeException, Error и их наследников еще называют unchecked exception , а всех остальных наследников класса Exception - checked exception .

Checked Exception обязывает пользователя обработать ее (использую конструкцию try-catch) или же отдать на откуп обрамляющим методам, в таком случае к декларации метода, который бросает проверяемое (checked) исключение, дописывают конструкцию throws , например:

Public Date parse(String source) throws ParseException { ... }

К unchecked исключениям относятся, например, NullPointerException, ArrayIndexOutOfBoundsException, ClassCastExcpetion и так далее. Это те ошибки, которые могут возникнут практически в любом методе. Несомненно, описывать каждый метод как тот, который бросает все эти исключения, было бы глупо.

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

Чаще всего Exceptions бросаются при нарушении контракта метода. Контракт (contract) - это негласное соглашение между создателем метода (метод сделает и/или вернет именно то, что надо) и пользователем метода (на вход метода будут передаваться значения из множества допустимых).

Нарушение контракта со стороны создателя метода - это, например, что-нибудь на подобии MethodNotImplementedYetException:).

Пользователь метода может нарушить контракт, например, таким способом: на вход Integer.parseInt(String) подать строку с буквами и по заслугам получить NumberFormatException.

Часто при реализации веб-сервисов первыми строками методов я пишу конструкции вида:

Public Contract getContractById(String id) { if (id == null) throw new NullPointerException("id is null"); ... }

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

2. А что собственно бросать? . Выбор не то чтобы сильно велик, но и не однозначен: checked, unchecked (runtime), unchecked (error).

Сразу скажу, в подавляющем большинстве случаев Error вам не понадобится. Это в основном критические ошибки (например, StackOverflowError), с которыми пусть работает JVM.

Checked Exceptions, как было написано выше, заставляет программиста-пользователя написать код для ее обработки или же описать метод как "вызывающий исключительную ситуацию".

С unchecked exception можно поступить по-разному. В случае с такими ошибками, пользователь сам решает, будет он обрабатывать эту ошибку, или же нет (компилятор не заставляет это делать).

Можно написать следующее простое правило: если некоторый набор входящих в метод данных может привести к нарушению контракта, и вы считаете, что программисту-пользователю важно разобраться с этим (и что он сможет это сделать), описывайте метод с конструкцией throws, иначе бросайте unchecked exception.

3. Ну и как это обрабатывать? Обрабатывать ошибку лучше там, где она возникла. Если в данном фрагменте кода нет возможности принять решение, что делать с исключением, его нужно бросать дальше, пока не найдется нужный обработчик, либо поток выполнения программы не вылетит совсем.

Вы наверняка знаете, что обработка исключений происходит с помощью блока try-catch-finally. Сразу скажу вам такую вещь: никогда не используйте пустой catch блок! . Выглядит этот ужас так:

Try { ... } catch(Exception e) { }

Если Вы уверены, что исключения в блоке try не возникнет никогда, напишите комментарий, как например в этом фрагменте кода:

StringReader reader = new StringReader("qwerty"); try { reader.read(); } catch (IOException e) { /* cannot happen */ }

Если же исключение в принципе может возникнуть, но только действительно в "исключительной ситуации" когда с ним ничего уже сделать будет нельзя, лучше оберните его в RuntimeException и пробросьте наверх, например:

String siteUrl = ...; ... URL url; try { url = new URL(siteUrl); } catch (MalformedURLException e) { throw new RuntimeException(e); }

Скорее всего ошибка здесь может возникнуть только при неправильной конфигурации приложения, например, siteUrl читается из конфигурационного файла и кто-то допустил опечатку при указании адреса сайта. Без исправления конфига и перезапуска приложения тут ничего поделать нельзя, так что RuntimeException вполне оправдан.

4. Зачем нужно все это делать? А почему бы и нет:). Если серьезно - правильное использование Exceptions и корректная их обработка сделают код более понятным, гибким, структурированным и возможным для повторного использования.

Updated 28.08.2009: Хочу показать вам несколько интересных моментов, которые касаются исключений и блоков try-catch-finally.

Можно ли сделать так, чтобы блок finally не выполнился? Можно:

Public class Test1 { public static void main(String args) { try { throw new RuntimeException(); } catch (Exception e) { System.exit(0); } finally { System.out.println("Please, let me print this."); } } }

Ну и еще одна интересная вещь. Какое исключение будет выброшено из метода:

Public class Test { public static void main(String args) { try { throw new NullPointerException(); } catch (NullPointerException e) { throw e; } finally { throw new IllegalStateException(); } } }

Правильный ответ - IllegalStateException. Не смотря на то, что в блоке catch происходит повторный "выброс" NPE, после него выполняется блок finally, который перехватывает ход выполнения программы и бросает исключение IllegalStateException.

Жду ваших вопросов и комментариев.



Комментариев: 2

Но ни в коем случае нельзя использовать исключительные ситуации для реализации частей бизнесс-логики.

Я бы сказал не стоит использовать исключения для управления flow алгоритма (типа выхода из вложенного цикла и все такое). А вот для реализации частей бизнес-логики как раз таки иногда и нужно, но только те которые checked. Классический пример - снятие денег со счета, когда выкидывается исключение, говорящее о том что денюшок то не хватает. Вот тут как раз клиенту метода снятия надо реализовать бизнес-логику по обработке такой ситуации.

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

P.S. Ну конечно бывают ситуации исключительные. Но это не более 20%.

lucker wrote: Ну а в тех случаях когда речь идет о нарушении контракта метода (не бизнес-ограничений) не надо и задумываться - только unchecked, и ловить их должен код, находящийся в самом начале потока, и ничего кроме логирования информации об ошибки с ними сделать он не может, да и не должен.

такой подход приведет к тому что код в начале потока будет в 80% случаев получать исключение неизвестно по какой причине и как возникшее, например стэк трэйс на вэб странице, ни один из модулей который будет использовать такой метод и понятия не будет иметь о том что гдето в глубине ктото выбросил анчекед исключение и результат его может быть самым удручающим, например: если такой метод кто-то начнет использовать в цикле то из-за одноко неправильного объекта переданого в метод, который выкинет анчекед из-за нарушения контракта, вместо скажем ожидаемого списка из нескольких строк на экране клиент не увидит ни одного, а мог бы просто увидеть на один меньше (тот который послужил причиной исключения) А произойдет это потому что девелопер который будет писать вывод понятия не будет иметь что данный конкретный метод может просто выкинуть тот или иной рантайм, если такой метод в случае нарушения контракта выбросит чекед исключение то пользователь метода будет иметь информацию и решит что ему делать с данным исключением, код будет иметь более предсказуемое поведение а следственно станет более професиональным.

Использование анчекед исключений где попало как раз и делает код мало пригодным к использованию, поскольку как раз скрывает тот самый контракт метода, когда пользователь метода понятия не имеет какие исключения могут вылететь из данного метода. А вылетать такие исключения как раз любят на продакшене во время демонстрации системы заказчику.

Статья кстати выглядит немного поверхностной, основная проблема в написании комерческого софта именно в том чтобы сделать его реюзабельным и легко изменяемым к постоянно меняющимся требованиям при этом надежным, предсказуемым и устойчивым. Поэтому приведенная цитата о том что 90% кода обрабатывает исключения а 10% делает работу - это цитата человека который врядли писал что-то что потом используют другие… поскольку работа програмы включает в себя и надежность и предсказуемость и повторяемость результатов и масштабируемость и возможность использовать код еще кем-то, все то что отличает комерческий код профи от кода студента.



Рекомендуем почитать

Наверх