Производительность затрат на кодирование "разработки на основе исключений" в Java? - PullRequest
14 голосов
/ 02 февраля 2010

Существуют ли какие-либо издержки производительности при создании, создании и отлове исключений в Java?

Я планирую добавить «разработку на основе исключений» в более крупный проект. Я хотел бы разработать свои собственные исключения и включить их в свои методы, заставляя разработчиков ловить и выполнять соответствующую работу.

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

public User getUser(String name);

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

User user = getUser("adam");

int age = user.getAge();

, что приведет к исключению NullPointerException и падению. Однако, если я сделал проверку, перед возвратом пользовательского объекта, если он нулевой, и выдал 'UserIsNullException':

public User getUser(String name) throws UserIsNullException;

Я заставляю разработчика думать и действовать:

try {

    User user = getUser("adam");

    int age = user.getAge();

}catch( UserIsNullException e) {

}

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

Как такой подход к дизайну повлияет на производительность? Преимущества перевешивают стоимость или это просто плохое кодирование?

Спасибо за любую помощь!

UPDATE! Чтобы быть ясным, мое внимание не заключено в обертку NullPointerException, как мог бы предложить мой пример. Цель состоит в том, чтобы заставить исполнителя написать попытку / поймать, сохраняя головную боль реального сбоя, поскольку:

пользователь == ноль

было забыто. Вопрос касается сравнения этих двух моделей дизайна:

int age;

try {

User user = getUser("adam");

age = user.getAge();

}catch( UserIsNullException e) {

age = 0;

}

против

int age;

User user = getUser("adam");

if( user != null ) {
age = user.getAge();
} else {
age = 0;
}

Ответы [ 14 ]

8 голосов
/ 02 февраля 2010

", что приведет к NullPointerException и сбой. "

исключение NullPointerException является исключением и может быть перехвачено при перехвате.

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

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

7 голосов
/ 02 февраля 2010

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

7 голосов
/ 02 февраля 2010

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

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

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

5 голосов
/ 02 февраля 2010

Как правило, исключения чрезвычайно дороги в Java и должны не использоваться для управления потоком!

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

Что также вызывает исключение:отклонение от "счастливого пути".Хотя это не относится к вашему примеру кода, иногда очень полезно использовать Null Object вместо возврата простого null.

5 голосов
/ 02 февраля 2010

Лично я думаю, что это плохая и неприятная идея.

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

Этианнотации доступны в JSR-305 , и, очевидно, существует также эталонная реализация .


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

3 голосов
/ 02 февраля 2010

Создание трассировки стека составляет около тысячи базовых инструкций.Это имеет определенную стоимость.


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

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

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

  • Другой разработчик отвечает за проверку недействительности Пользователя, полученного от вашего API (если документация четко об этом говорит). Его код регулярно выполняет такие проверки внутри компании, поэтому он может делать это и после вызова вашего APIБолее того, это придаст его коду некоторую однородность.

  • Когда целесообразно повторно использовать существующее исключение, это гораздо лучше, чем создание собственного. Например, если это было ошибкой дляпозвоните в API и попросите несуществующего пользователя, вы можете вызвать исключение IllegalArgumentException.

1 голос
/ 02 февраля 2010

Нулевые объекты - это то, что может вам помочь.На днях я читал книгу Мартина Фаулера «Рефакторинг», в которой описывается использование определенного объекта, который действует вместо возврата нуля, избавляя вас от необходимости постоянно проверять NULL.

Я выиграл 'не пытайтесь объяснить это здесь, как это очень хорошо описано в книге.

1 голос
/ 02 февраля 2010

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

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

1 голос
/ 02 февраля 2010

Мне нравится этот стиль кодирования, так как он очень четко показывает, что происходит с точки зрения того, кто использует ваш API. Иногда я даже называю методы API getMandatoryUser(String) и getUserOrNull(String), чтобы различать методы, которые никогда не будут возвращать null, и методы, которые могут возвращать null.

Что касается производительности, за исключением случаев, когда вы пишете критически важный фрагмент кода, затраты на производительность будут незначительными. Тем не менее, стоит упомянуть, что есть некоторые лучшие практики, когда дело доходит до блоков try / catch. Например, я считаю, что Effective Java рекомендует создавать блок try / catch вне цикла, чтобы избежать его создания на каждой итерации; например,

boolean done = false;

// Initialise loop counter here to avoid resetting it to 0 if an Exception is thrown.
int i=0;    

// Outer loop: We only iterate here if an exception is thrown from the inner loop.
do {
  // Set up try-catch block.  Only done once unless an exception is thrown.    
  try {
    // Inner loop: Does the actual work.
    for (; i<1000000; ++i) {
      // Exception potentially thrown here.
    }

    done = true;
  } catch(Exception ex) {
    ... 
  }
} while (!done);
1 голос
/ 02 февраля 2010

См. этот ответ о производительности с исключениями.

По сути, в вашей идее вы включаете исключение RuntimeException, NullPointerException, в проверенное исключение; imho, я бы сказал, что проблема может быть решена на бизнес-уровне с помощью ObjectNotFoundException: вы не нашли пользователя, тот факт, что пользователь является нулевым, и что это вызывает ошибки, наступает после.

...