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

в 20:47, , рубрики: exception, java

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

Один из наиболее распространённых неудачных подходов — это использование исключений для контроля потока выполнения. Этого следует избегать по двум причинам:

  1. Это снижает производительность и быстродействие вашего кода
  2. Это делает ваш код менее читаемым


Давайте начнём с рассмотрения примера. Здесь исключение используется для управления потоком выполнения:

   public static int findAge(String name) {
       try {
           String ageAsString = findUser(name);
           return ageAsString.length();
       } catch (NameNotFoundException e) {
           return 0;
       }
   }
   private static String findUser(String name) {
       if(name==null) {
           throw new NameNotFoundException();
       }
       return name;
   }

Если пользователь предоставит не нулевое значение имени, то метод findAge вернёт длину этого имени, но если имя пользователя будет null, то тогда метод findUser выбросит исключение NameNotFoundException и в этом случае метод findAge вернёт 0.

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

public static int findAgeNoEx(String name) {
       String ageAsString = findUserNoEx(name);
       return ageAsString.length();
   }
   private static String findUserNoEx(String name) {
       if(name==null) {
           return "";
       }
       return name;
   }

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

public class ControlFlowWithExceptionOrNot {
   public static class NameNotFoundException extends RuntimeException {
       private static final long serialVersionUID = 3L;
   }
   private static final int TRIAL = 10000000;
   public static void main(String[] args) throws InterruptedException {
       long start = System.currentTimeMillis();
       for (int i = 0; i < TRIAL; i++) {
           findAgeNoEx(null);
       }
       System.out.println("Duration :" + (System.currentTimeMillis() - start));
       long start2 = System.currentTimeMillis();
       for (int i = 0; i < TRIAL; i++) {
           findAge(null);
       }
       System.out.println("Duration :" + (System.currentTimeMillis() - start2));
   };
   public static int findAge(String name) {
       try {
           String ageAsString = findUser(name);
           return ageAsString.length();
       } catch (NameNotFoundException e) {
           return 0;
       }
   }
   private static String findUser(String name) {
       if (name == null) {
           throw new NameNotFoundException();
       }
       return name;
   }
   public static int findAgeNoEx(String name) {
       String ageAsString = findUserNoEx(name);
       return ageAsString.length();
   }
   private static String findUserNoEx(String name) {
       if (name == null) {
           return "";
       }
       return name;
   }
}

Вывод:

Duration :16
Duration :6212

Как видно, использование исключения обошлось нам в тысячи миллисекунд на моём Intel Core i7-3630QM.

Если мы сравним два наших findAge метода с точки зрения читабельности, то метод без исключения абсолютно понятен: во-первых мы может быть абсолютно уверены, что метод findUser возвращает строку; а во-вторых независимо от того, какая именно строка будет возвращена, мы получим её длину. В то же время, метод с исключением несколько запутанный: не до конца ясно что именно возвращает метод findUser. Он может вернуть строку, а может и выбросить исключение и из сигнатуры метода этого не видно. По этой причине парадигма функционального программирования не приветствует использование исключений.

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

Надеюсь, данная статья была вам интересна, а возможно и полезна.

Автор: Алексей Ильинчик

Источник


* - обязательные к заполнению поля