Бросать исключения в Java - PullRequest
       35

Бросать исключения в Java

5 голосов
/ 09 февраля 2009

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

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

1.) Бросить исключение в блоке try с помощью команды «throw new ...» и немедленно перехватить его в блоке catch - так называемый механизм try-throw-catch.

2.) Бросить исключение в метод с «throw new ...», а затем объявить в заголовке метода, что этот метод может вызвать исключение с «throws ...» - так называемый pass-the -buck.

Я недавно прочитал, что «не имеет никакого смысла бросать исключение, а затем ловить его тем же способом», что заставило меня задуматься, правильно ли я понимаю вещь, или человека, который написал это имело в виду другое. Разве первый способ обработки исключений не делает именно это (механизм try-throw-catch)? Я имею в виду, что он выдает исключение и перехватывает его тем же способом. Я читал, что лучше создавать исключение в одном методе и перехватывать его в другом методе, но это только один (возможно, лучший) способ. Другой способ также является законным и правильным, не так ли?

Не могли бы вы дать мне комментарий по этому поводу? Большое спасибо.

Ответы [ 11 ]

21 голосов
/ 09 февраля 2009

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

Например, FileNotFoundException выбрасывается из new FileInputStream(new File(<i>filename</i>)), поскольку сам FileInputStream не может обработать случай, когда файл отсутствует; это исключение нужно вызвать, чтобы приложение конечного пользователя могло решить проблему.

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

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

6 голосов
/ 09 февраля 2009

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

4 голосов
/ 09 февраля 2009

Не первый способ обработки исключения делает именно это ( механизм "попробуй брось поймай")? Я серьезно выдает исключение и ловит его в тот же метод.

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

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

3 голосов
/ 09 февраля 2009

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


«Разве первый способ обработки исключений не делает именно это?»

Мой ответ - да, так как вы описываете его, первый метод работает, выбрасывая и перехватывая исключение в том же методе. Однако я не знаю, что try-throw-catch должен работать так, как вы его описываете.


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

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

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

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


Мои комментарии:

Упомянутому выше механизму try-throw-catch может не понадобиться исключение для того же метода. Я должен был бы прочитать текст, который вы нашли наверняка, но я ожидаю, что это не обязательно. Если исключение не требуется для исключения в том же методе, то ваша стратегия обработки исключений представляет собой комбинацию 1) и 2).

В комбо, один метод будет использовать механизм try-throw-catch для перехвата исключения, вызванного вызываемым методом. Мне кажется, что 1) и 2) должны работать вместе, чтобы сформировать вашу стратегию обработки исключений.

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

Ура, Ed

3 голосов
/ 09 февраля 2009

Человек, который написал «нет никакого смысла бросать исключение, а затем ловить его тем же способом», имеет право на свое мнение, но оно не широко распространено. Существует множество случаев, когда необходимо генерировать и перехватывать исключение одним и тем же методом. Самое простое - это когда вы выполняете последовательность операций, а сбой любой из них делает остальные недействительными. Если вы обнаружите, что одна из этих операций завершилась неудачей, вполне разумно сгенерировать исключение и перехватить его в конце метода. На самом деле это логичный способ делать вещи. Возможно, вы могли бы переписать код, чтобы не использовать исключение, возможно, с некоторыми флагами состояния и с оператором break или двумя, но почему бы вам? Использование исключения проясняет происходящее и улучшает читаемость кода.

2 голосов
/ 09 февраля 2009

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

Вы бы выдавали исключение, а не просто обрабатывали ошибку немедленно, если:

  • Другой код, например код, вызвавший ваш метод, должен обработать ошибку. Например, если ваш код не является кодом UI, он, вероятно, не должен генерировать окна. Это ваш метод № 2.

  • Вы можете воспользоваться блокировкой try, catch, наконец. Возможно, вы могли бы написать более чистый код таким образом, но я думаю, что 90% времени ваш код будет более читабельным, если использовать простые операторы if.

2 голосов
/ 09 февраля 2009

При первом способе вы имеете в виду что-то вроде этого:

try {
  ok = doSomething();
  if (!ok) {
   throw new Exception("Error");
  }
 ok = doSomethingElse();
}catch (Exception e) {
}

Это позволит вам выйти из блока try-catch, не выполняя остальную часть. Это единственное допустимое использование, которое я могу придумать: генерировать исключение с помощью throw и перехватывать его самостоятельно в блоке try-catch. Однако вместо него должны использоваться стандартные блоки if. Я не понимаю, почему кто-то должен выбросить исключение, а потом поймать его сам.

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

1 голос
/ 09 февраля 2009

Использование исключения для потока управления специально рассматривается в Effective Java, 2nd Edition Джошуа Блохом, пункт 57:

Пункт 57: Использовать исключения только для исключительных условий

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

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

1 голос
/ 09 февраля 2009

По моему мнению, блоки, которые вы пишете, не должны включать в себя какие-либо "throw new", которые попадают в один и тот же метод. Когда вы бросаете исключение, вы говорите: «Я столкнулся с ситуацией, с которой я не могу справиться; кому-то еще придется иметь дело с этим». Ваш метод с «throw new» должен либо создать непроверенное исключение для выброса, либо объявить проверенное исключение в своей сигнатуре метода.

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

Я не создаю свое собственное исключение, а затем ловлю его тем же методом.

1 голос
/ 09 февраля 2009

Мой опыт заключается в том, что при использовании первого метода ваш код быстро становится нечитаемым, поскольку функциональность и обработка ошибок смешиваются. НО имеет смысл в некоторых случаях, когда у вас есть попытка {} catch {} finaly {} - например, при обработке файлов или базы данных, когда вы ВСЕГДА хотите, чтобы соединение было закрыто .

try{ //do something
}catch(Exception ex){
//log
}finally{
//connection.close
}

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

...