Java как определить название метода выброшена exception
Перейти к содержимому

Java как определить название метода выброшена exception

  • автор:

Java. TDD. Как проверить в юнит тесте что исключение было выброшено?

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

> else

И так далее. Подскажите есть ли у junit или еще у какой-то работающей с ней библиотеки, такой метод? И если можно с кратким примером синтаксиса. Попробовал использовать Rule но что-то идет не так:

@Rule public ExpectedException exception = ExpectedException.none(); @Test public void whenThen() throws ImposableMoveException < for (int i = 0; i != Board.desc.length; i++) < Board.desc[i][i] = new Place(new Cell(i, i), ""); >Board board = new Board(); Cell cell = new Cell(5, 5); board.move(new Cell(0, 0), cell); exception.expect(ImposableMoveException.class); exception.expectMessage(containsString("Error move")); > 

А ошибка теста указывает на 2 строки эту:

throw new ImposableMoveException("Error move"); 
board.move(new Cell(0, 0), cell); 

Ну собственно понятно что первая исключение кидает а вторая его провоцирует, вроде все работать должно подскажите пожалуйста где я ошибаюсь. Я что-то не понимаю в идее работы метода expect() и expectMessage()

  • java
  • test-driven-development

Правильное использование исключений в Java

Доброго времени суток, уважаемый Хабр.
Я хотел бы рассказать, как правильно нужно использовать исключения в Java. Частично этот материал рассматривается на просторах интернета, а также рассматривается немного в книге J.Bloch Effective Java. Речь пойдет о использовании проверенных и непроверенных (checked/unchecked) исключениях. Статья будет полезна новичкам, т.к. вначале не всегда ясно, как правильно нужно пользоваться исключениями.

Иерархия исключений в Java представлена следующим образом: родительский класс для всех Throwable. От него унаследовано 2 класса: Exception и Error. От класса Exception унаследован еще RuntimeException.
Error – критические ошибки, который могут возникнуть в системе (например, StackOverflowError ). Как правило обрабатывает их система. Если они возникают, то приложение закрывается, так как при данной ситуации работа не может быть продолжена.

Exception – это проверенные исключения. Это значит, что если метод бросает исключение, которое унаследовано от Exception (напр. IOException), то этот метод должен быть обязательно заключен в блок try-catch. Сам метод, который бросает исключение, должен в сигнатуре содержать конструкцию throws. Проверенные (checked) исключения означают, что исключение можно было предвидеть и, соответственно, оно должно быть обработано, работа приложения должна быть продолжена. Пример такого исключения — это попытка создать новый файл, который уже существует (IOException). В данному случае, работа приложения должна быть продолжена и пользователь должен получить уведомление, по какой причине файл не может быть создан.
Например:

try < File.createTempFile("prefix", ""); >catch (IOException e) < // Handle IOException >/** * Creates an empty file in the default temporary-file directory * any exceptions will be ignored. This is typically used in finally blocks. * @param prefix * @param suffix * @throws IOException - If a file could not be created */ public static File createTempFile(String prefix, String suffix) throws IOException

В данном примере можно увидеть, что метод createTempFile может выбрасывать IOException, когда файл не может быть создан. И это исключение должно быть обработано соответственно. Если попытаться вызвать этот метод вне блока try-catch, то компилятор выдаст ошибку и будет предложено 2 варианта исправления: окружить метод блоком try-catch или метод, внутри которого вызывается File.createTempFile, должен выбрасывать исключение IOException (чтобы передать его на верхний уровень для обработки).

RuntimeException – это непроверенные исключения. Они возникают во время выполнения приложения. К таким исключениям относится, например, NullPointerException. Они не требуют обязательного заключения в блок try-catch. Когда RuntimeException возникает, это свидетельствует о ошибке, допущенной программистом (неинициализированный объект, выход за пределы массива и т.д.). Поэтому данное исключение не нужно обрабатывать, а нужно исправлять ошибку в коде, чтобы исключение вновь не возникало.
Ниже приведен пример, как правильно работать с RuntimeException:

/** * Calculates rectangle square * @param aRect Rectangle * @throws NullPointerException if Rectangle is null */ public int calculateSquare(Rectange rect) < if (rect == null) < throw new NullPointerException(“Rectangle can't be null”); >// calculate rectangle square int rectWidth = rect .getWidth(); int rectHeight = rect .getHeight(); int square rectWidth * rectHeight(); return square; > . Rectangle rect = new Rectangle(); int square = calculateSquare(rect); …. 

В данном примере метод принимает объект класса Rectangle. В описании метода содержится строка @throws, которая описывает исключение, которое может быть выброшено и при каких условиях. Однако, сигнатура метода не содержит конструкции throws. Это значит, что при вызове метода его не нужно оборачивать блоком try-catch. А программист должен не допустить передачи в метод неинициализированного объекта.

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

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

Собирался написать статью про один интересный паттерн обработки исключений и попробовал найти в интернете описание чего-то похожего. Вместо этого нашел интересную статью: Exception-Handling Antipatterns (April 6, 2006 Tim McCune). Статья показалась заслуживающей перевода. Ниже перевод упомянутой статьи.

Должны ли вы бросать исключение или возвращать null? Должны ли вы использовать checked или unchecked исключения? Для многих, от новичков до средних разработчиков, обработка исключений имеет тенденцию оставляться «на потом». Их типичный паттерн — элементарный try/catch/printStackTrace(). Когда они пытаются быть более креативными, они обычно натыкаются на один или более распространённых антипаттернов обработки исключений.

Концепция антипаттерна? стала популярной в сообществе разработчиков программного обеспечения с выходом в 1998 книги «AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis». Антипаттерн иллюстрирует пример неудачных подходов, приводящих к возникновению программных ошибок. Антипаттерн описывает обобщённую форму плохого паттерна, определяет его отрицательные последствия, предписывает средство «лечения» и помогает ввести общеупотребимый словарь терминов, присваивая каждому паттерну имя.

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

Одна из наиболее важных концепций про обработку исключений — понимать, что есть три базовых класса исключений в Java: checked exceptions, unchecked exceptions, errors.

Checked exceptions — исключения, которые должны быть описаны в throws части метода.

void func() throws SomeCheckedException

Checked exceptions наследуются от класса Exception и предполагаются «бросающимися в глаза» исключениями. Checked exception означает ожидаемую проблему, которая может возникнуть в процессе нормальной работы системы. Некоторые примеры — проблемы коммуникации с внешними системами и проблемы с пользовательским вводом. Заметьте, что термин «пользовательский ввод» может относиться как пользовательскому интерфейсу, так и к параметрам, которые другой разработчик передает в ваш API. Часто, верная реакция на checked exception попытаться повторить операцию позже или попросить пользователя изменить ввод.

Unchecked exceptions — исключения, которые не нуждаются в описании в throws части метода.

void func()

Они наследуются от RuntimeException. Unchecked исключение обычно означает неожиданную проблему, возникшую вероятно из-за ошибки в коде. Наиболее общий пример — NullPointerException. Есть много базовых исключений в JDK, являющихся checked исключениями, но они таковыми быть не должны: IllegalAccessException или NoSuchMethodException. Unchecked исключение скорее всего не требует повтора операции или восстановительных действий. Корректной реакцией на них — обычно ничего не делать и позволить им быть проброшенными через стек выполнения вашей программы. Это и есть причина, по которой unchecked исключения не должны описываться в throws части описания метода. Где-то, на самом верхнем уровне стека выполнения, исключение должно быть записано в лог.

Errors (Ошибки) — серьезные проблемы, которые наиболее вероятно не могут быть исправлены. Некоторые примеры: OutOfMemoryError, LinkageError, StackOverflowError.

Реализация собственных исключений

Большинство пакетов (packages) и(или) системных компонентов должны содержать один или более собственных (custom) классов исключений. Есть два основных применения собственным исключениям. Первое, ваш код просто кидает собственное custom исключение когда что-то идет неправильно. К примеру:

throw new MyObjectNotFoundException("Couldn't find object id " + id);

Второе применение, ваш код оборачивает и бросает другое исключение:

catch (NoSuchMethodException e)

Оборачивая исключение вы можете предоставить дополнительную информацию пользователю, добавляя ваше собственное сообщение (как в примере выше), при этом сохраняя стек и сообщение оригинального исключения. Это также позволяет вам скрыть детали реализации вашего кода, что является наиболее важной причиной оборачивания исключений. Например посмотрите Hibernate API. Несмотря на то, что он активно пользуется JDBC и в большинстве операций, которые он выполняет, могут возникнуть SQLException, Hibernate не бросает SQLException нигде в его API. Вместо этого он оборачивает эти исключения в различные дочерние классы (subclasses) HibernateException. Использование такого подхода позволяет вам изменять нижележащую реализацию вашего модуля без модификации публичного API.

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

EJB 2

Разработчики спецификации EJB 2 решили использовать отличие между checked и unchecked исключениями для определения должна ли быть откачена активная транзакция. Если EJB бросает checked исключение, транзакция по-прежнему завершается нормально. Если EJB бросает unchecked исключение, транзакция откатывается. Вы в большинстве случаев хотите, чтобы в случае исключения активная транзакция была откачена. Работая с EJB 2 нужно помнить об этой особенности его обработки исключений.

EJB 3

Чтобы как-то облегчить вышеописанную проблему, EJB 3 добавляет аннотацию ApplicationException c rollback элементом. Это дает вам явный контроль над откатом транзации при исключении (не зависимо от типа checked или unchecked). Например:

@ApplicationException(rollback=true) public class FooException extends Exception .

Message-Driven Beans

При работе с Message-driven beans (MDB) помните, что откат активной транзакции также помещает сообщение, которое вы в данный момент обрабатываете обратно в очередь. Сообщение будет доставлено другому MDB, возможно на другой машине, если ваши сервера приложений работают в кластере. Это будет продолжаться пока счетчик повторов не достигнет значения retry limit сервера, после чего сообщение будет помещено в «dead letter queue». Если ваш MDB желает избежать репроцессинга (например, потому, что это вызывает «дорогостоящие» операции), он может вызвать getJMSRedelivered() на сообщение и если оно было уже redelivered, просто выбросить его.

Логирование

Когда у вас в коде возникает исключение, ваш код должен либо обработать его, либо пробросить его «дальше», либо обернуть его в свое исключение и пробросить дальше, либо логировать его. Если ваш код может программно обработать исключение (например повторить операцию в случае с проблемами в сети), тогда он должен сделать это. Если ваш код не может обработать ситуацию, тогда, в общем случае, код должен либо пробросить исключение выше (для unchecked исключений) либо обернуть его и пробросить для checked. Однако, кто-то другой где-то в самом верху стека вызовов, должен в конце-концов сделать запись в лог об исключении, если оно не было обработано программно. Этот логирующий код должен быть типично как можно более высоко в стеке выполнения, насколько это возможно.

Например в случае с MDB для логирования прекрасно подходит onMessage() метод MDB, а также main() метод класса. При получении исключения вы должны его должным образом записать в лог.

Хотя JDK имеет встроенный (стандартный) java.util.logging package, проект Log4j от Apache остается общеупотребимой альтернативой. Apache также предлагает Commons Logging проект, который работает как промежуточный уровень, позволяющий вам переключаться на различные реализации логеров в «подключаемом» стиле. Все вышеупомянутые системы логирования имеют одинаковые базовые уровни для сообщений:

  • FATAL: Должен использоваться в экстремальных случаях, когда немедленное внимание требуется. Этот уровень может быть подходящим для засылки SMS инженеру поддержки
  • ERROR: Указывает на ошибку или на ошибочное состояние, но не обязательно приводящее систему к остановке. Этот уровень явдяется подходящим для генерации email на список рассылки, где он может быть зарегистрирован как ошибка инженером поддержки.
  • WARN: Предупреждение. Не обязательно ошибка. Но кто-то вероятно захочет о ней однажды узнать. Если кто-то просматривает лог файл, скорее всего этот кто-то захочет увидеть все возникшие предупреждения.
  • INFO: Используется для базовой, высокоуровневой диагностической информации. Наиболее часто хорошая привычка генерировать такие сообщения перед и после относительно длительно выполняющихся участков кода для ответа на вопрос «Чем приложение занято?». Сообщения на этом уровне должны избегать излишней многословности.
    DEBUG: Используется для низкоуровневой помощи отладке.

catch (NoSuchMethodException e)
catch (NoSuchMethodException e)
catch (NoSuchMethodException e)

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

public void foo() throws Exception 

Это неряшливо, полностью разрушает принципы использования checked exception. Это говорит вызвавшему ваш метод «что-то случилось неправильное в моем методе». Никогда так не делайте. Укажите определенные checked исключения, которые ваш метод может бросить. Если их несколько, вы вероятно обернете из в ваше собственное исключение (см. Антипаттерн «Бросание Kitchen Sink») Throwing the Kitchen Sink

public void foo() throws MyException, AnotherException, SomeOtherException, YetAnotherException 

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

try < foo(); >catch (Exception e)

Это обычно неверно и неряшливо. Перехватывайте конкретные исключения, которые могут быть выброшеныю Проблема с таким перехватом исключения в том, что если разработчик метода foo() позже добавит новое исключение, которое потребует иного алгоритма обработки, с таким кодом вы просто никогда не узнаете, что ваш код требует изменения. (Или узнаете об этом слишком поздно). Destructive Wrapping

catch (NoSuchMethodException e) Такой подход теряет стек-трейс оригинального исключения и всегда неверен. Логируем и возвращаем Null catch (NoSuchMethodException e)
catch (NoSuchMethodException e) < e.printStackTrace(); return null; >// Man I hate this one

Хотя не всегда неверно, это обычно плохо. Вместо возврата null, бросайте исключение и позвольте вызывающему принимать решение, что с эти делать. Вы должны возвращать null в нормальном (не исключительном) случае (например, «Этот мето возвращает null, если искомая строка не найдена). Catch and Ignore

catch (NoSuchMethodException e)

Этот пример коварный. Не только возвращает null вместо перехвата и проброса исключения, но и полностью «глотает» исключение не оставляя никакой информации о его возникновении. Throw из Finally-блока

try < blah(); >finally

Этот код отличный, до тех пор, пока cleanUp() никогда не бросает исключения. В вышеприведенном примере, если blah() бросает исключение, и затем в блоке finally cleanUp() бросает исключение, это второе исключение будет брошено, а первое исходное исключение будет потеряно навеки. Если ваш код, находящийся в блоке finally может бросить исключение, убедитесь, что обработали его или записали в лог. Никогда не позволяйте ему быть выброшенным за пределы finally блока. Многострочные лог-сообщения

LOG.debug("Using cache policy A"); LOG.debug("Using retry policy B");

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

LOG.debug("Using cache policy A, using retry policy B");

Использование многострочных сообщений с множественными обращениями к логгеру, может выглядеть прекрасно в вашем тестовом окружении. Но когда ваш код будет писать в файл на сервере приложений с 500 тредами работающими параллельно, пишушими в один лог файл, ваши 2 строчки могут оказаться в лог файле разделенными на 1000 строчек, несмотря на то, что они написаны на двух последовательных строчках в коде. Неподдерживаемая операция (Unsupported Operation) возвращающая null

public String foo() < // Not supported in this implementation. return null; >

Когда вы реализуете обстрактный базовый класс, и вы делаете просто пример для наследущих классов, для последующего переопределения, это отлично. Однако, если это не тот случай, вы должны бросить UnsupportedOperationException вместо возврата null. Это делает более очевиным обращающемуся к коду, почему программа не работает, вместо выдумывания почему программа выбрасывает неожиданный NullPointerException. Игнорирование InterruptedException

while (true) < try < Thread.sleep(100000); >catch (InterruptedException e) <> doSomethingCool(); >

InterruptedException это знак вам, что ваш код должен завершить то, чем он сейчас занят. Наиболее общий случай для прерывания тредов — таймаут текущей транзакции или остановка тредового пула. Вместо игнорирования InterrupdedException, ваш код должен сделать все возможное для завершение того, чем он сейчас занимается и завершения текущего треда выполнения. Т.о. скорректированный пример:

while (true) < try < Thread.sleep(100000); >catch (InterruptedException e) < break; >doSomethingCool(); >

Доверие getCause()

catch (MyException e) < if (e.getCause() instanceof FooException) < .
  • Указываем исключение в параметрах у метода
  • Return from finally block in Java
  • Другие статьи о Java в разделе Java…

Java как определить название метода выброшена exception

[an error occurred while processing this directive]

Конспект лекций по Java. Занятие 13

[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive] (none)
[an error occurred while processing this directive] ([an error occurred while processing this directive] В.Фесюнов [an error occurred while processing this directive])

[an error occurred while processing this directive](none)

  • Обработка исключительных ситуаций (Exceptions)
    • Поведение программы при возникновении исключения
    • Структура и использование блока перехвата исключений.
    • Классы исключительных ситуаций
    • Механизм контроля перехвата исключений

    Обработка исключительных ситуаций (Exceptions)

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

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

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

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

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

    Существуют два варианта генерации исключения — автоматическая генерация (примеры — NullPointerException, ArithmeticException, ClassCastException) и явная программная генерация.

    Автоматическая генерация. Если Java-машина обнаруживает некоторую ошибку, например, деление на ноль или ошибку приведения типов, то она сама генерирует соответствующее исключение.

    Программная генерация. Сгенерировать исключение можно явно при помощи операции throw. Это выглядит примерно так:

    throw new IllegalArgumentException("Параметр k должен быть положительным числом");

    Т.е. при генерации исключения мы порождаем объект класса нужной нам исключительной ситуации.

    Поведение программы при возникновении исключения

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

    • Если, например, поставить оператор сразу за оператором throw, то такой оператор никогда не выполнится.

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

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

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

    Структура и использование блока перехвата исключений.

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

    try < . . . >catch ( Имя_класса_исключения ex ) < . . . >catch .

    Здесь внутри скобок после try находится блок, в котором мы собираемся ловить исключения. Т.е. исключение, возникшее в этом блоке, будет перехвачен данным try-catch-блоком (далее мы уточним, что не все возможные исключения перехватываются, а только указанные в конструкциях catch). В блоке catch , в его заголовке, указывается имя класса исключения, для которого предназначен этот блок. Блок будет срабатывать только при возникновении данного исключения или исключения, порожденного от данного (напомним, исключения — это классы, т.е. для них допустимо наследование классов).

    Имя ex в заголовке catch-блока — это произвольное имя. Оно служит для именования объекта-исключения и с его помощью можно извлечь информацию о возникшем исключении.

    Блоков catch в данном try-catch-блоке может быть несколько — каждый для своего исключения.

    try < inputFile("data.txt"); calculate(); >catch ( FileNotFoundException fe ) < System.out.println("Файл data.txt не найден"); >catch ( ArithmeticException aex )

    В данном примере в try-блок заключены вызовы двух методов данного класса - inputFile() и calculate(). Если в них возникнет одна из двух исключительных ситуаций — FileNotFoundException или ArithmeticException, то она будут перехвачена и сработает соответствующий catch-блок. Если возникнет какое-то другое исключение, то оно не будет перехвачено и метод, в котором находится приведенный фрагмент (подразумеваем, что других try-catch-блоков в методе нет), будет прерван.

    Рассмотрим еще один пример.

    Пусть у нас описан следующий класс ExceptionExample1.

    public class ExceptionExample1 < double f(double x) < try < return g(x, x-1)/x; >catch (ArithmeticException e ) < System.out.println("Деление на ноль"); return 0; >> double g(double a, double b) < return Math.sin(a)/b; >>

    и где-то выполняется такой фрагмент:

    ExceptionExample1 obj = new ExceptionExample1(); double y = obj.f(0);

    Тогда при выполнении метода f(. ) произойдет исключение при попытке деления на ноль, оно тут же будет перехвачено и в результате метод f(. ) вернет ноль, а на консоль будет выдано сообщение "Деление на ноль".

    Аналогичная ситуация произойдет и в случае

    ExceptionExample1 obj = new ExceptionExample1(); double y = obj.f(1);

    Однако здесь исключение произойдет в методе g(. ). Он прекратит свою работу аварийно, но в методе f(. ) возникшее в g(. ) исключение будет перехвачено и произойдет то же самое, что и в первом случае.

    Если бы f(. ) не содержал try-catch-блока, то выполнение как первого, так и второго фрагмента закончилось бы аварийно. Если при этом сами фрагменты не находятся внутри try-catch-блока, то будет прерван и метод, в котором они находятся, и т.д. Если во всей цепочке вызовов методов не встретится соответствующий try-catch-блок, то прекратится и выполнение всего потока. Если этот поток единственный в программе, то прекратится выполнение всей программы.

    Отметим еще один важный момент. В блоке catch для ArithmeticException стоит оператор

    Если попробовать его убрать, то транслятор выдаст ошибку. Действительно, в данной точке исключение обработано, программа продолжает свою нормальную работу, а, следовательно, метод f(. ) должен вернуть какое-то значение. Т.е. try-catch-блок — это блок, который предназначен для восстановления нормальной работы программы после возникновения исключения.

    Если же мы хотим просто выдать подходящее сообщение и фактически не перехватывать исключение (в данном примере это было бы естественней, чем перехват), то мы можем в блоке перехвата сгенерировать повторно данное исключение, т.е. вместо

    Итак, мы не можем исключить из catch-блока оператор return, но можем заменить его на оператор throw. Это вызовет аварийное завершение метода f(. ), что не требует возврата какого-либо значения. Такая замена с точки зрения транслятора Java будет корректной.

    Классы исключительных ситуаций

    Рассмотрим классы исключительных ситуаций. Это не произвольные классы Java. Они обладают своими особенностями и строятся по определенным правилам.

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

    В стандартной библиотеке Java существует развитая иерархия классов исключительных ситуаций.

    Рассмотрим эту иерархию.

    Рисунок 1.

    Throwable — базовый класс для всех исключительных ситуаций.

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

    Exception — это базовый класс для всех тех исключений, с которыми мы имеем дело в программах. Например, IOException порожден от Exception и может генерироваться различными методами библиотеки ввода/вывода. В свою очередь, для более точной спецификации исключений, от IOException порождены другие классы исключений, такие, например, как FileNotFoundException.

    Особое место занимает RuntimeException . Дело в том, что все Exception, кроме RuntimeException (т.е. все классы порожденные от Exception кроме тех, которые порождены от RuntimeException) обязаны быть перехвачены. Транслятор Java жестко контролирует это. Как именно - рассмотрим чуть позже. А пока разберемся для чего это нужно.

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

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

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

    Другое дело исключение типа ArithmeticException. Оно может произойти при выполнении любой арифметической операции. Если бы пришлось его перехватывать, то вся программа состояла бы из одних try-catch-блоков, а это перебор. Поэтому исключения такого рода унаследованы от класса RuntimeException, что позволяет их не контролировать.

    Это не означает, что их не следует вообще никогда перехватывать. Чаще всего известно, что в данном алгоритме может или не может возникнуть деление на ноль. Если оно может возникнуть, то это можно либо проверить явно, либо поставить try-catch-блок на ArithmeticException.

    Механизм контроля перехвата исключений

    Исключения, которые порождены от Exception, но не от RuntimeException, могут быть сгенерированы только явно операцией throw . Компилятор требует, чтобы в этом случае выполнялось одно из двух условий. Либо такой фрагмент должен находиться внутри try-catch-блока, который может перехватить данное исключение, либо в заголовке того метода, где расположен данный фрагмент, должна стоять конструкция " throws ИмяКлассаИсключения ".

    Например, мы рассматривали фрагмент программы, в котором фигурировал вызов метода

    По смыслу примера этот метод может генерировать FileNotFoundException. Поэтому его описание должно выглядеть примерно так:

    public void inputFile( String fName ) throws FileNotFoundException

    При этом в самом методе перехватывать это исключение не нужно.

    Продолжим рассмотрение механизма контроля перехвата исключений.

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

    Рассмотрим несколько рекомендаций по перехвату исключений и нюансов перехвата исключений, связанных с иерархией наследования классов исключений.

      1. Если некоторое исключение Ex1 имеет подкласс (порожденный класс) Ex2, то в catch-блок для Ex1 перехватит и исключение Ex2. Так в примере

    try < inputFile("data.txt"); calculate(); >catch ( FileNotFoundException fe ) < System.out.println("Файл data.txt не найден"); >catch ( ArithmeticException aex )
    try < . . . >catch ( IOException ex ) < . . . >catch ( FileNotFoundException fex )

    Практическая работа

    Разберем следующую программу.

    import java.util.*; import java.io.*; public class InputTest < public static void main(String args[]) < if( args.length == 0 ) < System.out.println("Нужен параметр вызова: имя файла"); return; >String thisLine; ArrayList list = new ArrayList(); try < BufferedReader fin = new BufferedReader(new InputStreamReader( new FileInputStream(args[0]))); while ((thisLine = fin.readLine()) != null) < System.out.println("==Введена строка:"+thisLine); list.add(thisLine); >> catch (FileNotFoundException e) < System.out.println("failed to open file " + args[0]); System.out.println("Error: " + e); return; >catch (IOException e) < System.out.println("I/O error on file " + args[0]); System.out.println("Error: " + e); return; >Collections.sort(list); System.out.println("Отсортированный список строк:"); Iterator iter = list.iterator(); while( iter.hasNext() ) < String str = (String)iter.next(); System.out.println(str); >> >

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

    BufferedReader fin = new BufferedReader(new InputStreamReader( new FileInputStream(args[0])));

    служит для открытия файла, имя которого содержится в args[0], т.е. в первом параметре вызова программы. В частности, этот фрагмент содержит вызов конструктора FileInputStream(. ). Если мы обратимся к документации по пакету java.io, то обнаружим, что соответствующий конструктор описан так:

    public FileInputStream(String name) throws FileNotFoundException

    Т.е. он содержит в описании фрагмент "throws FileNotFoundException". Если, в свою очередь, перейти по ссылке на описание FileNotFoundException, то мы обнаружим, что FileNotFoundException порожден от Exception , но не от RuntimeException . Это означает, что мы не можем включить данный фрагмент в программу без try-catch-блока, предназначенного для перехвата данного исключения.

    while ((thisLine = fin.readLine()) != null)

    объект fin — это объект класса BufferedReader . Т.е. метод readLine() — это метод данного класса. Он читает и возвращает в качестве результата очередную строку файла.

    Обратившись к документации по методу readLine() класса BufferedReader, мы обнаружим, что этот метод может выбрасывать исключение IOException , которое, как и FileNotFoundException требует перехвата.

    Таким образом, мы должны организовать перехват как FileNotFoundException, так и IOException, что и сделано в нашей программе. Наш try-catch-блок содержит два catch-блока — один для FileNotFoundException, другой для IOException.

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

    Следует также обратить внимание на порядок следования этих catch-блоков - сначала catch-блок для FileNotFoundException, а потом catch-блок для IOException. Это важно потому, что IOException вляется базовым классом для FileNotFoundException, и, если поставить их в обратном порядке, то блок для FileNotFoundException никогда не отработает — FileNotFoundException будет перехвачен блоком для IOException.

    • Мы могли бы оставить один catch-блок для IOException, но тогда сообщение об ошибке было бы недостаточно информативным.

    Блок finally

    Это опциональный (необязательный) блок в конструкции try-catch. Если он присутствует, то он должен быть последним блоком в этой конструкции. Выглядит все это следующим образом:

    try < . . . >catch ( . ) < . . . >catch . . . > finaly

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

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

    Рассмотрим предыдущий пример, улучшенный за счет применения блока finally.

    import java.util.*; import java.io.*; public class InputTest < public static void main(String args[]) < if( args.length == 0 ) < System.out.println("Нужен параметр вызова: имя файла"); return; >String thisLine; ArrayList list = new ArrayList(); BufferedReader fin = null; try < fin = new BufferedReader(new InputStreamReader( new FileInputStream(args[0]))); while ((thisLine = fin.readLine()) != null) < System.out.println("==Введена строка:"+thisLine); list.add(thisLine); >Collections.sort(list); System.out.println("Отсортированный список строк:"); Iterator iter = list.iterator(); while( iter.hasNext() ) < String str = (String)iter.next(); System.out.println(str); >> catch (FileNotFoundException e) < System.out.println("Файл не найден: " + args[0]); System.out.println("Error: " + e); >catch (IOException e) < System.out.println("Ошибка ввода/вывода. Файл " + args[0]); System.out.println("Error: " + e); >finally < if ( fin != null ) try < fin.close(); // . Закрыть файл >catch ( IOException ex ) < System.out.println("Ошибка закрытия файла " + args[0]); System.out.println("Error: " + ex); >fin = null; > > >

    В этом примере оператор fin.close() предназначен для закрытия файла. Он включен в блок finally, а это значит, что закрытие файла произойдет в любом случае. Оператор if в блоке finally нужен потому, что файл может и не открыться (опять же из-за исключения). В этом случае закрывать его не надо.

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

    Методы класса Throwable

    Рассмотрим вопрос, какую информацию можно получить из исключения.

    Основные возможности выдачи информации по исключению заложены в методах класса Throwable, который является базовым классом всей иерархии исключений. Он имеет ряд методов, но мы рассмотрим только наиболее важные из них.

    public String toString()

    Краткое сообщение о исключении.

    public String getMessage()

    Полное сообщение о исключении.

    public void printStackTrace()

    public void printStackTrace(PrintStream s)

    public void printStackTrace(PrintWriter s)

    Выдача в стандартный или указанный поток полной информации о точке возникновения исключения.

    Программа Dlg4.java демонстрирует выдачу информации по исключению. В ней каждый третий клик на любой из 4-х кнопок вызывает генерацию исключения.

    import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Dlg4 extends JFrame < int cnt = 0; Dlg4() < super("Знакомство с исключительными ситуациями"); try < UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); >catch(Exception e) < >setSize(400, 200); Container c = getContentPane(); c.setLayout(new GridLayout(2,2)); JButton btnAry[] = new JButton[4]; for ( int i = 0; i < 4; i++ ) < JPanel pn = new JPanel(); btnAry[i] = new JButton(" Исключение "+(i+1)); pn.add(btnAry[i]); c.add(pn); >btnAry[0].addActionListener(new ActionListener() < public void actionPerformed(ActionEvent e) < try < exceptTest(1); >catch ( Exception ex ) < ex.printStackTrace(); >> >); btnAry[1].addActionListener(new ActionListener() < public void actionPerformed(ActionEvent e) < try < exceptTest(2); >catch ( Exception ex ) < ex.printStackTrace(); >> >); btnAry[2].addActionListener(new ActionListener() < public void actionPerformed(ActionEvent e) < try < exceptTest(3); >catch ( Exception ex ) < ex.printStackTrace(); >> >); btnAry[3].addActionListener(new ActionListener() < public void actionPerformed(ActionEvent e) < try < exceptTest(4); >catch ( Exception ex ) < ex.printStackTrace(); >> >); WindowListener wndCloser = new WindowAdapter() < public void windowClosing(WindowEvent e) < System.exit(0); >>; addWindowListener(wndCloser); setVisible(true); > void exceptTest(int btnNo) throws Exception < if ( ++cnt % 3 == 0 ) throw new Exception("Сгененировано по кнопке N "+btnNo); >public static void main(String[] args) < Dlg4 d = new Dlg4(); >>

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

    java.lang.Exception: Сгененировано по кнопке N 1 at Dlg4.exceptTest(Dlg4.java:76) at Dlg4$1.actionPerformed(Dlg4.java:31) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1450) at и т.д.

    Т.е. метод printStackTrace() выводит достаточно подробную информацию о точке возникновения исключения с указанием имени файла и номера строки файла.

    Домашнее задание

    Модифицировать программу InputTest.java следующим образом.

    1). Она должна требовать не один, а два параметра вызова. Второй параметр — строка для поиска.

    2). После сортировки и распечатки отсортированного списка программа должна искать в списке строку, заданную вторым параметром. Результат поиска нужно отпечатать.

    [an error occurred while processing this directive]
    [an error occurred while processing this directive] (none)

    • Конспект лекций по Java
      • Оглавление
      • Занятие 1. Вступление.
      • Занятие 2. JAVA - объектно-ориентированный язык программирования.
      • Занятие 3. Реализация принципов объектно-ориентированного подхода в Java. Знакомство с документацией.
      • Занятие 4. Операции. Литералы. Операторы.
      • Занятие 5. Массивы. Конструкторы классов. Работа со строками.
      • Занятие 6. Знакомство с библиотеками и пакетами. Первая диалоговая программа.
      • Занятие 7. Наследование классов.
      • Занятие 8. Наследование классов (продолжение). Полиморфизм. Абстрактные классы. Интерфейсы.
      • Занятие 9. Вложенные классы.
      • Занятие 10. Статические вложенные классы. Коллекции объектов.
      • Занятие 11. Коллекции объектов (продолжение). Задание порядка на множестве.
      • Занятие 12. Коллекции - ассоциативные массивы (Map). Сортировка и поиск.
      • Занятия 13. Обработка исключительных ситуаций (Exceptions).
      • Занятие 14. Исключительные ситуации (продолжение). Ввод/вывод.
      • Занятие 15. Ввод/вывод (продолжение).
      • Занятие 16. Интерфейс FileFilter. Класс JFileChooser.
      • Занятие 17. Сериализация объектов.
      • Занятие 18. События и их реализация. Класс JList библиотеки Swing.
      • Занятие 19. Аплеты.
      • Занятие 20. Продолжение знакомства с библиотекой Swing. Класс JTabbedPane.
      • Занятие 21. Идентификация типа во время выполнения.
      • Занятие 22. Идентификация типа во время выполнения (продолжение). Множественные нити выполнения (Multiple threads).
      • Занятие 23. Множественные нити выполнения (продолжение).
      • Занятие 24. Множественные нити выполнения (продолжение).
      • Занятие 25. Технология JavaBeans.
      • Занятие 26. Технология JavaBeans (продолжение).
      < Вернуться на caйт:: Copyright © 1999 — 2010, IT • archiv.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *