Имея блок try-catch, вы должны размещать в нем ВСЕ утверждения или просто небезопасные?

Предположим, что save throws и i используется только для save. Являются ли следующие фрагменты кода одинаковыми? Пожалуйста, рассмотрите семантику, производительность и другие аспекты.

void bob(){
  int i = calculate();
  try {
    save(i);
  } catch(Exception e){
    report(e)
  }
}

против.

void bob(){
  try {
    int i = calculate();
    save(i);
  } catch(Exception e){
    report(e)
  }
}

Как правило, я хочу знать, следует ли разместить все выражения функции в блоке try-catch или просто метать.

Ответ 1

Семантика,, если вы решили, какой метод вы собираетесь разместить в своей конструкции try-catch (и вам удобно, что вы приняли это решение правильно), тогда Ответ довольно прост:

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

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

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

Изменить: я не смог найти какие-либо детали в спецификации JVM для ссылки, но есть много сообщений пользователей, которые изучают и объясняют сгенерированный байт-код как этот и этот (среди многих других - поиск Google даст несколько интересных результатов). Для меня это похоже на то, что компилятор Java пытается как можно меньше испускать (за исключением, конечно, фактического кода, который вы помещаете в предложения try и catch, а также некоторые неизбежные инструкции по потоку программ для перехода по указанным статьям или поместить объект исключения, если таковой имеется). Он оставляет ВМ ответственность за выяснение, где исключение является кандидатом, которого нужно поймать. Это, скорее всего, переносит больше нагрузки на сценарии, где фактически происходит исключение, но, как мы знаем, исключения для исключительных случаев, а не контроля потока в любом случае.

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

Ответ 2

Они не совпадают. Разница заключается в области переменной i. Во втором случае вы не можете использовать i вне блока try-catch.

Как правило, я хочу знать, следует ли разместить все утверждения функции в блоке try-catch или просто метать.

Лучший способ - просто обернуть код, уязвимый для исключения исключения внутри блока try-catch. Таким образом, вы можете обрабатывать конкретное исключение, относящееся к определенному блоку кодов. Итак, первый путь - это тот, на который нужно пойти.

Ответ 3

В этом случае:

void bob(){
  try {
    int i = calculate();
    save(i);
  } catch(Exception e){
    report(e)
  }
}

все исключения (кроме ошибок) будут пойманы, потому что Exception является супер-классом всех типов Exception (кроме ошибок). Таким образом, будут обрабатываться не только проверенные исключения, но и флажок, кроме RuntimeException. Если это ваша цель, я предлагаю следовать этому пути. С другой стороны, я думаю, что минимизация области try...catch является хорошей практикой, потому что в случае исключения найти проблемную строку кода проще.

Ответ 4

Rohit указывает (правильно), что область переменной i здесь является разницей.

Дополнительная разница заключается в том, что если calculate() выбрасывает Unchecked Exception; он попадет в блок catch. Если вы должны поместить вызов в calculate() в блоке try, зависит от того, хотите ли вы обрабатывать Unchecked Exception из calculate() в блоке catch здесь или разрешить его выбросить наружу. На мой взгляд, поскольку он снят и, следовательно, полностью неожиданен, я бы не поставил вызов calculate() в блоке try.

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

// degraded performance example
void bob(){

    int i;

    try {
        i = calculate();
    } catch(Exception e){
        report(e)
    }

    try {
        save(i);
    } catch(Exception e){
        report(e)
    }
}