Что лучше/эффективнее: проверьте на наличие плохих значений или перехватите Исключения в Java

Что более эффективно в 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:

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

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

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

Ответы

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

Ответ 5

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

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

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

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

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

}

thats мои 2 цента

Ответ 6

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

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

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

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

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

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

Ответ 9

такой вопрос, как просить,

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

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

Ответ 10

Обратите внимание, что если ваш код не генерирует исключения, это не всегда означает, что вход находится в пределах. Опираясь на бросание исключений стандартной Java (API + JVM), например NullPointerException или ArrayIndexOutOfBoundsExceptions, очень нездоровый способ проверки ввода. Иногда в мусоре генерируется мусор, но не исключается.

И да, исключения довольно дороги. Они не должны бросаться во время нормального потока обработки.

Ответ 11

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

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

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

    this.age = age;
}

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

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.

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