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

703

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

Используйте проверенные исключения для восстанавливаемых условий и исключения времени выполнения для ошибок программирования (Элемент 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. Разве вышеприведенный код не может быть проверенным исключением? Я могу попытаться восстановить ситуацию, как это? Могу я? (Примечание: мой третий вопрос находится внутри 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. Должен ли я выдать точное исключение или замаскировать его с помощью исключения?

Ниже мои чтения

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

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

Тханг Фам
источник
6
У меня есть отличный пример непроверенного исключения. У меня есть DataSeriesкласс, который содержит данные, которые всегда должны оставаться в порядке времени. Есть способ добавить новый DataPointв конец DataSeries. Если весь мой код работает правильно на протяжении всего проекта, DataPointникогда не следует добавлять в конец, который имеет более раннюю дату, чем тот, который уже находится в конце. Каждый модуль во всем проекте построен с этим трюизмом. Однако я проверяю это условие и выкидываю непроверенное исключение, если это происходит. Почему? Если это произойдет, я хочу знать, кто это делает, и исправить это.
Эрик Робертсон
3
Чтобы добавить еще больше путаницы. Многие люди выступали за проверенные исключения ~ 10 лет назад, но в настоящее время взгляды все больше и больше переходят к «проверенные исключения - это плохо». (Я, однако, не согласен с этим)
Кай
10
Это полезно только для обработки Исключения, когда у вас есть что-то полезное, иначе вы должны позволить вызывающей стороне обработать это. Записывать его и делать вид, что этого не произошло, обычно бесполезно. Просто перебрасывать это бессмысленно. Заключение в RuntimeException не так полезно, как некоторые думают, оно просто заставляет компилятор перестать помогать вам. (ИМХО)
Питер Лори
40
Мы должны прекратить использовать вводящие в заблуждение условия проверенных / непроверенных исключений. Их следует назвать чек санкционированные против чек-не санкционированных исключений.
Благословенный Компьютерщик
3
Я также подумал о том, что 5-й пункт public void method_name выдает Exception {}, почему некоторые люди делают это?
Maveň ツ

Ответы:

475

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

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

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

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

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

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

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

    • войдите и вернитесь
    • сбросить его (объявить его методом)
    • создать новое исключение, передав текущее в конструктор
  4. Не может ли приведенный выше код быть проверенным исключением? Я могу попытаться восстановить ситуацию, как это? Могу я?
    Это могло бы быть. Но ничто не мешает вам поймать и непроверенное исключение

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

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

Bozho
источник
36
Что касается выдачи Exception, это не всегда потому, что люди ленивы, также часто вы, когда вы реализуете фреймворки, позволяете пользователям фреймворка генерировать любые исключения. Например, вы можете проверить подпись интерфейса Callable в JSE
Kaj
10
@Kaj - да, такие общие вещи, как Callable, перехватчики и тому подобное, являются особыми случаями. Но в большинстве случаев это потому, что люди ленивы :)
Божо
7
re: 3.1 "зарегистрируйся и вернись". Делайте это разумно. Это очень близко к еде или сокрытию и исключению. Я сделал бы это для чего-то, что не указывает на проблему, которая не является действительно исключительной. Журналы затопляются и игнорируются слишком легко.
Крис
4
«когда вы хотите заставить пользователя вашего API думать, как справиться с исключительной ситуацией», - вы не можете заставить кого-либо думать, если он этого не хочет. Если они не хотят думать, они напишут плохой блок исключений, который вообще ничего не делает или, что еще хуже, удаляет или вмешивается в информацию о критических ошибках. Вот почему проверенные исключения являются ошибкой.
Адрианос
3
@adrianos "... вы не можете заставить кого-то думать, если он этого не хочет ...." С помощью этой линии мышления мы могли бы также устранить ошибки компиляции .... Я на самом деле не нацеливаюсь на вас, я слышал это аргумент снова и снова и до сих пор считают это самым плохим возможным объяснением для обозначения Проверенных Исключений как неудачи. Как примечание, я видел ранее такой язык, где компиляция (и на самом деле ошибки во время выполнения) были фактически сделаны невозможными путем desing. Эта дорога привела к очень темным местам.
Ньютопия
233

То, является ли что-то «проверенным исключением», не имеет никакого отношения к тому, поймали ли вы это или что вы делаете в блоке catch. Это свойство классов исключений. Все , что подкласс Exception кроме для RuntimeExceptionи его подклассы является проверяемым исключением.

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

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

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

Майкл Боргвардт
источник
3
Спасибо! Я иногда выбрасываю исключения из своих методов из-за дерьма в принципе. Я, один из разработчиков в моей команде, хочу ввести недопустимое выражение xpath, чтобы они могли обработать исключение. В маловероятном случае они ловят исключение и ничего не делают, они услышат об этом в обзоре кода.
jeremyjjbrown
12
«Все, что является подклассом Throwable, за исключением RuntimeException и его подклассов, является проверенным исключением». - Ваше утверждение неверно. Ошибка также наследует Throwable и не проверяется.
Барцилла
8
@JonasEicher: в основном, главное преимущество исключений состоит в том, что они позволяют вам выбирать, где в стеке вызовов вы хотите обрабатывать ошибки, которые часто довольно высоки, сохраняя при этом слои между ними полностью свободными от артефактов обработки ошибок. Проверенные исключения уничтожают именно это преимущество. Другая проблема заключается в том, что проверенное / непроверенное различие связано с классом исключений, который также представляет концептуальную категоризацию исключений - смешивание двух аспектов, которые не обязательно связаны вообще.
Майкл Боргвардт
2
«Но большинство считает, что это не стоит проблем с дизайном, которые он создает». - Цитировать, пожалуйста?
Кервин
3
@ Барцилла Да. Для полноты, как Throwableсказано в javadoc : «Throwable и любой подкласс Throwable, который также не является подклассом ни RuntimeException, ни Error, рассматриваются как проверенные исключения»
Барт ван Хейкелом,
74
  1. Вышеуказанное считается проверенным исключением? Нет Тот факт, что вы обрабатываете исключение, не делает его a, Checked Exceptionесли оно есть RuntimeException.

  2. Есть ? даRuntimeExceptionunchecked exception

Checked Exceptionsявляются subclassesв java.lang.Exception Unchecked Exceptionsэто subclassesизjava.lang.RuntimeException

Вызовы, выбрасывающие проверенные исключения, должны быть заключены в блок try {} или обработаны на уровне выше в вызывающей стороне метода. В этом случае текущий метод должен объявить, что он выбрасывает указанные исключения, чтобы вызывающие могли принять соответствующие меры для обработки исключения.

Надеюсь это поможет.

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

A: Да, это очень хороший вопрос и важное соображение дизайна. Класс 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

RuntimeExceptions, как правило, не следует пытаться поймать. Они обычно сигнализируют об ошибке программирования и должны быть оставлены в покое. Вместо этого программист должен проверить состояние ошибки, прежде чем вызывать некоторый код, который может привести к 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, перехват NullPointerExceptionдолжен заменяться изящной нулевой проверкой, в то время как я рекомендую перехватывать NumberFormatExceptionявно, чтобы избежать возможного введения подверженного ошибкам кода.

д-живой
источник
Спасибо. Еще один вопрос, когда вы всплываете exception, я должен всплыть точное исключение или замаскировать его, используя Exception. Я пишу код поверх какого-то унаследованного кода, и меня Exceptionвезде переполняют. Интересно, это правильное поведение?
Тханг Фам
1
Это очень хороший и важный вопрос, отредактировал мой ответ, чтобы включить объяснение.
d-live
Большое спасибо. Вы могли бы показать мне содержание LoginFailureException(sqle)?
Тханг Фам
1
У меня нет никакого кода для этого материала, я просто составил имена и т. Д. Если вы видите java.lang.Exception, у него есть 4 конструктора, два из которых принимают java.lang.Throwable. В приведенных выше фрагментах я предполагаю, что LoginFailureExceptionрасширяет Exceptionи объявляет конструкторpublic LoginFailureException(Throwable cause) { super(cause) }
d-live
Лучший ответ по теме. Я думаю, что исключения во время выполнения не должны быть перехвачены, потому что эти исключения возникают из-за отсутствия хорошего программирования. Я полностью согласен с частью «Пожирание первоначальной причины скрывает фактическую причину, которая не подлежит восстановлению, - это кошмар для рабочих групп поддержки, к которым у них есть доступ только к журналам приложений и сообщениям об ошибках».
huseyin
19

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, но вы не обязаны и, фактически, реализация не обязана поддерживать те же самые непроверенные исключения, которые не были включены в контракт.

Aleadam
источник
Спасибо. Еще один вопрос, когда вы всплываете exception, я должен всплыть точное исключение или замаскировать его, используя Exception. Я пишу код поверх какого-то унаследованного кода, и меня Exceptionвезде переполняют. Интересно, это правильное поведение?
Тханг Фам
1
@ Гарри, я позволю людям с большим знанием, чем я, ответить на это: stackoverflow.com/questions/409563/…
Aleadam
10

1) Нет, NumberFormatException является непроверенным исключением. Даже если вы поймали это (вы не обязаны), потому что это не проверено. Это потому, что это подкласс, IllegalArgumentExceptionкоторый является подклассом RuntimeException.

2) RuntimeExceptionявляется корнем всех непроверенных исключений. Каждый подклассRuntimeException не проверяется. Все остальные исключения и Throwableпроверяются за исключением ошибок (которые подпадают под Throwable).

3/4) Вы можете предупредить пользователя, что он выбрал несуществующий файл, и попросить новый. Или просто выйдите, сообщив пользователю, что он ввел что-то недействительное.

5) Бросать и ловить 'Exception' - плохая практика. Но в более общем случае вы можете выдать другие исключения, чтобы вызывающий мог решить, как с этим справиться. Например, если вы написали библиотеку для обработки чтения какого-либо файла и ваш метод был передан несуществующему файлу, вы не знаете, как с этим справиться. Звонящий хочет спросить снова или выйти? Таким образом, вы бросаете исключение вверх по цепочке к вызывающей стороне.

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

dontocsata
источник
Спасибо. Еще один вопрос, когда вы всплываете exception, я должен всплыть точное исключение или замаскировать его, используя Exception. Я пишу код поверх какого-то унаследованного кода, и меня Exceptionвезде переполняют. Интересно, это правильное поведение?
Тханг Фам
Вы можете либо просто использовать свой метод, либо выдавать исключение (что не идеально). Или поймайте Exception и добавьте лучшее Exception (например, IOException или что-то в этом роде). Все исключения могут принимать исключение в своем конструкторе как «причину», поэтому вы должны использовать это.
dontocsata
8

Проверено - Скорее всего. Проверено во время компиляции.

Например. FileOperations

Не проверено - из-за неверных данных. Проверено во время выполнения.

Например..

String s = "abc";
Object o = s;
Integer i = (Integer) o;

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at Sample.main(Sample.java:9)

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

bharanitharan
источник
6

Проверенные исключения проверяются во время компиляции JVM и связаны с ресурсами (файлы / db / stream / socket и т. Д.). Мотив проверенного исключения заключается в том, что во время компиляции, если ресурсы недоступны, приложение должно определить альтернативное поведение для обработки этого в блоке catch / finally.

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

Объяснение взято с http://coder2design.com/java-interview-questions/

Джатиндер Пал
источник
5

Мое абсолютно любимое описание различий между непроверенными и проверенными исключениями предоставлено в статье из учебника по Java « Непроверенные исключения - противоречие » (извините, что все элементарное в этом посте - но, эй, основы иногда бывают лучшими):

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

Суть "какого типа исключения генерировать" является семантической (в некоторой степени), и приведенная выше цитата обеспечивает превосходное руководство (следовательно, я все еще поражаюсь представлению о том, что C # избавился от проверенных исключений - особенно, как утверждает Лисков для их полезность).

Все остальное становится логичным: на какие исключения компилятор ожидает от меня явного ответа? Те, от которых вы ожидаете, что клиент восстановится.

Томас
источник
5

Чтобы ответить на последний вопрос (другие, кажется, полностью ответили выше), «Должен ли я выдать точное исключение или замаскировать его с помощью исключения?»

Я предполагаю, что вы имеете в виду что-то вроде этого:

public void myMethod() throws Exception {
    // ... something that throws FileNotFoundException ...
}

Нет, всегда объявляйте наиболее точное возможное исключение или список таких. Исключения, которые вы объявляете для вашего метода как способного к выбросу, являются частью контракта между вашим методом и вызывающей стороной. Бросок "FileNotFoundException"означает, что возможно, имя файла недействительно и файл не будет найден; звонящий должен будет разобраться с этим разумно. Бросок Exceptionозначает «Эй, дерьмо случается. Сделка». Который очень беденAPI .

В комментариях к первой статье есть несколько примеров, когда «throws Exception» является допустимым и разумным объявлением, но это не относится к большинству « normal» кодов, которые вы когда-либо будете писать.

Том Диббл
источник
Точно, сделайте ваше объявление об исключении чека частью документации вашего кода, а также помогите человеку, использующему ваше программное обеспечение.
Сальвадор Валенсия
5

Исключения во время выполнения : исключения во время выполнения называются непроверенными исключениями. Все остальные исключения являются проверенными исключениями, и они не являются производными от java.lang.RuntimeException.

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

Ряд методов в Java API генерирует проверенные исключения, поэтому вы часто будете писать обработчики исключений, чтобы справляться с исключениями, сгенерированными методами, которые вы не писали.

Omer
источник
3

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

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

Примечание: чтобы дать четкое описание того, какой тип ошибки произошел, мы можем создать наш собственный объект Exception и отправить его клиенту.

ЯВА
источник
Хорошая точка зрения. Это означает, что он поднимается до самого ответственного уровня, который контролирует логический поток и контролирует бизнес-логику приложения. Например, для уровня базы данных было бы невозможно сообщить клиенту, что что-то критическое отсутствует или не отвечает. Когда он поднимается до самого верхнего уровня сервера, тогда просто обновить представление клиента критическим сообщением об ошибке.
Сальвадор Валенсия
3

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

Узнайте больше о проверенных и непроверенных исключениях здесь http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/

Дэн Молдаван
источник
3

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

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

Позвольте мне показать вам пример.

Давайте иметь хороший и чистый интерфейс, как это

public interface IFoo {
    public void foo();
}

Теперь мы можем написать много реализаций метода foo(), как эти

public class Foo implements IFoo {
    @Override
    public void foo() {
        System.out.println("I don't throw and exception");
    }
}

Класс Foo отлично подойдет. Теперь давайте сделаем первую попытку в классе Bar

public class Bar implements IFoo {
    @Override
    public void foo() {
        //I'm using InterruptedExcepton because you probably heard about it somewhere. It's a checked exception. Any checked exception will work the same.
        throw new InterruptedException();
    }
}

Этот класс Bar не будет компилироваться. Поскольку InterruptedException является проверенным исключением, вы должны либо перехватить его (с помощью try-catch внутри метода foo ()), либо объявить, что вы его бросаете (добавляя throws InterruptedExceptionв сигнатуру метода). Поскольку я не хочу фиксировать это исключение здесь (я хочу, чтобы оно распространялось вверх, чтобы я мог правильно с ним справиться где-то еще), давайте изменим сигнатуру.

public class Bar implements IFoo {
    @Override
    public void foo() throws InterruptedException {
        throw new InterruptedException();
    }
}

Этот класс Bar тоже не скомпилируется! Метод Бар foo () НЕ переопределяет метод IFoo foo (), так как их сигнатуры разные. Я мог бы удалить аннотацию @Override, но я хочу программировать с интерфейсом IFoo like IFoo foo;и позже решить, какую реализацию я хочу использовать, например foo = new Bar();. Если метод Бар foo () не переопределяет метод IFoo IFoo, то когда я это сделаю, foo.foo();он не вызовет реализацию Баром функции foo ().

Чтобы сделать public void foo() throws InterruptedExceptionпереопределение Баром IFoo, public void foo()я ДОЛЖЕН добавить throws InterruptedExceptionк сигнатуре метода IFoo. Это, однако, вызовет проблемы с моим классом Foo, поскольку его сигнатура метода foo () отличается от сигнатуры метода IFoo. Кроме того, если бы я добавил throws InterruptedExceptionв метод Foo foo (), я получил бы еще одну ошибку, утверждая, что метод Foo foo () объявляет, что он генерирует InterruptedException, но никогда не генерирует InterruptedException.

Как вы можете видеть (если я проделал приличную работу по объяснению этого материала), тот факт, что я выбрасываю проверенное исключение, такое как InterruptedException, заставляет меня связывать мой интерфейс IFoo с одной из его реализаций, что, в свою очередь, вызывает хаос в IFoo другие реализации!

Это одна из основных причин, почему проверенные исключения являются ПЛОХОЙ. В шапках.

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

Blueriver
источник
2
Да, это плохо, потому что ты сказал, что не хочешь его поймать. Но чтобы не повлиять на подпись IFOO вам придется. Я бы предпочел сделать это и двигаться дальше, а не перебалансировать все мои подписи интерфейсов, чтобы избежать перехвата исключения (только потому, что исключения ПЛОХО).
Сальвадор Валенсия
Ага, я согласен. Я был немного неясен о чем-то. Я хочу, чтобы исключение распространялось, поэтому я могу иметь дело с этим где-то еще. Одним из решений является перехват InterruptedException и создание непроверенного исключения. то есть мы избегаем проверенных исключений и пропускаем непроверенные исключения (даже если они имеют смысл только как оболочка)
Blueriver,
Msgstr "Это, однако, вызовет проблемы с моим классом Foo, так как его сигнатура метода foo () отличается от сигнатуры метода IFoo". Я только что проверил вашу идею, и ее можно скомпилировать, даже если мы добавим throws InterruptedExceptionв сигнатуру метода IFoo, не бросая ничего в какой-либо реализации. Так что это на самом деле не вызывает никаких проблем. Если в интерфейсе вы делаете каждый бросок сигнатуры метода Exception, он просто дает реализации возможность выбрасывать или не генерировать исключение (любое исключение, поскольку Exceptionинкапсулирует все исключения).
Flyout91
Однако я допускаю, что это может сбивать с толку, потому что, когда вы реализуете такой интерфейс и нажмете что-то вроде «Добавить невыполненные методы», они будут автоматически созданы с throw Exceptionпредложением в их сигнатуре, даже если ваша реализация ничего не выдаст или может быть более конкретное исключение. Но я все еще чувствую, что это хорошая практика - всегда генерировать исключение для метода интерфейса, потому что, опять же, это дает пользователю возможность выбрасывать или не выбрасывать что-либо.
Flyout91
Это пропускает весь смысл. Цель интерфейса - объявить контракт, который клиент должен выполнить. Это может включать в себя сценарии сбоя, которые он может обработать. Когда реализация сталкивается с ошибкой, она должна сопоставить эту ошибку с соответствующей абстрактной ошибкой, объявленной клиентским интерфейсом.
Эриксон
3
  • Java различает две категории исключений (проверено и не проверено).
  • Java применяет улов или объявленное требование для проверенных исключений.
  • Тип исключения определяет, будет ли исключение отмечено или не отмечено.
  • Все типы исключений, которые являются прямыми или косвенными subclassesдля класса, RuntimeException являются непроверенным исключением.
  • Все классы, которые наследуются от класса, Exceptionно не RuntimeExceptionсчитаются checked exceptions.
  • Классы, которые наследуются от класса Error, считаются непроверенными.
  • Компилятор проверяет каждый вызов метода и замедление, чтобы определить, выбрасывает ли метод checked exception.
    • Если это так, компилятор гарантирует, что исключение будет перехвачено или объявлено в предложении throws.
  • Чтобы удовлетворить объявленную часть требования catch-or-Declare, метод, который генерирует исключение, должен предоставить throwsпредложение, содержащее checked-exception.
  • Exception классы определяются для проверки, когда они считаются достаточно важными для отлова или объявления.
tokhi
источник
2

Вот простое правило, которое может помочь вам решить. Это связано с тем, как интерфейсы используются в Java.

Возьмите свой класс и представьте, что вы разрабатываете интерфейс для него таким образом, чтобы интерфейс описывал функциональность класса, но не основную реализацию (как должен делать интерфейс). Представьте, что, возможно, вы реализуете класс другим способом.

Посмотрите на методы интерфейса и рассмотрите исключения, которые они могут выдавать:

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

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

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

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

rghome
источник
2

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

Зоран Пандовски
источник
2

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

В этом видео объясняются отмеченные и непроверенные исключения в Java:
https://www.youtube.com/watch?v=ue2pOqLaArw

Ozgen
источник
1

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

mamboking
источник
гм. если NumberFormatException является проверенным исключением, как вы говорите, не противоречит ли оно тому, что оно унаследовано от RuntimeException ?
Eis
Извините, мне было не очень понятно. Я имел в виду исключение FileNotFoundException, а не NumberFormatException. Исходя из его # 2 и # 4, казалось, что он думал, что Checked vs. Unchecked был основан на том, как вы обработали исключение после его перехвата. Не так, как это было определено.
mamboking
-1

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

«Хотя это проверенное исключение, его редко можно восстановить. Большинству абонентов следует просто обернуть это исключение в неконтролируемое исключение и повторно обработать его».

Так почему же кто-то может заставить разработчиков продолжать проверять исключение, если вместо этого мы должны просто «обернуть его»? лол

http://developer.android.com/reference/org/json/JSONException.html

Славомир
источник
4
Потому что только большинство абонентов, а не все абоненты, должны переносить и отбрасывать. Тот факт, что исключение проверено, означает, что вызывающий абонент должен думать о том, являются ли они одним из "большинства" вызывающих абонентов или одним из меньшинств, которые могут и должны обработать исключение.
Уоррен Дью
1
Если вам нравится проверять ошибки при каждом вызове, который вы делаете, вернитесь к C. Исключения - это способ отделить нормальное выполнение программы от ненормального, не загрязняя ваш код. Исключения гарантируют, что вы не можете игнорировать ошибку на некотором уровне .
Славомир
-1

Проверенные исключения :

  • Исключения, которые проверяются компилятором для плавного выполнения программы во время выполнения, называются Checked Exception.

  • Это происходит во время компиляции.

  • Если они не обрабатываются должным образом, они приведут к ошибке времени компиляции (не исключение).
  • Все подклассы класса Exception, кроме RuntimeException, являются проверенными исключениями.

    Гипотетический пример Предположим, что вы выходите из дома на экзамен, но если вы проверите, взяли ли вы свой билет в зал дома (время компиляции), то в экзаменационном зале проблем не возникнет (время выполнения).

Непроверенное исключение :

  • Исключения, которые не проверяются компилятором, называются непроверенными исключениями.

  • Это происходит во время выполнения.

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

  • Все подклассы RunTimeException и Error являются непроверенными исключениями.

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

Варун Вашиста
источник
-2

Все исключения должны быть проверены исключения.

  1. Непроверенные исключения - неограниченные переходы. И неограниченные gotos считаются плохой вещью.

  2. Непроверенные исключения нарушают инкапсуляцию. Чтобы правильно их обработать, все функции в дереве вызовов между метателем и ловцом должны быть известны, чтобы избежать ошибок.

  3. Исключением являются ошибки в функции, которая их генерирует, но не ошибки в функции, которая их обрабатывает. Цель исключений - дать программе второй шанс, отложив решение о том, является ли это ошибкой или нет, в другой контекст. Только в другом контексте может быть принято правильное решение.

shawnhcorey
источник