Как правильно обрабатывать исключения Java

Как правильно обрабатывать исключения Java

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





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





Понимание исключений Java

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





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

NullPointerException

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



Помните, что исключение - это просто объект, но с одной важной характеристикой: оно должно происходить от

Exception

класс или любой подкласс





Exception

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

  • NullPointerException
  • NumberFormatException
  • IllegalArgumentException
  • RuntimeException
  • IllegalStateException

Так что же происходит, когда вы генерируете исключение?





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

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

Exception in thread 'main' java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Мы можем многое почерпнуть из этого. Во-первых, выброшенное исключение было

NullPointerException

. Это произошло в

getTitle()

в строке 16 Book.java. Этот метод был вызван из

getBookTitles()

в строке 25 файла Author.java. Что метод был вызван из

main()

в строке 14 Bootstrap.java. Как видите, знание всего этого упрощает отладку.

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

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

Допустим, у вас есть

someMethod()

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

как избавиться от вредоносного ПО в Windows 10
public void someMethod(int value) {
if (value 100) {
throw new
IllegalArgumentException

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

someMethod()

называется и используйте блок try-catch :

public void callingMethod() {
try {
someMethod(200);
someOtherMethod();
} catch (IllegalArgumentException e) {
// handle the exception in here
}
// ...
}

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

В нашем примере мы вводим блок try и сразу вызываем

someMethod()

. Поскольку 200 не находится между 0 и 100,

IllegalArgumentException

брошен. Это немедленно завершает выполнение

someMethod()

, пропускает остальную логику в блоке try (

someOtherMethod()

никогда не вызывается) и возобновляет выполнение в блоке catch.

Что было бы, если бы мы позвонили

someMethod(50)

вместо? В

IllegalArgumentException

никогда не будет брошен.

someMethod()

будет работать как обычно. Блок try будет выполняться как обычно, вызывая

someOtherMethod()

когда someMethod () завершается. Когда

someOtherMethod()

заканчивается, блок catch будет пропущен и

callingMethod()

буду продолжать.

Обратите внимание, что у вас может быть несколько блоков catch для каждого блока try:

public void callingMethod() {
try {
someMethod(200);
someOtherMethod();
} catch (IllegalArgumentException e) {
// handle the exception in here
} catch (NullPointerException e) {
// handle the exception in here
}
// ...
}

Также обратите внимание, что необязательный наконец блок тоже существует:

public void method() {
try {
// ...
} catch (Exception e) {
// ...
} finally {
// ...
}
}

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

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

Обратите внимание, что у вас может быть блок finally без блока catch:

public void method() {
try {
// ...
} finally {
// ...
}
}

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

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

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

Чтобы создать проверенное исключение, пройдите с

Exception

. Чтобы создать непроверенное исключение, пройдите от

RuntimeException

.

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

IOException

- проверенное исключение, следующий код не будет компилироваться:

public void wontCompile() {
// ...
if (someCondition) {
throw new IOException();
}
// ...
}

Сначала вы должны объявить, что это вызывает проверенное исключение:

public void willCompile() throws IOException {
// ...
if (someCondition) {
throw new IOException();
}
// ...
}

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

Когда следует использовать отмеченные или непроверенные исключения?

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

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

Рекомендации по использованию исключений Java

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

  • Предпочитайте конкретные исключения общим исключениям. Используйте NumberFormatException над IllegalArgumentException по возможности, в противном случае используйте IllegalArgumentException над RuntimeException когда возможно.
  • Никогда не лови Throwable ! Exception класс фактически расширяется Throwable , а блок catch фактически работает с Throwable или любой класс, расширяющий Throwable. Однако Error класс также расширяется Throwable , и вы никогда не захотите поймать Error потому что Error s указывают на серьезные неисправимые проблемы.
  • Никогда не лови Exception ! InterruptedException расширяет Exception , поэтому любой блок, который ловит Exception также поймает InterruptedException , и это очень важное исключение, с которым не стоит связываться (особенно в многопоточных приложениях), если вы не знаете, что делаете. Если вы не знаете, какое исключение следует перехватить, подумайте о том, чтобы ничего не перехватывать.
  • Используйте описательные сообщения, чтобы упростить отладку. Когда вы генерируете исключение, вы можете указать String сообщение в качестве аргумента. К этому сообщению можно получить доступ в блоке catch с помощью Exception.getMessage() метод, но если исключение не будет обнаружено, сообщение также появится как часть трассировки стека.
  • Старайтесь не улавливать и игнорировать исключения. Чтобы обойти неудобства, связанные с проверенными исключениями, многие новички и ленивые программисты устанавливают блок catch, но оставляют его пустым. Плохой! Всегда обрабатывайте это изящно, но если вы не можете, по крайней мере, распечатайте трассировку стека, чтобы вы знали, что было сгенерировано исключение. Вы можете сделать это с помощью Exception.printStackTrace() метод.
  • Остерегайтесь чрезмерного использования исключений. Когда у вас есть молоток, все выглядит как гвоздь. Когда вы впервые узнаете об исключениях, вы можете почувствовать себя обязанным все превратить в исключение ... до такой степени, что большая часть потока управления вашего приложения сводится к обработке исключений. Помните, исключения предназначены для «исключительных» случаев!

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

Есть вопросы? Знаете ли вы о каких-либо других советах, связанных с исключениями, которые я пропустил? Делитесь ими в комментариях ниже!

Делиться Делиться Твитнуть Эл. адрес Как создать диаграмму потока данных для визуализации данных любого проекта

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

Читать далее
Похожие темы
  • Программирование
  • Джава
Об авторе Джоэл Ли(Опубликовано 1524 статей)

Джоэл Ли является главным редактором MakeUseOf с 2018 года. У него есть степень бакалавра наук. Кандидат компьютерных наук и более девяти лет профессионального опыта написания и редактирования.

Apple Watch 2 нержавеющая сталь против алюминия
Ещё от Joel Lee

Подписывайтесь на нашу новостную рассылку

Подпишитесь на нашу рассылку, чтобы получать технические советы, обзоры, бесплатные электронные книги и эксклюзивные предложения!

Нажмите здесь, чтобы подписаться