Когда выбирать отмеченные и непроверенные исключения
В Java (или любом другом языке с проверенными исключениями), когда вы создаете свой собственный класс исключений, как вы решаете, следует ли его проверять или не проверять?
Мой инстинкт должен сказать, что проверенное исключение будет вызываться в тех случаях, когда вызывающий может быть в состоянии восстановиться каким-либо продуктивным способом, где в качестве неконтролируемого исключения будет больше для неисправимых случаев, но меня будут интересовать мысли других.
18 ответов
Проверенные исключения - это здорово, если вы понимаете, когда их следует использовать. API ядра Java не соблюдает эти правила для SQLException (а иногда и для IOException), поэтому они так ужасны.
Проверенные исключения должны использоваться для предсказуемых, но не поддающихся проверке ошибок, которые целесообразно исправлять.
Непроверенные исключения должны быть использованы для всего остального.
Я сломаю это для вас, потому что большинство людей неправильно понимают, что это значит.
- Предсказуемый, но непредсказуемый: вызывающий сделал все, что в его силах, для проверки входных параметров, но некоторые условия вне их контроля привели к сбою операции. Например, вы пытаетесь прочитать файл, но кто-то удаляет его между моментом проверки, существует ли он, и временем начала операции чтения. Объявляя проверенное исключение, вы говорите вызывающей стороне предвидеть этот сбой.
- Разумное восстановление после: нет смысла говорить вызывающим абонентам ожидать исключений, которые они не могут восстановить. Если пользователь пытается прочитать из несуществующего файла, вызывающая сторона может запросить у них новое имя файла. С другой стороны, если метод завершается неудачей из-за программной ошибки (неверные аргументы метода или ошибочная реализация метода), приложение ничего не может сделать, чтобы устранить проблему в середине выполнения. Лучшее, что он может сделать - зарегистрировать проблему и подождать, пока разработчик не исправит ее позже.
Если исключение, которое вы выбрасываете, не удовлетворяет всем вышеперечисленным условиям, оно должно использовать исключение Unchecked.
Переоценка на каждом уровне: иногда метод, перехватывающий проверенное исключение, не подходит для обработки ошибки. В этом случае подумайте, что является разумным для ваших абонентов. Если исключение предсказуемо, непредсказуемо и разумно для их восстановления, то вы должны бросить проверенное исключение самостоятельно. Если нет, вы должны поместить исключение в исключение без проверки. Если вы будете следовать этому правилу, вы обнаружите, что конвертируете проверенные исключения в непроверенные исключения и наоборот, в зависимости от того, в каком слое вы находитесь.
Для проверенных и непроверенных исключений используйте правильный уровень абстракции. Например, хранилище кода с двумя различными реализациями (база данных и файловая система) должно избегать раскрытия деталей, относящихся к реализации, путем броска SQLException
или же IOException
, Вместо этого следует обернуть исключение в абстракцию, которая охватывает все реализации (например, RepositoryException
).
От ученика Java:
Когда возникает исключение, вы должны либо перехватить и обработать исключение, либо сказать компилятору, что вы не можете его обработать, заявив, что ваш метод выдает это исключение, тогда код, который использует ваш метод, должен будет обработать это исключение (даже также может объявить, что выдает исключение, если не может его обработать).
Компилятор проверит, что мы сделали одну из двух вещей (поймать или объявить). Так что они называются проверенными исключениями. Но ошибки и исключения времени выполнения не проверяются компилятором (даже если вы можете отловить или объявить, это не обязательно). Итак, эти два называются непроверенными исключениями.
Ошибки используются для представления тех условий, которые возникают за пределами приложения, например, сбой системы. Исключения во время выполнения обычно возникают по ошибке в логике приложения. Вы ничего не можете сделать в этих ситуациях. Когда возникает исключение времени выполнения, вы должны переписать программный код. Таким образом, они не проверяются компилятором. Эти исключения во время выполнения будут обнаружены во время разработки и тестирования. Затем мы должны провести рефакторинг нашего кода, чтобы удалить эти ошибки.
Правило, которое я использую: никогда не используйте непроверенные исключения! (или когда ты не видишь никакого пути вокруг этого)
Есть очень веские аргументы в пользу обратного: никогда не используйте проверенные исключения. Я не хочу принимать участие в дебатах, но, похоже, существует широкий консенсус в отношении того, что введение проверенных исключений было неправильным решением задним числом. Пожалуйста, не стреляйте в мессенджера и обращайтесь к этим аргументам.
В любой достаточно большой системе со многими слоями проверенные исключения бесполезны, так как, в любом случае, вам нужна стратегия архитектурного уровня для обработки того, как будет обрабатываться исключение (используйте барьер ошибок)
С проверенными исключениями ваша система обработки ошибок является микроуправляемой и невыносимой в любой большой системе.
Большую часть времени вы не знаете, является ли ошибка "исправимой", потому что вы не знаете, на каком уровне находится вызывающая сторона вашего API.
Допустим, я создаю API-интерфейс StringToInt, который преобразует строковое представление целого числа в Int. Должен ли я генерировать проверенное исключение, если API вызывается со строкой "foo"? Это восстановимо? Я не знаю, потому что в своем слое вызывающая сторона моего API StringToInt, возможно, уже проверила ввод, и, если выброшено это исключение, это либо ошибка, либо повреждение данных, и его невозможно восстановить для этого слоя.
В этом случае вызывающая сторона API не хочет перехватывать исключение. Он только хочет, чтобы исключение "всплыло". Если я выбрал проверенное исключение, у этого вызывающего будет много бесполезных блокировок перехвата только для искусственного отбрасывания исключения.
То, что подлежит восстановлению, в большинстве случаев зависит от вызывающего API, а не от создателя API. API не должен использовать отмеченные исключения, поскольку только непроверенные исключения позволяют выбрать либо перехват, либо игнорирование исключения.
Ты прав.
Непроверенные исключения используются для быстрого сбоя системы, и это хорошо. Вы должны четко указать, что ожидает ваш метод, чтобы работать должным образом. Таким образом, вы можете проверить ввод только один раз.
Например:
/**
* @params operation - The operation to execute.
* @throws IllegalArgumentException if the operation is "exit"
*/
public final void execute( String operation ) {
if( "exit".equals(operation)){
throw new IllegalArgumentException("I told you not to...");
}
this.operation = operation;
.....
}
private void secretCode(){
// we perform the operation.
// at this point the opreation was validated already.
// so we don't worry that operation is "exit"
.....
}
Просто чтобы привести пример. Дело в том, что если система быстро выходит из строя, то вы будете знать, где и почему она вышла из строя. Вы получите трассировку стека, как:
IllegalArgumentException: I told you not to use "exit"
at some.package.AClass.execute(Aclass.java:5)
at otherPackage.Otherlass.delegateTheWork(OtherClass.java:4569)
ar ......
И ты узнаешь, что случилось. OtherClass в методе "DelegateTheWork" (в строке 4569) вызвал ваш класс со значением "exit", даже если это не так и т. Д.
В противном случае вам придется разбрасывать проверки по всему коду, и это может привести к ошибкам. Кроме того, иногда трудно отследить, что пошло не так, и вы можете ожидать долгие часы разочаровывающей отладки.
То же самое происходит с исключениями NullPointerException. Если у вас есть класс из 700 строк с 15 методами, который использует 30 атрибутов, и ни один из них не может иметь значение null, вместо проверки в каждом из этих методов на обнуляемость вы можете сделать все эти атрибуты доступными только для чтения и проверить их в конструкторе или заводской метод.
public static MyClass createInstane( Object data1, Object data2 /* etc */ ){
if( data1 == null ){ throw NullPointerException( "data1 cannot be null"); }
}
// the rest of the methods don't validate data1 anymore.
public void method1(){ // don't worry, nothing is null
....
}
public void method2(){ // don't worry, nothing is null
....
}
public void method3(){ // don't worry, nothing is null
....
}
Проверенные исключения полезны, когда программист (вы или ваши коллеги) все сделали правильно, проверил ввод, выполнил тесты, и весь код безупречен, но код подключается к стороннему веб-сервису, который может быть недоступен (или файл вы использовали был удален другим внешним процессом и т. д.). Веб-служба может даже быть проверена перед попыткой подключения, но во время передачи данных что-то пошло не так.
В этом сценарии вы или ваши коллеги ничего не сможете сделать, чтобы помочь этому. Но все же вы должны что-то сделать и не дать приложению просто умереть и исчезнуть в глазах пользователя. Для этого вы используете проверенное исключение и обрабатываете исключение, что вы можете делать, когда это происходит? Большую часть времени вы просто пытаетесь зарегистрировать ошибку, возможно, сохранить свою работу (работу приложения) и представить сообщение пользователю., (Сайт blabla не работает, повторите попытку позже и т. Д.)
Если проверенное исключение чрезмерно используется (добавление "throw Exception" во все сигнатуры методов), тогда ваш код станет очень хрупким, потому что все будут игнорировать это исключение (потому что оно слишком общее), и качество кода будет серьезно скомпрометированы.
Если вы злоупотребите непроверенным исключением, произойдет нечто подобное. Пользователи этого кода не знают, может ли что-то пойти не так, и появится много попыток {...}catch( Throwable t).
Вот мое "последнее правило".
Я использую:
- непроверенное исключение в коде моего метода для сбоя из-за вызывающего (что включает в себя явную и полную документацию)
- проверил исключение для сбоя из-за вызываемого абонента, который мне нужно сделать явным для тех, кто хочет использовать мой код
По сравнению с предыдущим ответом, это четкое обоснование (с которым можно согласиться или не согласиться) для использования одного или другого (или обоих) исключений.
Для обоих этих исключений я создам свое собственное исключение без проверки и с проверкой для моего приложения (хорошая практика, как упомянуто здесь), за исключением очень распространенного исключения без проверки (например, NullPointerException)
Так, например, целью этой конкретной функции ниже является создание (или получение, если оно уже существует) объекта,
имея в виду:
- ДОЛЖЕН существовать контейнер объекта для создания / получения (ответственность ЗВОНОКА
=> непроверенное исключение, и очистить комментарий Javadoc для этой вызываемой функции) - другие параметры не могут быть нулевыми
(выбор кодера, чтобы поместить это в CALLER: кодер не будет проверять нулевой параметр, но кодер ДОКУМЕНТИТ ЕГО) - результат не может быть пустым
(ответственность и выбор кода вызываемого абонента, выбор которого будет представлять большой интерес для вызывающего абонента
=> проверенная исключительная ситуация, потому что каждый вызывающий объект ДОЛЖЕН принять решение, если объект не может быть создан / найден, и это решение должно быть применено во время компиляции: они не могут использовать эту функцию, не имея дело с этой возможностью, то есть с этой проверенной исключение).
Пример:
/**
* Build a folder. <br />
* Folder located under a Parent Folder (either RootFolder or an existing Folder)
* @param aFolderName name of folder
* @param aPVob project vob containing folder (MUST NOT BE NULL)
* @param aParent parent folder containing folder
* (MUST NOT BE NULL, MUST BE IN THE SAME PVOB than aPvob)
* @param aComment comment for folder (MUST NOT BE NULL)
* @return a new folder or an existing one
* @throws CCException if any problems occurs during folder creation
* @throws AssertionFailedException if aParent is not in the same PVob
* @throws NullPointerException if aPVob or aParent or aComment is null
*/
static public Folder makeOrGetFolder(final String aFoldername, final Folder aParent,
final IPVob aPVob, final Comment aComment) throws CCException {
Folder aFolderRes = null;
if (aPVob.equals(aParent.getPVob() == false) {
// UNCHECKED EXCEPTION because the caller failed to live up
// to the documented entry criteria for this function
Assert.isLegal(false, "parent Folder must be in the same PVob than " + aPVob); }
final String ctcmd = "mkfolder " + aComment.getCommentOption() +
" -in " + getPNameFromRepoObject(aParent) + " " + aPVob.getFullName(aFolderName);
final Status st = getCleartool().executeCmd(ctcmd);
if (st.status || StringUtils.strictContains(st.message,"already exists.")) {
aFolderRes = Folder.getFolder(aFolderName, aPVob);
}
else {
// CHECKED EXCEPTION because the callee failed to respect his contract
throw new CCException.Error("Unable to make/get folder '" + aFolderName + "'");
}
return aFolderRes;
}
Дело не только в способности оправиться от исключения. На мой взгляд, больше всего важно, заинтересован ли вызывающий абонент поймать исключение или нет.
Если вы пишете библиотеку для использования в другом месте или более низкий уровень в вашем приложении, спросите себя, заинтересован ли вызывающий абонент поймать (узнать о) ваше исключение. Если нет, используйте исключение без проверки, чтобы вы не обременяли его без необходимости.
Это философия, используемая многими системами. В частности, приходят на ум Spring и Hibernate - они преобразуют известное проверенное исключение в непроверенное исключение именно потому, что проверенные исключения чрезмерно используются в Java. Одним из примеров, который я могу вспомнить, является JSONException от json.org, который является проверенным исключением и в основном раздражает - его следует отключить, но разработчик просто не продумал его.
Кстати, в большинстве случаев интерес вызывающего абонента к исключению напрямую связан с возможностью восстановления после исключения, но это не всегда так.
Вот очень простое решение вашей проверенной / непроверенной дилеммы.
Правило 1. Думайте о непроверенном исключении как о тестируемом условии перед выполнением кода. например…
x.doSomething(); // the code throws a NullPointerException
где х нуль...... код должен иметь следующее...
if (x==null)
{
//do something below to make sure when x.doSomething() is executed, it won’t throw a NullPointerException.
x = new X();
}
x.doSomething();
Правило 2. Думайте о проверяемом исключении как о непроверяемом условии, которое может возникнуть во время выполнения кода.
Socket s = new Socket(“google.com”, 80);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
… В приведенном выше примере URL (google.com) может быть недоступен из-за неработоспособности DNS-сервера. Даже в тот момент, когда DNS-сервер работал и преобразовал имя "google.com" в IP-адрес, если в любой момент после подключения к google.com сеть может выйти из строя. Вы просто не можете все время проверять сеть перед чтением и записью в потоки.
Есть моменты, когда код просто должен исполниться, прежде чем мы узнаем, есть ли проблема. Заставляя разработчиков писать свой код таким образом, чтобы заставить их справляться с этими ситуациями с помощью Checked Exception, я должен склонить голову создателю Java, который придумал эту концепцию.
В общем, почти все API в Java следуют 2 приведенным выше правилам. Если вы попытаетесь записать файл, диск может заполниться до завершения записи. Возможно, что другие процессы вызвали переполнение диска. Просто нет способа проверить эту ситуацию. Для тех, кто взаимодействует с оборудованием, где в любое время использование оборудования может дать сбой, Checked Exceptions представляется элегантным решением этой проблемы.
Здесь есть серая зона. В случае, если требуется много тестов (ошеломляющее утверждение if с большим количеством && и ||), выбрасываемое исключение будет CheckedException просто потому, что слишком много боли, чтобы получить право - вы просто не можете сказать эту проблему это ошибка программирования. Если тестов намного меньше 10 (например, 'if (x == null)'), то ошибка программиста должна быть UncheckedException.
Вещи становятся интересными, когда имеешь дело с переводчиками языка. Согласно приведенным выше правилам, следует ли считать синтаксическую ошибку проверенным или непроверенным исключением? Я бы сказал, что если синтаксис языка можно протестировать до его запуска, это должно быть исключение UncheckedException. Если язык не может быть протестирован - подобно тому, как ассемблерный код выполняется на персональном компьютере, то синтаксическая ошибка должна быть проверенным исключением.
2 приведенных выше правила, вероятно, уберут 90% вашей заботы, из которой можно выбирать. Чтобы обобщить правила, следуйте этому шаблону… 1) если выполняемый код может быть проверен перед его выполнением на предмет его корректной работы, и если возникает Исключение - то есть ошибка программиста, Исключением должно быть UncheckedException (подкласс RuntimeException).). 2) если код, который должен быть выполнен, не может быть протестирован, прежде чем он будет выполнен для правильной работы, исключение должно быть проверенным исключением (подкласс исключения).
Вы можете назвать это проверенным или непроверенным исключением; однако программист может перехватить оба типа исключений, поэтому лучший ответ таков: запишите все свои исключения как непроверенные и задокументируйте их. Таким образом, разработчик, который использует ваш API, может выбрать, хочет ли он или она перехватить это исключение и что-то сделать. Проверенные исключения являются пустой тратой времени каждого, и это делает ваш код шокирующим кошмаром. Надлежащее модульное тестирование затем вызовет любые исключения, с которыми вам, возможно, придется столкнуться и что-то сделать.
Проверенное исключение: если клиент может восстановиться после исключения и хочет продолжить, используйте проверенное исключение.
Непроверенное исключение: если клиент не может ничего сделать после исключения, тогда вызовите непроверенное исключение.
Пример: если вы должны выполнять арифметическую операцию в методе A() и на основе выходных данных из A(), вам нужно выполнить другую операцию. Если выходные данные из метода A() являются нулевыми, чего вы не ожидаете во время выполнения, то ожидается, что вы получите исключение пустого указателя, которое является исключением времени выполнения.
Обратитесь сюда
Вот я хочу поделиться своим мнением, которое у меня есть после многолетнего опыта разработки:
Проверено исключение. Это часть бизнес-прецедента или потока вызовов, это часть логики приложения, которую мы ожидаем или не ожидаем. Например, соединение отклонено, условие не выполнено и т. Д. Мы должны обработать его и показать пользователю соответствующее сообщение с инструкциями, что произошло и что делать дальше (повторите попытку позже и т. Д.). Я обычно называю это исключением пост-обработки или "пользовательским" исключением.
Непроверенное исключение. Это является частью исключения из программирования, некоторой ошибкой в программировании программного кода (ошибка, дефект) и отражает способ, которым программисты должны использовать API согласно документации. Если внешний документ lib/framework сообщает, что ожидает получить данные в некотором диапазоне и не в нулевом, потому что будет выдано исключение NPE или IllegalArgumentException, программист должен ожидать его и правильно использовать API в соответствии с документацией. В противном случае будет сгенерировано исключение. Я обычно называю это исключением предварительной обработки или исключением "проверки".
По целевой аудитории. Теперь поговорим о целевой аудитории или группе людей, для которых были разработаны исключения (по моему мнению):
- Проверено исключение. Целевая аудитория - пользователи / клиенты.
- Непроверенное исключение. Целевая аудитория - разработчики. Другими словами, непроверенное исключение предназначено только для разработчиков.
По этапу жизненного цикла разработки приложения.
- Проверенное исключение предназначено для существования в течение всего жизненного цикла производства как обычный и ожидаемый механизм, который приложение обрабатывает в исключительных случаях.
- Непроверенное исключение предназначено для существования только в течение жизненного цикла разработки / тестирования приложений, все они должны быть исправлены в течение этого времени и не должны создаваться, когда приложение уже запущено в производство.
Причина, по которой фреймворки обычно используют неконтролируемые исключения (например, Spring), заключается в том, что фреймворк не может определить бизнес-логику вашего приложения, поэтому разработчики должны поймать и спроектировать собственную логику.
Мы должны различать эти два типа исключений в зависимости от того, является ли это ошибкой программиста или нет.
- Если ошибка является ошибкой программиста, это должно быть непроверенное исключение. Например: SQLException/IOException/NullPointerException. Эти исключения являются ошибками программирования. Они должны быть обработаны программистом. В то время как в JDBC API SQLException является проверенным исключением, в Spring JDBCTemplate это непроверенное исключение. Программист не беспокоится о SqlException при использовании Spring.
- Если ошибка не является ошибкой программиста, а ее причина исходит от внешней, это должно быть проверенное исключение. Например: если файл удален или разрешение на изменение файла кем-то другим, его следует восстановить.
FileNotFoundException является хорошим примером для понимания тонких различий. FileNotFoundException генерируется, если файл не найден. Есть две причины для этого исключения. Если путь к файлу определен разработчиком или получен от конечного пользователя через графический интерфейс, это должно быть непроверенное исключение. Если файл удален кем-то другим, это должно быть проверенное исключение.
Проверенное исключение может быть обработано двумя способами. Они используют try-catch или распространяют исключение. В случае распространения исключения все методы в стеке вызовов будут тесно связаны из-за обработки исключения. Поэтому мы должны тщательно использовать Checked Exception.
В случае, если вы разрабатываете многоуровневую корпоративную систему, вы должны выбрать в основном исключение без проверки, но не забудьте использовать проверенное исключение для случая, когда вы ничего не можете сделать.
Я согласен с предпочтением для непроверенных исключений, как правило, особенно при разработке API. Вызывающий всегда может выбрать перехват зарегистрированного, непроверенного исключения. Ты просто не заставляешь звонящего.
Я считаю проверенные исключения полезными на нижнем уровне, как детали реализации. Часто кажется, что механизм управления потоком данных лучше, чем управлять заданным ошибочным "кодом возврата". Иногда это может помочь увидеть влияние идеи на низкоуровневое изменение кода тоже... объявить проверенное исключение в нисходящем направлении и посмотреть, кого нужно будет отрегулировать. Этот последний пункт не применяется, если есть много общего: catch (Exception e) или throws Exception, которое обычно не слишком хорошо продумано в любом случае.
Проверенные исключения полезны для восстанавливаемых случаев, когда вы хотите предоставить информацию вызывающей стороне (т. Е. Недостаточно прав, файл не найден и т. Д.).
Непроверенные исключения используются редко, если вообще используются, для информирования пользователя или программиста о серьезных ошибках или неожиданных условиях во время выполнения. Не бросайте их, если вы пишете код или библиотеки, которые будут использоваться другими, поскольку они могут не ожидать, что ваше программное обеспечение сгенерирует непроверенные исключения, так как компилятор не заставляет их быть захваченными или объявленными.
Я думаю, что мы можем думать об исключениях из нескольких вопросов:
почему происходит исключение? Что мы можем сделать, когда это произойдет
по ошибке ошибка. такой как метод нулевого объекта называется.
String name = null;
... // some logics
System.out.print(name.length()); // name is still null here
Такое исключение должно быть исправлено во время теста. В противном случае, это нарушает производство, и вы получаете очень большую ошибку, которую нужно немедленно исправить. Этот вид исключений не нужно проверять.
с помощью внешнего ввода вы не можете контролировать или доверять выходу внешнего сервиса.
String name = ExternalService.getName(); // return null
System.out.print(name.length()); // name is null here
Здесь вам может понадобиться проверить, является ли имя пустым, если вы хотите продолжить, когда оно пустое, в противном случае вы можете оставить его в покое, и оно остановится здесь и даст вызывающей стороне исключение времени выполнения. Этот вид исключений не нужно проверять.
по исключению времени выполнения из внешнего, вы не можете контролировать или доверять внешней службе.
Здесь вам может понадобиться перехватить все исключения из ExternalService, если вы хотите продолжить, когда это произойдет, в противном случае вы можете оставить его в покое, и он остановится здесь и даст вызывающей стороне исключение времени выполнения.
по проверенному исключению из внешнего вы не можете контролировать или доверять внешнему сервису.
Здесь вам может понадобиться перехватить все исключения из ExternalService, если вы хотите продолжить, когда это произойдет, в противном случае вы можете оставить его в покое, и он остановится здесь и даст вызывающей стороне исключение времени выполнения.
В этом случае, нам нужно знать, что за исключение произошло в ExternalService? Это зависит:
если вы можете обрабатывать некоторые виды исключений, вам нужно их перехватить и обработать. Для других, пузырь их.
если вам нужен лог или ответ пользователю конкретного исполнения, вы можете их поймать. Для других, пузырь их.
В любом случае исключение ожидается менее вероятным, и мы можем продолжить работу даже после его обнаружения, и мы не можем ничего сделать, чтобы избежать этого исключения, тогда мы можем использовать проверенное исключение.
Всякий раз, когда мы хотим сделать что-то значимое, когда происходит определенное исключение и когда это исключение ожидается, но не обязательно, мы можем использовать проверенное исключение.
Всякий раз, когда исключение перемещается в разных слоях, нам не нужно перехватывать его в каждом слое, в этом случае мы можем использовать исключение времени выполнения или обернуть исключение как исключение без проверки.
Исключение во время выполнения используется, когда исключение, скорее всего, произошло, нет пути идти дальше и ничто не может быть восстановлено. Таким образом, в этом случае мы можем принять меры предосторожности в отношении этого исключения. Пример: NUllPointerException, ArrayOutofBoundsException. Это с большей вероятностью произойдет. В этом сценарии мы можем принять меры предосторожности при кодировании, чтобы избежать такого исключения. В противном случае мы должны будем написать try catch и везде блоки.
Более общие исключения могут быть сделаны Не проверены, менее общие проверяются.
Я думаю, что при объявлении Application Exception это должно быть Unchecked Exception, то есть подкласс RuntimeException. Причина в том, что он не загромождает код приложения try-catch и создает объявление метода. Если ваше приложение использует Java Api, который выдает проверенные исключения, которые в любом случае необходимо обрабатывать. В других случаях приложение может выдать непроверенное исключение. Если вызывающему приложению все еще нужно обработать непроверенное исключение, это можно сделать.
Правило, которое я использую: никогда не используйте непроверенные исключения! (или когда ты не видишь никакого пути вокруг этого)
С точки зрения разработчика, использующего вашу библиотеку, или конечного пользователя, использующего вашу библиотеку / приложение, это действительно отстой, когда вы сталкиваетесь с приложением, которое вылетает из-за необработанного исключения. И рассчитывать на ловушку все тоже не годится.
Таким образом, конечный пользователь все еще может получить сообщение об ошибке, а приложение полностью исчезнет.