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

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

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

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

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 или Exception RuntimeException ? Или я должен просто использовать IllegalArgumentException ?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Существует причина, по которой вспомогательные classы Spring DAO переводят проверенное SQLException в неконтролируемое исключение DataAccessException.

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

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

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

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

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

В вашем случае это вовсе не исключительный статус, когда кто-то пытается перевести больше денег, чем позволяет баланс. Я полагаю, что это происходит очень часто в реальном мире. Поэтому вам следует проконсультироваться с этими ситуациями. Исключением может быть то, что ваш 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(); } // ... 

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

Мое правило

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

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

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

Из непроверенных исключений – споры :

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

У Брюса Эккеля, автора книги « Мышление на Java» , есть хорошая эссе по этой теме.

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

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

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

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

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

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

  • Почему исключения следует использовать консервативно?
  • Какая часть бросания Исключения стоит дорого?
  • Разница между java.lang.RuntimeException и java.lang.Exception
  • Есть ли разница между «броском» и «броском»?
  • Включите IncludeExceptionDetailInFaults (либо из ServiceBehaviorAttribute, либо из поведения конфигурации ) на сервере
  • Почему я получаю «Исключение; должен быть пойман или объявлен брошенным ", когда я пытаюсь скомпилировать свой Java-код?
  • Исключение Java ArrayIndexOutOfBounds
  • Зачем создавать пользовательские исключения?
  • Неужели так плохо ловить общее исключение?
  • Драйвер JDBC генерирует исключение «ResultSet Closed» на пустой ResultSet
  • Как использовать try catch для обработки исключений - лучшая практика
  • Давайте будем гением компьютера.