Исключения в Java: Правильно ли вы их обрабатываете?
Программирование

Исключения в Java: Правильно ли вы их обрабатываете?

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

Image Credit: Dmitry Nikolaev via Shutterstock.com.

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

  • Невозможность открыть файл конфигурации может быть лучше обработана выше в коде, возможно, с помощью альтернативного расположения файла конфигурации.
  • Доступ к элементу массива вне его границ означает ошибку в программе. Удачной отладки!.
  • Ошибка синтаксического анализа XML должна быть доведена до сведения пользователя, чтобы XML-файл мог быть исправлен.
  • Нехватка памяти в программе (возможно, при обработке большого файла) может быть устранена путем увеличения объема памяти, доступной для java-процесса.

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

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

На рисунке ниже показаны основные части иерархии исключений Java. Базовым классом является Throwable , который подразделяется на Exception и Error. Класс Exception предназначен для условий, связанных с программой, которые приложения могут перехватить в попытке спасти ситуацию. Класс Error , с другой стороны, предназначен для указания серьезных ошибок в среде выполнения Java, которые приложения не должны ловить. Вот некоторые примеры: OutOfMemoryError и StackOverflowError

Исключение Exception бывает двух типов: проверенное и непроверенное. Проверенное исключение должно быть обработано вызывающим кодом. Это правило соблюдается компилятором java. Непроверенное исключение, с другой стороны, может быть распространено вверх по цепочке вызовов без необходимости его явного объявления. Примеры ниже пояснят это

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

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

Следующий код не скомпилируется, поскольку он не делает ни того, ни другого

privatevoidloadFile(String filename)

FileReader in =newFileReader(filename);

Один из способов заставить код компилироваться – обработать исключение (см. ниже)

privatevoidloadFile(String filename)

try
  FileReader in =newFileReader(filename)); {
catch(FileNotFoundException ex) {
  // handle exception here

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

privatevoidloadFile(String filename)throwsjava.io.FileNotFoundException

FileReader in =newFileReader(filename)); {.

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

Непроверенное исключение – это исключение, которое является подклассом RuntimeException и не требует непосредственной обработки или объявления, как указано выше. Например, следующий код приводит к NullPointerException , которое является типом RuntimeException. Однако код компилируется без ошибок, поскольку NullPointerException является непроверенным исключением

privatevoidhandleEvent()

String name =null
if( name.length() >) {.

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

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

В следующем примере кода показано, как обернуть исключение. Метод method_1() бросает SQLException в своем теле. Чтобы код был скомпилирован правильно, исключение должно быть объявлено как брошенное

privatevoidmethod_1()throwsSQLException

thrownewSQLException;

Когда этот метод вызывается из другого метода ( method_2() ), этот метод может перехватить SQLException и обернуть его в непроверенное исключение, поэтому ему не нужно объявлять исключение в сигнатуре своего метода

privatevoidmethod_2()
try
  method_1();
catch(java.sql.SQLException ex) {
  thrownewRuntimeException(ex);

Трассировка стека исключений

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

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

Exception in thread'main'java.lang.IndexOutOfBoundsException: Index:, Size:
at java.util.ArrayList.rangeCheck(ArrayList.java:653
at java.util.ArrayList.get(ArrayList.java:429
at sample.sample1.main(sample1.java:24

Исключение, пойманное здесь, – IndexOutOfBoundsException. Оно содержит дополнительную информацию об ошибке. Трассировка стека содержит 3 стековых кадра, каждый из которых включает информацию о местоположении, как показано на рисунке

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

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

Следующий код записывает сообщение об ошибке в файл журнала

privatevoidloadConfig()
try
  // invoke code which might generate an IOException
catch(java.io.IOException ex) {
  // handle exception here. May be log to a log file.
  log.warning(ex.getMessage());

Когда исключение обернуто внутри другого, вы можете получить обернутое исключение:

Throwable cause = ex.getCause();
log.warning('Underlying cause: '+ cause.getMessage());

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

StringBuilder sbuf =newStringBuilder('Stack Trace: ');
for(StackTraceElement el : ex.getStackTrace()) {
sbuf.append(el.getClassName() +'.'+ el.getMethodName()).append('
');

log.warning(sbuf.toString());

Или, может быть, зарегистрировать исключение и отбросить его?

try

catch(java.io.IOException ex) {
log.warning(ex.getMessage());
throwex;

Класс Exception предоставляет метод printStackTrace() , который может вывести трассировку стека в ваш собственный PrintStream (или PrintWriter )

try

catch(java.io.IOException ex) {
PrintStream out =.;
out.println(ex.getMessage());
ex.printStackTrace(out);

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

try
// throws some exceptions here
catch(java.io.IOException ex) {
// IOException specific handling here
catch(java.sql.SQLException ex) {
// SQLException specific handling here

Чтобы поймать несколько типов исключений, но использовать один и тот же код обработки, вы можете объявить блок catch с несколькими типами следующим образом:

try
// throws some exceptions here
catch(java.io.IOException | java.sql.SQLException ex) {
// IOException and SQLException specific handling here
catch(SAXException ex) {
// SAXException specific handling here

Очистка ресурсов с помощью Finally

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

InputStream in =null
try

in =newFileInputStream(filename);

catch(java.io.IOException ex) {
log.warning(ex.getMessage());
finally
// code here is executed on exiting the try block,
// whether normally or due to an exception
if( in !=null) in.close();

Блок ‘Попытка с ресурсами’

В Java 1. 7 появилась конструкция try-with-resources , которая облегчает очистку ресурсов. Она выглядит следующим образом:

try( InputStream in =newFileInputStream(.) ) {
// code which uses the InputStream.

Когда код выходит из блока (чисто или из-за исключения), переменная InputStream автоматически очищается

Очистка нескольких ресурсов путем объявления их всех в голове блока

try( InputStream in =newFileInputStream(.);
Connection con =.; ) {
// code which uses the InputStream and the Connection.

Любой объект, класс которого реализует интерфейс AutoCloseable , может быть очищен таким образом. Следующий класс выполняет определенную очистку в методе close()

publicclassMyClassimplementsAutoCloseable
publicvoidclose()
  // cleanup code here

Используйте экземпляр этого класса в блоке try-with-resources

try( MyClass obj =newMyClass(.) ) {
// code which uses the MyClass object.

Некоторые часто встречающиеся исключения

Давайте теперь рассмотрим некоторые часто встречающиеся исключения

  • IndexOutOfBoundsException (непроверенное): указывает, что индекс элемента, к которому осуществляется доступ, выходит за границы массива, строки и т.д.
  • SQLException (проверено): выбрасывается из-за ошибки базы данных.
  • IOException (проверено): ошибка доступа к файлу или ошибки, связанные с вводом и выводом данных.
  • InterruptedException (проверено): выбрасывается, когда выполнение потока прерывается.
  • SAXException (проверено): выбрасывается из-за ошибок разбора XML.
  • NullPointerException (непроверенное): использование null там, где требуется объект.

Завершение

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

Есть ли у вас истории из жизни, связанные с исключениями? Если да, расскажите нам об этом в разделе комментариев ниже

Image Credit: Dmitry Nikolaev via Shutterstock.com

Об авторе

Алексей Белоусов

Привет, меня зовут Филипп. Я фрилансер энтузиаст . В свободное время занимаюсь переводом статей и пишу о потребительских технологиях для широкого круга изданий , не переставая питать большую страсть ко всему мобильному =)

Комментировать

Оставить комментарий