Статьи

Java — 5 лучших методов обработки исключений, которых следует избегать

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


Ниже приведены ключевые моменты, описанные далее в этой статье:

  • Классы Throwable и Error не должны быть перехвачены
  • Throwable.printStackTrace (…) никогда не должен вызываться
  • Общие исключения Ошибка, RuntimeException, Throwable и Exception никогда не должны создаваться
  • Обработчики исключений должны сохранять исходное исключение
  • System.out или System.err не должны использоваться для регистрации исключений

Классы Throwable и Error не должны быть перехвачены

Нужно стараться избегать перехвата Throwable и Errors в их коде. На этой странице написана очень подробная статья по этой теме  . Ниже приведен пример  несовместимого кода,  взятого из кода ядра Spring:
try {
cl = Thread.currentThread().getContextClassLoader();
}catch (Throwable ex) { //Non-compliant code
}

В двух словах, следующие причины:

  • Throwable — это суперкласс всех ошибок и исключений в Java. Ошибка — это суперкласс всех ошибок, которые не предназначены для обнаружения приложениями. Таким образом, перехват Throwable по существу будет означать, что ошибки, такие как системные исключения (например, OutOfMemoryError, StackOverFlowError или InternalError), также будут перехвачены. И рекомендуемый подход заключается в том, что приложение не должно пытаться восстанавливаться после ошибок, подобных этим. Таким образом, классы Throwable и Error не должны быть перехвачены. Только Exception и его подклассы должны быть пойманы.

Выше говорилось, что есть причины, по которым люди все еще ловят Throwable. Ошибки данных, такие как проблемы кодирования и т. Д., Которые не известны во время программирования, могут быть обнаружены с помощью этого метода. Тем не менее, перехват Throwable, например InternelError или OutofMemoryError, не поможет, и поэтому должен быть сгенерирован. Таким образом, следует избегать написания кода, состоящего из использования Throwable в качестве общей практики.

Throwable.printStackTrace (…) никогда не должен вызываться

Ниже приведен пример кода, который представляет использование вызова printStackTrace в классе Throwable.

try {
  /* ... */
} catch(Throwable t) {
  t.printStackTrace();        // Non-Compliant
}

Ниже приведены некоторые причины, по которым следует избегать вызова метода printStackTrace для классов Throwable / Exception и вместо этого использовать метод Logger с использованием одной из сред, таких как LogBack или Log4J:

  • Трудно получить журналы для отладки . Журналы, написанные с использованием printStackTrace, записываются в System.err, который трудно маршрутизировать или фильтровать в другом месте. Вместо этого, используя Loggers, легко получить журналы для целей отладки.
  • Нарушение рекомендаций по кодированию. Как правило, в соответствии с рекомендациями по кодированию в готовых к работе приложениях разработчикам необходимо использовать методы Logger для регистрации различного уровня информации. Однако, когда дело доходит до обработки исключений, экземпляры printStackTrace обычно находятся в разных местах. Таким образом, это является нарушением практики кодирования и, следовательно, его следует избегать.
  • Ниже приведены несколько хороших страниц, подробно объясняющих причины:

Общие исключения, такие как Error, RuntimeException, Throwable и Exception, никогда не должны создаваться

Ниже приведены некоторые причины, по которым универсальные исключения / Throwable никогда не должны создаваться:

  • Основная причина, по которой следует избегать генерирования Generic Exceptions, Throwable, Error и т. Д., Заключается в том, что выполнение таким способом предотвращает отлавливание классами предполагаемых исключений. Таким образом, вызывающая сторона не может проверить исключение, чтобы определить, почему оно было выброшено, и, следовательно, не может попытаться восстановить.
  • Кроме того, перехват RuntimeException считается плохой практикой. И, таким образом, генерирование Generic Exceptions / Throwable приведет к тому, что разработчик поймает исключение на более позднем этапе, что в конечном итоге приведет к дальнейшим запахам кода.

Ниже приведен пример кода, который представляет этот запах кода:

public void foo(String bar) throws Throwable { // Non-compliant
  throw new RuntimeException("My Message");    // Non-Compliant
}
// One other instance which displays throwing Exception
public void doSomething() throws Exception {...} // Non-compliant code

Вместо этого можно было бы сделать что-то вроде следующего:

public void foo(String bar) { 
  throw new CustomRuntimeException("My Message");    // Compliant
}

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

При написании кода для обработки исключений я часто встречал такой код, который выполняет некоторые из следующих действий:

  • В приведенном ниже примере кода исключение потеряно.
    try { 
    /* ... */ 
    } catch( Exception e ) {
    SomeLogger.info( e.getMessage() ); // The exception is lost. Just that exception message is written; Also, context information is not logged.
    }
  • В приведенном ниже примере кода весь объект исключения теряется.
    try { 
    /* ... */ 
    } catch( Exception e ) {
    SomeLogger.info( "some context message" ); // The exception is lost
    }
  • В приведенном ниже примере кода контекстное сообщение не предоставляется.
    try { 
    /* ... */ 
    } catch( Exception e ) {
            SomeLogger.info( e ); // No context message
    }

В качестве лучшей практики хотелось бы сделать что-то вроде следующего:
try { 
  /* ... */ 
} catch( Exception e ) {
  SomeLogger.info( "some context message", e ); // Context message is there. Also, exception object is present
}

В случае бросаемых исключений должно быть сделано следующее:

try {
  /* ... */
} catch (Exception e) {                                                   
  throw new CustomRuntimeException("context", e); // Context message is there. Also, exception object is present
}

System.out или System.err не должны использоваться для регистрации исключений

Основная причина, по которой следует избегать использования System.out или System.err для регистрации исключений, заключается в том, что можно просто потерять важные сообщения об ошибках. Вместо этого следует использовать каркасы журналирования, такие как Log4J или LogBack и т. Д., Чтобы регистрировать исключения.