Почему следует избегать использования исключений в управлении вашим потоком в 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. Он может вернуть строку, а может и выбросить исключение и из сигнатуры метода этого не видно. По этой причине парадигма функционального программирования не приветствует использование исключений.

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

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


ссылка на оригинал статьи https://habr.com/ru/post/476142/