Что лучше / эффективнее: проверять неверные значения или ловить исключения в Java - PullRequest
14 голосов
/ 20 августа 2009

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

Вот два блока примера кода, чтобы проиллюстрировать эту разницу:

void doSomething(type value1) {
    ResultType result = genericError;

     if (value1 == badvalue || value1 == badvalue2 || ...) {
          result = specificError;
     } else {
          DoSomeActionThatFailsIfValue1IsBad(value1);
          // ...
          result = success;
     }
     callback(result);
}

против

void doSomething(type value1) {
     ResultType result = genericError;
     try {
          DoSomeActionThatFailsIfValue1IsBad(value1);
          // ...
          result = success;
     } catch (ExceptionType e) {
          result = specificError;
     } finally {
          callback(result);
     }
}

С одной стороны, вы всегда делаете сравнение. С другой стороны, я, честно говоря, не знаю, что делают внутренние компоненты системы, чтобы сгенерировать исключение, сгенерировать его, а затем вызвать предложение catch. Кажется, что он менее эффективен, но если он не добавляет накладных расходов в случае отсутствия ошибок, то в среднем он более эффективен. Что он? Это добавляет подобную проверку в любом случае? Это проверка там в неявном коде, добавленном для обработки исключений, даже с дополнительным уровнем явной проверки? Возможно, это всегда зависит от типа исключения? Что я не рассматриваю?

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

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

Дополнительные вопросы для рассмотрения:

  • Как изменится ваш ответ, если значение будет "плохим" (будет вызывать исключение) большую часть времени?
  • Сколько из этого будет зависеть от особенностей используемой виртуальной машины?
  • Если бы тот же вопрос был задан для языка X, был бы ответ другим? (Который, в более общем плане, спрашивает, можно ли предположить, что проверка значений всегда более эффективна, чем полагаться на обработку исключений просто потому, что это добавляет больше накладных расходов текущими компиляторами / интерпретаторами.)
  • (Новое) Вызывание исключения происходит медленно. Есть ли в блоке try издержки, даже если исключение не выдается?

Сходства на SO:

  • Это похоже на пример кода в этого ответа , но утверждает, что они похожи только в концепции, а не в скомпилированной реальности.
  • Посылка похожа на этот вопрос , но, в моем случае, запрашивающая сторона задачи (например, "Something") не является вызывающей стороной метода (например, "doSomething") (таким образом, нет возвращается).
  • И этот очень похож, но я не нашел ответа на свой вопрос.

  • И похоже на слишком много других вопросов для перечисления, кроме:

    Я не спрашиваю о передовой теоретической практике. Я спрашиваю больше о производительности и эффективности во время выполнения (что должно означать, что для конкретных случаев есть ответы, не связанные с мнением), особенно на платформах с ограниченными ресурсами. Например, если единственным плохим значением является просто нулевой объект, было бы лучше / эффективнее проверить это или просто попытаться использовать его и поймать исключение?

Ответы [ 11 ]

9 голосов
/ 20 августа 2009

" Как ваш ответ изменится, если значение будет" плохое "(будет выдавать исключение) большую часть времени? " Я думаю, что это ключ прямо здесь. Исключения являются дорогостоящими по сравнению со сравнениями, поэтому вы действительно хотите использовать исключения для исключительных условий.

Точно так же ваш вопрос о том, как этот ответ может измениться в зависимости от языка / среды, связан с тем, что: стоимость исключений различна в разных средах. .Net 1.1 и 2.0 работают невероятно медленно, например, при первом возникновении исключения.

6 голосов
/ 20 августа 2009

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

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

3 голосов
/ 20 августа 2009

Я мог найти на удивление мало текущей информации о стоимости создания исключений. Совершенно очевидно, что они должны быть, вы создаете объект и, вероятно, получаете информацию трассировки стека.

В конкретном примере, о котором вы говорите:

if (value1 == badvalue || value1 == badvalue2 || ...) {
      result = specificError;
 } else {
      DoSomeActionThatFailsIfValue1IsBad(value1);
      // ...
      result = success;
 }

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

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

2 голосов
/ 20 августа 2009

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

вопрос на самом деле, когда вы бросаете исключение? в исключительных ситуациях.

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

например, метод,

public int IsAuthenticated(String username, String password)
{

     if(!Validated(username,password)
     {
          // just an error
          // log it
          return -2;   
     }

     // contacting the Database here
     if cannot connect to db
     {
          // woww this is HUUGE
          throw new DBException('cannot connect'); // or something like that
     }

     // validate against db here
     if validated, return 0;

    // etc etc

}

вот мои 2 цента

2 голосов
/ 20 августа 2009

По моему мнению, вы должны иметь блоки try / catch вокруг всего, что может потенциально генерировать исключения, если только у вас есть безопасная работающая система. У вас есть более точный контроль над ошибками, если вы проверяете возможные ошибки данных. Поэтому я предлагаю сделать оба.

1 голос
/ 21 августа 2009

вопрос, подобный этому, похож на вопрос

"более эффективно написать интерфейс или базовый класс со всеми абстрактными функциями"

имеет ли значение, что является более эффективным? только один из них - правильный путь для данной ситуации

1 голос
/ 20 августа 2009

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

Что еще более важно, исключения являются проблемой стиля. Исключения для исключительных условий делают код проще, потому что кода для проверки ошибок меньше, поэтому реальная функциональность более четкая и компактная.

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

Мой совет, который применим ко всем языкам и средам, о которых я знаю:

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

Свободно используйте пробные блоки.

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

1 голос
/ 20 августа 2009

Обычно можно предположить, что try-catch более дорогой, поскольку в коде он выглядит тяжелее, но это полностью зависит от JIT. Я думаю, что это невозможно сказать без реального случая и некоторых измерений производительности. Сравнения могут быть более дорогими, особенно если у вас много значений, например, или потому что вам нужно вызвать equals(), поскольку == не будет работать во многих случаях.

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

1 голос
/ 20 августа 2009

Мое личное мнение заключается в том, что исключения указывают на то, что что-то сломано - это может быть API, вызванный с недопустимыми аргументами, либо деление на ноль, либо файл не найден и т. Д. Это означает, что исключения могут быть вызваны проверкой значений.

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

Это, на мой взгляд, хорошая вещь. «Умный» код трудно обернуть вокруг себя.

С другой стороны - JVM становится намного умнее - кодирование для эффективности обычно не окупается.

0 голосов
/ 22 августа 2009

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

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

public void setAge(int age)
{
    if(age < 0)
    {
        throw new IllegalArgumentException("Array can't be negative");
    }

    this.age = age;
}

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

public void readFile(String filename) throws IOException
{
   File myfile = new File(filename);
   FileInputStream fis = new FileInputStream(myfile);

   //do stuff

   fis.read();

   //do more stuff
}

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

Надеюсь, что это поставит вас на правильный путь. Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...