Вопрос-ответ

Understanding checked vs unchecked exceptions in Java

Понимание проверенных и непроверенных исключений в Java

Джошуа Блох в "Эффективной Java" сказал, что


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


Давайте посмотрим, правильно ли я это понимаю.

Вот мое понимание проверяемого исключения:

try{
String userInput = //read in user input
Long id = Long.parseLong(userInput);
}catch(NumberFormatException e){
id = 0; //recover the situation by setting the id to 0
}

1. Считается ли приведенное выше проверяемым исключением?

2. Является ли RuntimeException непроверяемым исключением?

Вот мое понимание непроверенного исключения:

try{
File file = new File("my/file/path");
FileInputStream fis = new FileInputStream(file);
}catch(FileNotFoundException e){

//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{
String filePath = //read in from user input file path
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
}catch(FileNotFoundException e){
//Kindly prompt the user an error message
//Somehow ask the user to re-enter the file path.
}

5. Почему люди это делают?

public void someMethod throws Exception{

}

Почему они позволяют исключению всплывать? Не лучше ли обработать ошибку раньше? Почему всплывает?

6. Должен ли я отображать точное исключение или маскировать его с помощью Exception?

Ниже приведены мои показания

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

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

Переведено автоматически
Ответ 1

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

Однако я думаю, что проверяемые исключения полезны - они используются, когда вы хотите заставить пользователя вашего API подумать, как справиться с исключительной ситуацией (если ее можно восстановить). Просто проверяемые исключения чрезмерно используются на платформе Java, что заставляет людей ненавидеть их.

Вот мой расширенный взгляд на тему.

Что касается конкретных вопросов:


  1. Считается ли NumberFormatException проверяемым исключением?
    Нет. NumberFormatException непроверено (= является подклассом RuntimeException). Почему? Я не знаю. (но должен был быть метод isValidInteger(..))



  2. Является ли RuntimeException непроверенным исключением?
    Да, именно.



  3. Что мне здесь делать?
    Это зависит от того, где находится этот код и что вы хотите, чтобы произошло. Если это на уровне пользовательского интерфейса - перехватите это и покажите предупреждение; если это на уровне сервиса - вообще не перехватывайте это - пусть всплывает. Просто не проглатывайте исключение. Если возникает исключение, в большинстве случаев вам следует выбрать одно из следующих:




  • запишите это и верните

  • перестройте его (объявите, что оно генерируется методом)

  • создайте новое исключение, передав текущее в constructor


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



  2. Почему люди добавляют class Exception в предложение throws?
    Чаще всего потому, что людям лень думать, что перехватывать, а что повторно выбрасывать. Выбрасывание Exception - плохая практика, и ее следует избегать.



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

Ответ 2

Является ли что-либо "проверяемым исключением", не имеет ничего общего с тем, перехватываете ли вы его или что вы делаете в блоке catch. Это свойство классов исключений. Все, что является подклассом Exception except for RuntimeException и его подклассов, является проверяемым исключением.

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


Почему они позволяют исключению всплывать? Разве чем раньше обработать ошибку, тем лучше? Почему всплывает?


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

Ответ 3

  1. Считается ли приведенное выше проверяемым исключением? Нет, тот факт, что вы обрабатываете исключение, не делает его a Checked Exception если это a RuntimeException.


  2. Является 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) {
throw new LoginFailureException("Cannot login!!"); //<-- Eat away original root cause, thus obscuring underlying problem.
}

Вместо этого выполните следующее:

try {
attemptLogin(userCredentials);
} catch (SQLException sqle) {
throw new LoginFailureException(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 {
String userAge = (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 перехватит исключение, последнее может быть выдано. Оно просто "передает мяч". Примером его использования может быть, если вы хотите использовать его в своих собственных частных методах и вместо этого обрабатывать исключение в своем общедоступном методе.

Полезно ознакомиться с самим документом Oracle: http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html


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


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


Также есть важная информация в спецификации языка Java:


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


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

java exception