Understanding checked vs unchecked exceptions in Java
Понимание проверенных и непроверенных исключений в Java
Джошуа Блох в "Эффективной Java" сказал, что
Используйте проверяемые исключения для условий, которые можно восстановить, и исключения во время выполнения для ошибок программирования (пункт 58 во 2-м издании)
Давайте посмотрим, правильно ли я это понимаю.
Вот мое понимание проверяемого исключения:
try{ StringuserInput=//read in user input Longid= Long.parseLong(userInput); }catch(NumberFormatException e){ id = 0; //recover the situation by setting the id to 0 }
1. Считается ли приведенное выше проверяемым исключением?
2. Является ли RuntimeException непроверяемым исключением?
//3. What should I do here? //Should I "throw new FileNotFoundException("File not found");"? //Should I log? //Or should I System.exit(0);? }
4. Теперь, не мог ли приведенный выше код также быть проверяемым исключением? Я могу попытаться исправить ситуацию таким образом? Могу ли я? (Примечание: мой 3-й вопрос находится внутри catch выше)
try{ StringfilePath=//read in from user input file path Filefile=newFile(filePath); FileInputStreamfis=newFileInputStream(file); }catch(FileNotFoundException e){ //Kindly prompt the user an error message //Somehow ask the user to re-enter the file path. }
5. Почему люди это делают?
publicvoid someMethod throws Exception{
}
Почему они позволяют исключению всплывать? Не лучше ли обработать ошибку раньше? Почему всплывает?
6. Должен ли я отображать точное исключение или маскировать его с помощью Exception?
Многие люди говорят, что проверяемые исключения (т. Е. Те, которые вы должны явно перехватывать или повторно отбрасывать) вообще не следует использовать. Они были исключены, например, в C #, и в большинстве языков их нет. Таким образом, вы всегда можете создать подкласс RuntimeException (непроверенное исключение).
Однако я думаю, что проверяемые исключения полезны - они используются, когда вы хотите заставить пользователя вашего API подумать, как справиться с исключительной ситуацией (если ее можно восстановить). Просто проверяемые исключения чрезмерно используются на платформе Java, что заставляет людей ненавидеть их.
Считается ли NumberFormatException проверяемым исключением? Нет. NumberFormatException непроверено (= является подклассом RuntimeException). Почему? Я не знаю. (но должен был быть метод isValidInteger(..))
Является ли RuntimeException непроверенным исключением? Да, именно.
Что мне здесь делать? Это зависит от того, где находится этот код и что вы хотите, чтобы произошло. Если это на уровне пользовательского интерфейса - перехватите это и покажите предупреждение; если это на уровне сервиса - вообще не перехватывайте это - пусть всплывает. Просто не проглатывайте исключение. Если возникает исключение, в большинстве случаев вам следует выбрать одно из следующих:
запишите это и верните
перестройте его (объявите, что оно генерируется методом)
создайте новое исключение, передав текущее в constructor
Итак, не мог ли приведенный выше код также быть проверяемым исключением? Я могу попытаться исправить ситуацию таким образом? Могу ли я? Это могло быть. Но ничто не мешает вам перехватить и непроверенное исключение.
Почему люди добавляют class Exception в предложение throws? Чаще всего потому, что людям лень думать, что перехватывать, а что повторно выбрасывать. Выбрасывание Exception - плохая практика, и ее следует избегать.
Увы, не существует единого правила, позволяющего определять, когда перехватывать, когда повторно создавать, когда использовать проверенные, а когда непроверенные исключения. Я согласен, это вызывает много путаницы и много плохого кода. Общий принцип изложен Блохом (вы процитировали его часть). И общий принцип заключается в повторном переносе исключения на уровень, где вы можете его обработать.
Ответ 2
Является ли что-либо "проверяемым исключением", не имеет ничего общего с тем, перехватываете ли вы его или что вы делаете в блоке catch. Это свойство классов исключений. Все, что является подклассом Exceptionexcept for RuntimeException и его подклассов, является проверяемым исключением.
Компилятор Java заставляет вас либо перехватывать проверяемые исключения, либо объявлять их в сигнатуре метода. Предполагалось, что это повысит безопасность программы, но, по мнению большинства, это не стоит проблем с проектированием, которые это создает.
Почему они позволяют исключению всплывать? Разве чем раньше обработать ошибку, тем лучше? Почему всплывает?
Потому что в этом весь смысл исключений. Без этой возможности вам не понадобились бы исключения. Они позволяют вам обрабатывать ошибки на выбранном вами уровне, вместо того, чтобы заставлять вас иметь дело с ними в низкоуровневых методах, где они изначально возникают.
Ответ 3
Считается ли приведенное выше проверяемым исключением? Нет, тот факт, что вы обрабатываете исключение, не делает его a Checked Exception если это a RuntimeException.
Является RuntimeException an unchecked exception? Да
Checked Exceptions являются subclasses из java.lang.Exception Unchecked Exceptions являются subclasses из java.lang.RuntimeException
Вызовы, вызывающие проверяемые исключения, должны быть заключены в блок try{} или обработаны уровнем выше в вызывающем методе. В этом случае текущий метод должен объявить, что он генерирует указанные исключения, чтобы вызывающие устройства могли принять соответствующие меры для обработки исключения.
Надеюсь, это поможет.
Вопрос: должен ли я отображать точное исключение или маскировать его с помощью Exception?
Ответ: Да, это очень хороший вопрос и важное соображение при разработке. Класс Exception является очень общим классом исключений и может использоваться для переноса внутренних исключений низкого уровня. Вам лучше создать пользовательское исключение и обернуть внутри него. Но, и самое главное - никогда не скрывайте основную первопричину. Например, Don't ever выполните следующие действия -
try { attemptLogin(userCredentials); } catch (SQLException sqle) { thrownewLoginFailureException(sqle); //<-- Wrap original exception to pass on root cause upstairs!. }
Устранение первоначальной первопричины, исключающей возможность восстановления фактической причины, является кошмаром для групп производственной поддержки, где все, к чему им предоставляется доступ, - это журналы приложений и сообщения об ошибках. Хотя последнее является лучшим дизайном, но многие люди не используют его часто, потому что разработчики просто не могут передать базовое сообщение вызывающему. Итак, сделайте твердое замечание: Always pass on the actual exception возвращайтесь независимо от того, включено ли какое-либо исключение для конкретного приложения.
О попытке перехвата RuntimeExceptions
RuntimeExceptionкак правило, их не следует перехватывать при попытке. Обычно они сигнализируют об ошибке программирования, и их следует оставить в покое. Вместо этого программист должен проверить условие ошибки перед вызовом некоторого кода, который может привести к RuntimeException. Например:
try { setStatusMessage("Hello Mr. " + userObject.getName() + ", Welcome to my site!); } catch (NullPointerException npe) { sendError("Sorry, your userObject was null. Please contact customer care."); }
Это плохая практика программирования. Вместо проверки на нуль следовало выполнить как -
if (userObject != null) { setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!); } else { sendError("Sorry, your userObject was null. Please contact customer care."); }
Но бывают случаи, когда такая проверка ошибок обходится дорого, например, при форматировании чисел, учтите это -
try { StringuserAge= (String)request.getParameter("age"); userObject.setAge(Integer.parseInt(strUserAge)); } catch (NumberFormatException npe) { sendError("Sorry, Age is supposed to be an Integer. Please try again."); }
Здесь проверка ошибок перед вызовом не стоит затраченных усилий, потому что по сути это означает дублирование всего кода преобразования строки в целое число внутри метода parseInt() - и подвержен ошибкам, если реализован разработчиком. Поэтому лучше просто отказаться от try-catch.
Итак, NullPointerException и NumberFormatException являются обоими RuntimeExceptions, перехват a NullPointerException следует заменить изящной проверкой на нуль, в то время как я рекомендую перехват a NumberFormatException явно, чтобы избежать возможного введения кода, подверженного ошибкам.
Ответ 4
1 . Если вы не уверены насчет исключения, проверьте API:
java.lang.Object extended by java.lang.Throwable extended by java.lang.Exception extended by java.lang.RuntimeException //<-NumberFormatException is a RuntimeException extended by java.lang.IllegalArgumentException extended by java.lang.NumberFormatException
2 . Да, и каждое исключение, которое его расширяет.
3 . Нет необходимости перехватывать и выдавать одно и то же исключение. В этом случае вы можете отобразить диалоговое окно создания нового файла.
4 . Исключение FileNotFoundException является уже проверенным исключением.
5 . Если ожидается, что вызывающий метод someMethod перехватит исключение, последнее может быть выдано. Оно просто "передает мяч". Примером его использования может быть, если вы хотите использовать его в своих собственных частных методах и вместо этого обрабатывать исключение в своем общедоступном методе.
Почему разработчики решили принудительно указывать в методе все неперехваченные проверяемые исключения, которые могут быть сгенерированы в пределах его области видимости? Любое исключение, которое может быть сгенерировано методом, является частью общедоступного интерфейса программирования метода. Те, кто вызывает метод, должны знать об исключениях, которые может выдавать метод, чтобы они могли решить, что с ними делать. Эти исключения являются такой же частью программного интерфейса этого метода, как его параметры и возвращаемое значение.
Следующий вопрос может быть таким: "Если так хорошо документировать API метода, включая исключения, которые он может генерировать, почему бы также не указать исключения во время выполнения?" Исключения во время выполнения представляют собой проблемы, являющиеся результатом проблемы программирования, и, как таковой, нельзя разумно ожидать, что клиентский код API восстановится после них или каким-либо образом их обработает. К таким проблемам относятся арифметические исключения, такие как деление на ноль; исключения указателей, такие как попытка доступа к объекту через нулевую ссылку; и исключения индексации, такие как попытка доступа к элементу массива через слишком большой или слишком маленький индекс.
Классы проверяемых исключений, указанные в предложении throws, являются частью контракта между разработчиком и пользователем метода или конструктора.
ИМХО, суть в том, что вы можете перехватывать любые RuntimeException, но от вас это не требуется, и, фактически, реализация не обязана поддерживать те же самые непроверенные исключения, поскольку они не являются частью контракта.