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

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

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

Например, предположим, что я создал следующий класс:

public class Account {
    private float balance;

    /* ... constructor, getter, and other fields and methods */

    public void transferTo(Account other, float amount) {
        if (amount > balance)
            throw new NotEnoughBalanceException();
        /* ... */
    }
}

Как мне создать NotEnoughBalanceException? Должно ли оно расширяться Exception или RuntimeException? Или я должен просто использовать IllegalArgumentException вместо этого?

Ответ 1

Там много разногласий по этой теме. На моей последней работе мы столкнулись с некоторыми реальными проблемами, когда исключения Runtime забываются до тех пор, пока они не появятся на производстве (на agewards.com), поэтому мы решили использовать исключительно проверенные исключения.

В моей текущей работе я обнаружил, что во многих или во всех случаях есть много исключений Runtime.

Вот что я думаю: используя CheckedExceptions, я вынужден во время компиляции хотя бы признать исключение в вызывающем. С исключениями Runtime я не вынужден компилятором, но могу написать unit test, что заставляет меня с этим справиться. Поскольку я все еще верю, что чем раньше ошибка поймана, тем дешевле ее исправить, я предпочитаю CheckedExceptions по этой причине.

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

Мой опыт подсказывает мне, что я получаю более высокое качество, то есть код, который ПРОСТО РАБОТАЕТ, когда я использую проверенные исключения. Проверенные исключения могут загромождать код, но есть способы справиться с этим. Мне нравится переводить исключения при прохождении границы слоя. Например, если я перехожу из своего уровня персистентности, я хотел бы преобразовать исключение SQL в исключение персистентности, поскольку следующий слой вверх не должен заботиться о том, чтобы я сохранялся в базе данных SQL, но захочу знать, нельзя ли что-то сохранить. Другой метод, который я использую, заключается в создании простой иерархии исключений. Это позволяет мне писать более чистый код на один слой, так как я могу поймать суперкласс и иметь дело только с отдельными подклассами, когда это действительно имеет значение.

Ответ 2

В целом, я думаю, совет Джошуа Блоха в Эффективная Java лучше всего подытоживает ответ на ваш вопрос: Использовать проверенные ожидания для восстанавливаемые условия и исключения времени выполнения для ошибок программирования (пункт 58 в 2-м издании).

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

Но также обратите внимание на пункты 59: избегайте ненужного использования проверенных исключений и 57: используйте исключения только для исключительных условий. Как указывали другие, этот случай не может служить основанием для исключения. Рассмотрите возможность возврата false (или, возможно, объекта статуса с подробностями о том, что произошло), если недостаточно кредитов.

Ответ 3

Когда использовать проверенные исключения? Честно? По моему скромному мнению... никогда. Я думаю, что прошло около 6 лет с тех пор, как я в последний раз создал проверенное исключение.

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

try {
  ...
} catch (IOException e) {
  // do nothing
}

В то время как у меня есть бесчисленное количество раз, написанных таким образом:

try {
  ...
} catch (IOException e) {
  throw new RuntimeExceptione(e);
}

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

Там причина Spring Вспомогательные классы DAO переводят проверенное SQLException в неконтролируемое исключение DataAccessException.

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

Кроме того, проверенные исключения прерывают инкапсуляцию.

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

Проверенные исключения в Java были экспериментом... неудачным экспериментом. Мы должны просто сократить наши потери, признать, что допустили ошибку и продолжим. ИМХО .Net поняла это, исключив исключения. С другой стороны, у него было преимущество второго усыновления от ошибок Java.

Ответ 4

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

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

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

IMHO recap: Исключения == странная черная магия.

бытием-конструктивно-редактирование:

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

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

менее enterprisey-предложение-редактирование:

Если вы делаете это ради своего собственного удовольствия (и, похоже, это так, см. комментарии), я бы предложил возвратить логическое значение. Использование будет примерно таким:

// ...
boolean success = transferTo(otherAccount, ONE_MILLION_DOLLARS_EXCLAMATION);

if (!success) {
  UI.showMessage("Aww shucks. You're not that rich");
  return; // or something...
} else {
  profit();
}
// ...

Ответ 5

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

Ответ 6

Мое правило

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

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

Существует много документации по этому вопросу. Вы можете найти много, просматривая Hibernate веб-страниц, так как они изменили все исключения Hibernate 2.x от отмеченного до непроверенного в версии 3.x

Ответ 7

От Unchecked Exceptions - The Controversy:

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

Обратите внимание, что исключенное исключение - это одно производное от RuntimeException, а проверенное исключение - это производное от Exception.

Зачем бросать RuntimeException, если клиент не может ничего сделать для восстановления из исключения? В статье объясняется:

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

Ответ 8

Я чувствую, что проверенное исключение - полезный контракт, который следует использовать экономно. Классический пример того, где я думаю, что проверенное исключение - хорошая идея, - это InterruptedException. Я чувствую, что хочу быть в состоянии остановить поток/процесс, когда хочу, чтобы он остановился, независимо от того, как долго кто-то указал Thread.sleep().

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

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

catch (InterruptedException ie) {
    Thread.currentThread().interrupt();
}

Этот обработчик гарантирует, что код поймает проверенное исключение и сделает именно то, что вы хотите: остановить этот поток. По общему признанию, если есть еще один обработчик исключений /eater вверх по течению, не исключено, что он будет обрабатывать исключение менее хорошо. Тем не менее, FindBugs может помочь вам найти их.

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

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

Ответ 9

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

Дизайнеры С# решили, что предпочтительными являются исключенные исключения.

Или вы можете следить за C-стилем и проверять возвращаемые значения и не бросать исключения.

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

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

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

Ответ 10

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

Ответ 11

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

Известная проблема, которая проверяет исключения, чрезмерно используется в Java, но это не значит, что все они плохие. Вот почему он является неотъемлемой частью философии Spring, например (http://www.springsource.org/about)

Ответ 12

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

Брюс Экель, автор книги "Думая на Java", имеет приятное эссе по этой теме.

Ответ 13

Я не думаю, что сценарий (недостаточные фонды) требует выброса Exception - он просто не является достаточно исключительным и должен обрабатываться нормальным потоком управления программой. Однако, если мне действительно нужно было исключить исключение, я бы выбрал проверенное исключение, расширив Exception, а не RuntimeException, который не отмечен. Это заставляет меня обрабатывать исключение явно (мне нужно объявить его брошенным или поймать где-нибудь).

IllegalArgumentException является подклассом RuntimeException, что делает его неконтролируемым исключением. Я бы подумал только об этом, если у вызывающего есть удобный способ определить, являются ли аргументы метода законными. В вашем конкретном коде неясно, имеет ли вызывающий объект доступ к balance, или весь "контрольный баланс и перевод средств" является атомной операцией (если это не так, то у вызывающего действительно нет удобного способа проверки аргументы).

EDIT: Уточнила мою позицию при метании IllegalArgumentException.

Ответ 14

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

Если вы являетесь разработчиком API (back-end developer), используйте проверенные исключения, в противном случае используйте исключения Runtime.

Также обратите внимание, что использование исключений Runtime в некоторых ситуациях следует считать большой ошибкой, например, если вы хотите сбросить исключения во время выполнения из вашей сессии beans (см. http://m-hewedy.blogspot.com/2010/01/avoid-throwing-runtimeexception-from.html для получения дополнительной информации об этой проблеме из-за использования экскрементов Runtime в сеансе beans).