Эксплуатационные затраты на кодирование "разработка, основанная на исключениях" на Java?

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

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

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

public User getUser(String name);

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

User user = getUser("adam");

int age = user.getAge();

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

public User getUser(String name) throws UserIsNullException;

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

try {

    User user = getUser("adam");

    int age = user.getAge();

}catch( UserIsNullException e) {

}

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

Как бы такой подход к дизайну повлиял на производительность? Выгоды из-за стоимости или это просто плохое кодирование?

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

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

user == null

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

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;
}

Ответы

Ответ 1

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

исключение NullPointerException является исключением и может быть уловлено в catch.

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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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


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

Ответ 5

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

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

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

Ответ 6

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


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

Код, который вы форсируете на своих абонентах, действительно уродлив, трудно писать и поддерживать.

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

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

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

Ответ 7

См. этот ответ об эффективности с исключениями.

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

Ответ 8

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

Что касается производительности, если вы не пишете критическую часть кода с очень низкой задержкой, служебные данные производительности будут небрежными. Однако стоит упомянуть, что есть несколько лучших практик, когда дело доходит до блоков try/catch. Например, я считаю, что эффективная 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);

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

Как бы такой подход к дизайну Эффективность? Выгоды вычесть стоимость или просто плохое кодирование?

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

Ответ 12

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

int getUserAge(String userName) 
{
    int age = 0;
    UserSearchResult result = getUser(userName);
    if (result.getFoundUser())
    {
        User user = result.getUser();
        age = user.getAge();
    }
    return age;
}

Ответ 13

В соответствии с вашим последним правлением. Я думаю (как предложил Тони) использование шаблона NullObject было бы более полезным здесь.

Рассмотрим этот третий сценарий:

class NullUser extends User {

    public static NullUser nullUser = new NullUser();

    private NullUser(){}

    public int getAge() {
        return 0;
    }
}

//Later...
int age;

User user = getUser("adam"); // return nullUser if not found.
age = user.getAge();         // no catch, no if/null check, pure polymorphism

Ответ 14

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

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