Статьи

Бета-версии NetBeans 7.4 предупреждают о неэффективной обработке исключений

Существует множество примеров того, как обработка исключений в Java может быть более сложной, чем может показаться на первый взгляд, и Джош Блох посвятил целую главу « Эффективная Java» (обе редакции) обработке исключений. Проверил модель исключения в Java остается « спорным Мне было приятно видеть, что бета-версия NetBeans 7.4, которую я недавно скачал, имеет некоторые советы, которые помогут хотя бы в нескольких нюансах обработки исключений Java.

Я недавно скачал бета- версию NetBeans 7.4 и установил эту последнюю версию NetBeans на свой ноутбук. Я загрузил пакет «Все» для загрузки IDE NetBeans (включая GlassFish Server Open Source Edition 4.0 и Apache Tomcat 7.0.41 и поддержку Groovy ) и установил его на свой компьютер.

Двумя новыми подсказками в категории «вероятных ошибок», которые есть в «текущей версии разработки» NetBeans, являются блок «finally» подавляет исключения »и« throw »внутри блока« finally ». В этом посте я продемонстрирую эти подсказки и кратко расскажу, почему эти подсказки помогают улучшить обработку исключений.

блок finally блокирует исключения

Как задокументировано в таких статьях, как James Stauffer « Не возвращаться» в предложении finally, а также в форумах, таких как Java, вопрос о дизайне try-finally return , общепринято, что возврат метода из блока finally — плохая идея. Пример этой плохой практики кодирования Java показан ниже.

Пример исключения блока наконец-то блокирующего

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void throwException() throws Exception
{
   throw new Exception("A checked exception!");
}
 
/**
 * Demonstrate NetBeans hint "The 'return' statement in the 'finally' block
 * discards unhandled exceptions" by returning a value from the "finally"
 * block.
 *
 * @return Integer value that is really meaningless in this case.
 * @throws RuntimeException This exception is always thrown.
 */
public int demonstrateReturnFromFinallyBlock()
{
   int value = 5;
   try
   {
      value = 7;
      throwException();
   }
   catch (Exception exception)
   {
      throw new RuntimeException(exception);
   }
   finally
   {
      return value;
   }
}

Когда метод demonstrateReturnFromFinallyBlock() в приведенном выше фрагменте кода выполняется, вывод выглядит следующим образом:

Возвращаемое значение: 7

Хотя можно было бы ожидать, что метод demonstrateReturnFromFinallyBlock() сгенерирует исключение Runtime вместо того, чтобы возвращать целочисленное значение 7 (поскольку Javadoc для этого метода даже объявляет!), Исключение «всегда выброшенное» фактически отбрасывается из-за оператора return в finally заблокировать. Это потенциально неприятная проблема, которая может быть очевидна только во время выполнения, а не во время компиляции. К счастью, бета-версия NetBeans 7.4 содержит подсказку об этой потенциальной проблеме, как показано на следующем снимке экрана:

demonstrateReturnFromFinallyExample

На приведенном выше снимке экрана показано, что бета-версия NetBeans 7.4 предупреждает о состоянии оператора return в блоке finally , подчеркивая его желтым цветом. Это также показывает, что при наведении курсора на этот фрагмент кода, подчеркнутый желтым цветом, редактор NetBeans намекает: «Оператор return в блоке finally отменяет необработанные исключения».

Можно также узнать об этом же условии, передав -Xlint: finally компилятору javac . Очевидно, что это можно сделать в командной строке, о чем я ранее писал в параметрах -Xlint javac , но на следующем снимке экрана показано то же самое, что делается в NetBeans с помощью спецификации -Xlint:finally помощью параметра project.properties javac.compilerargs=-Xlint:finally .

xlintFinallyWarningForReturnFromFinally

блок ‘throw’ inside ‘finally’

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/**
 * Demonstrate NetBeans hint warning that throwing an exception from a
 * 'finally' block is a bad idea because it hides the original exception.
 */
public void demonstrateThrowFromFinallyBlock()
{
   Integer twoDividedByZero = null;
   try
   {
      twoDividedByZero = 2 / 0;
   }
   finally
   {
      if (twoDividedByZero == null)
      {
         throw new RuntimeException("Cannot calculate quotient with division.");
      }
   }
}

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

На следующем снимке экрана показано предупреждение бета-версии NetBeans 7.4 об этом состоянии.

demonstrateThrowFromFinallyHint

При наведении курсора на подчеркнутый желтым цветом код отображается предупреждение NetBeans «Оператор throw в блоке finally может скрыть исходное исключение». Снимок экрана выше показывает, что это именно то, что произошло в этом случае: ArithmeticException и его трассировка стека были скрыты RuntimeException выброшенным из блока finally .

Если я закомментирую строку, выдавшую исключение из блока finally , исходное исключение снова станет доступным. Это продемонстрировано на следующем снимке экрана.

demonstrateThrowFromFinallyHintGone

Включение даже -Xlint:all (или просто -Xlint ) не будут предупреждать о плохой форме выброса исключения из блока finally . В этом конкретном примере -Xlint:devzero (или -Xlint:all или -Xlint ) показали бы, что в коде происходит деление на ноль, но в общем случае -Xlint не предупреждает вас, чтобы javac предупреждала вас при броске из Блок finally скрывает оригинальное исключение. Это делает этот конкретный намек NetBeans особенно полезным.

Вывод

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