Почему Java-разработчики часто молча потребляют исключения?


81

Я никогда раньше не занимался серьезным кодированием на Java, но я изучил синтаксис, библиотеки и концепции, основываясь на моих существующих навыках (Delphi и C #). Одна вещь, которую я с трудом понимаю, - это то, что я видел так много кода, который молча потребляет исключения после printStackTraceследующего:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

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

Исходя из моего опыта, я бы предпочел полностью удалить printStackTrace . Я бы просто перебросил как необработанный aka RuntimeException(или, что еще лучше, AssertionError), затем поймал бы и зарегистрировал его в наиболее подходящем месте: во внешнем цикле фреймворка.

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            throw new AssertionError(e);
        }
    }

56
Я бы сказал, что это не совсем тихо, когда печатается трассировка стека :)
willcodejavaforfood

13
Могу я процитировать Исаака Уоллера «Но ваша программа не завершается и продолжает работать, скорее всего, в неопределенном состоянии»
Саке

15
@willcodejavaforfood, Ваш комментарий получил так много голосов, что еще больше озадачил меня типичным java-мышлением.
Саке

13
@willcodejavaforfood - он молчит с точки зрения вызывающего абонента. Звонящий не знает, что что-то произошло. Если это то, что задумано, это нормально, но в целом это плохие новости. Также - подумайте о том, что происходит в апплете - пользователь никогда не увидит вывод (если только у него не будет открыта консоль java, чего никогда не бывает). Хорошо, так что никто больше не использует апплеты;) Однако аналогично сервлету - конечный пользователь не знает, что что-то произошло - это просто сохраняется в журнале.
Скотт Стэнчфилд

4
Выброс исключений в Java настолько распространен, что большинство Java-программистов игнорируют их, потому что, если вы правильно отловите ошибки, вам нужно будет написать намного больше кода ... так что в основном все эти Java-программисты делают это, потому что они ленивы.
Trevor Boyd Smith

Ответы:


203

Я всегда думал, что это похоже на следующий сценарий:

"В человека стреляют.

Он задерживает дыхание, и у него достаточно сил, чтобы сесть на автобус.

Через 10 миль мужчина выходит из автобуса, проходит пару кварталов и умирает ».

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

Лучше это:

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

Когда приезжает полиция, все улики на месте.

Если система выйдет из строя, лучше быстро выйти из строя

Решение вопроса:

  1. Невежество.
      +
  2. Лень

РЕДАКТИРОВАТЬ:

Конечно, полезен раздел улова.

Если что-то можно сделать с исключением, то это и нужно делать.

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

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


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

21
Подождите ... Сначала мне нужно перестать смеяться ... Хорошо ... Это был, безусловно, самый интересный ответ на вопрос, который я когда-либо видел. Это очень хорошо передает суть. Хотя может быть очень полезно передать исключение «вверх по цепочке» для обработки, лучше, по крайней мере, сначала перехватить исключение, а затем передать его, чтобы можно было включить некоторую полезную информацию.
Мистер Уилл

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

13
@Oscar, +100, если бы мог. Плохая обработка исключений - это результат НЕВЕДЕНИЯ и ЗЛОУПОТРЕБЛЕНИЯ, а не лени. Лень = как можно меньше усилий. Глупость в обработке исключений вызывает больше работы, а не уменьшает ее ... конечно, обычно это вызывает больше работы для кого-то еще, поэтому кажется "ленивым".
Джеймс Шек

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

33

Обычно это происходит из-за того, что IDE предлагает полезное «быстрое исправление», которое помещает проблемный код в блок try-catch с этой обработкой исключений. Идея в том, что вы действительно что-то делаете, а ленивые разработчики - нет.

Это, без сомнения, дурной тон.


2
Согласны, приведенный пример выглядит как быстрое исправление Eclipse по умолчанию, с удаленным комментарием // FIXME :.
JeeBee

2
Ну, вы сказали: «Это, без сомнения, дурной тон », и я на 100% согласен. Однако некоторые другие ответы и комментарии заставляют меня интересоваться философией, лежащей в основе этой очень большой (как мне кажется) части разработчиков java.
Саке

4
Я действительно хочу, чтобы Eclipse сделала что-нибудь радикальное. Например, e.printStackTrace (); System.exit (1);
Адам Яскевич

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

6
Если я уверен, что исключение никогда не должно происходить, я обычно бросаю AssertionError вместо RuntimeException как способ документировать свое намерение.
Эско Луонтола

20

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


1
Так что для профессионалов нормально писать плохой код в блоге или журнале, а не в продакшене? id лучше писать плохой код в продакшене. если действительно умный парень использует printStackTrace, он убеждает людей использовать его. я понимаю, что код исключения на 8 символов длиннее, но ...
IAdapter

8
@ABCDE: Я полностью не согласен. Вы предпочитаете писать плохой код в продакшене? Этот код должен работать! Статьи в блогах и журналах должны проиллюстрировать точку зрения. Если дело не в обработке ошибок, это может быть просто беспорядок. Многие авторы используют значительно упрощенную обработку ошибок или вообще не используют ее в статьях. Это не пример для подражания.
Bill the Lizard

19
  1. Java заставляет вас явно обрабатывать все исключения. Если метод, который вызывает ваш код, объявлен для генерации исключений FooException и BarException, ваш код ДОЛЖЕН обрабатывать (или генерировать) эти исключения. Единственное исключение - RuntimeException , которое молчит, как ниндзя.
  2. Многие программисты ленивы (в том числе и я), и очень легко просто распечатать трассировку стека.

7
Но ваша программа не завершается и продолжает работу, скорее всего, в неопределенном состоянии.
Исаак Уоллер,

5
Как состояние не определено, если вы знаете тип исключения? Это милая небольшая цитата, но обычно программа МОЖЕТ и ДОЛЖНА продолжать работу. Вы же не хотите, чтобы все приложение вылетало из-за исключения - сообщите об этом пользователю и попросите его сделать то же самое снова.
GreenieMeanie

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

2
По крайней мере, половина исключений - это «RuntimeExceptions», по вашему мнению, существует только одно. И это не тихо, и uncaught RutimeException напечатает трассировку стека и приведет к сбою вашей программы.
Bill K

@greenieMeanie Обычно при разработке вы хотите, чтобы он сильно вылетал из-за малейшей проблемы - не позволяйте разработчикам уйти с рук, испортив звонок - не прощайте НИЧЕГО. С другой стороны, когда вы его отправляете, вы хотите сделать обратное. Незаметно регистрируйте каждое исключение и старайтесь изо всех сил восстановить и продолжить (в чем Java действительно неплохо справляется).
Bill K

12

Я нахожу, что это часто делается по двум причинам

  1. Программист был ленив
  2. Программист хотел защитить точку входа в этот компонент (правильно или неправильно)

Я не верю, что это явление ограничивается Java. Я часто видел такое кодирование на C # и VB.Net.

На первый взгляд это довольно шокирующе и ужасно выглядит. Но на самом деле ничего нового. Это происходит постоянно в приложениях C ++, которые используют возвращаемые значения кода ошибки вместо исключений. Однако разница в том, что игнорирование потенциально фатального возвращаемого значения на самом деле ничем не отличается от вызова функции, возвращающей void.

Foo* pFoo = ...;
pFoo->SomeMethod(); // Void or swallowing errors, who knows?

Этот код выглядит лучше, но если бы SomeMethod () сказал вернуть HResult, это было бы семантически не иначе, как проглатывание исключения.


3
Я думаю, что Причина 1 гораздо более распространена, чем Причина 2, в частности, для этого вопроса
Мэтт Б.

2
@matt b, согласен. Лень является причиной значительной части ошибок, которые я исправляю в наши дни.
JaredPar

игнорирование кода ошибки происходит намного быстрее и требует гораздо меньше шаблонного кода!
gbjbaanb

11

потому что проверенные исключения - неудачный эксперимент

(может быть, printStackTrace () - настоящая проблема ? :)


17
Это не неудачный эксперимент. На самом деле они мне нравятся! Как указано в других ответах, программисты, как правило, ленивы и просто игнорируют случаи ошибок. Поскольку проверенные исключения вынуждают вас иметь дело с ними, у вас будет лучший код в целом. (Если Программист не игнорирует их молча). Кроме того, даже если вы не ленивы, вы не можете забыть обработать важное исключение, потому что компилятор скажет вам сделать это ...
Малакс

5
проверяются исключением является отказом по нескольким причинам: проверить некоторые из них mindview.net/Etc/Discussions/CheckedExceptions
DFA

2
+1 не могу не согласиться. Проверенные исключения часто делают код менее безопасным, и это один из примеров того, как это сделать.
cletus

2
Проверенные исключения определенно не идеальны, но они заставляют вас столкнуться с реальностью, что иногда все идет не по плану. Непроверенные исключения просто пытаются притвориться, что мы живем в идеальном мире, где все идет не так, как надо,
Mcjabberz

2
@Peter Lawrey: проверенные исключения - это провал, потому что очень умные люди, такие как те, кто пришел с удивительной структурой Swing, рвут их и отказываются их использовать. И я очень сомневаюсь, что такие люди, как Джошуа Блох или те, кто стоит за фреймворком Spring, «не знают, как их правильно использовать». Конечно, вы можете правильно обработать проверенное исключение. Настоящая поломка заключается в том, что кто-то где-то думал, что бросок был приемлемой практикой.
SyntaxT3rr0r

6

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

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

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

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

У меня действительно есть проблема с вводными статьями очень высокого уровня, в которых обсуждаются такие проблемы, но я не возвращаюсь к ним, но имейте в виду, что у Java-программистов нет определенного «мышления» относительно обработки ошибок; Я знаю многих ваших любимых программистов на C #, которые тоже не заботятся о решении всех своих проблем.


Но писать высокоуровневую статью с использованием try {...} catch (IOException e) {}просто глупо (и TODO НЕ помогает). Это наверняка заставит многих новичков сделать то же самое. Кроме того, это гораздо больше писать , чем throws IOException, что является правильным большую часть времени. Может быть, один раз на статью это невозможно, и тогда писать throw wrap(e)(без скучных определений wrap) - хорошее решение. Одна из первых вещей, на которые я опирался в Java, это то, что тихое глотание - ужасная вещь, и я очень осторожен, поэтому я не делаю этого даже временно (даже сбой в долгосрочной перспективе лучше).
maaartinus 02

5

Печать System.out или e.printStackTrace (), подразумевающая использование System.out, обычно является красным флажком, означающим, что кто-то не потрудился выполнить усердную работу. За исключением настольных Java-приложений, для большинства Java-приложений лучше использовать ведение журнала.

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

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


3

Он потребляется беззвучно, только если блок catch действительно пуст.

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

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


3

Как указывали другие, вы видите это по одной из трех причин:

  1. IDE сгенерировала блок try-catch
    • Код был скопирован и вставлен
    • Разработчик поместил трассировку стека для отладки, но так и не вернулся, чтобы правильно обработать исключение.

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

Лучшее описание того, что следует делать в блоке catch, можно найти в главе 9 « Эффективной Java » Джошуа Блоха .


2

В C # все исключения являются исключениями времени выполнения, но в Java у вас есть исключения времени выполнения и проверенные исключения, которые вы должны либо перехватывать, либо объявлять в своих методах. Если вы вызываете какой-либо метод, в конце которого есть «throws», вы должны либо перехватить упомянутые исключения, либо ваш метод также должен объявить эти исключения.

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


2

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

  • "Этого не случится!" Конечно, когда-нибудь вы «знаете», что этого Исключения не произойдет, но еще более целесообразно повторно вызвать исключение времени выполнения с возникшим Исключением в качестве «причины», а не просто игнорировать его. Бьюсь об заклад, это произойдет когда-нибудь в будущем. ;-)

  • Создание прототипа кода Если вы просто набираете свой материал, чтобы посмотреть, работает ли он, вы можете проигнорировать все исключения, которые могут возникнуть. Это единственный случай, когда я делаю ленивый улов (Throwable). Но если из кода получится что-то полезное, я включаю правильную обработку исключений.

  • "Я не знаю, что делать!" Я видел много кода, особенно библиотечного, который поглощает возникающие исключения, потому что на этом уровне приложения невозможно выполнить надлежащую обработку. НЕ ДЕЛАЙТЕ ЭТОГО! Просто повторно вызовите исключение (либо добавив предложение throws в сигнатуру метода, либо заключив исключение в конкретную библиотеку).


3
Увидев улов (метательный), я съежился.
Bill the Lizard

2
Совершенно верно. Я просто использую либо catch (Throwable), либо throws Throwable в коде, если я играю. "Настоящий" код не должен включать его! период.
Malax

Согласовано. Я съеживаюсь, потому что видел это раньше. Если другой разработчик увидит, что вы написали catch (Throwable), он зашел слишком далеко! :)
Bill the Lizard

1
Вы как бы хотите поймать (Throwable) на самом верхнем уровне, даже если код завершает процесс. Попытка ковырять в одном пропавшем потоке - не лучшая идея.
Том Хотин - tackline

2

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

Если вы найдете это в чем-то другом, кроме примера кода, не стесняйтесь пересылать код в TDWTF, хотя у них сейчас может быть слишком много примеров этого :)


2

Боюсь, что большинство java-программистов не знают, что делать с Исключениями, и всегда считают это раздражением, которое замедляет их кодирование в «номинальном» случае. Конечно, они совершенно неправы, но их трудно убедить в том, что ЭТО ВАЖНО правильно обрабатывать исключения. Каждый раз, когда я встречаю такого программиста (а это бывает часто), я даю ему две записи для чтения:

  • знаменитое мышление на java
  • Краткая и интересная статья Барри Рузека доступна здесь: www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html

Кстати, я полностью согласен с тем, что глупо перехватывать типизированное исключение, чтобы повторно выбросить его, встроенное в исключение RuntimeException:

  • если вы поймаете это, ОБРАТИТЕСЬ.
  • в противном случае измените подпись вашего метода, чтобы добавить возможные исключения, которые вы бы / не могли обработать, чтобы у вызывающего абонента была возможность сделать это самостоятельно.

Я на 100% не доверяю Брюсу Экелю в этом вопросе :)
CurtainDog

2

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

Причина: реализация интерфейса не может генерировать (проверенные) исключения, отличные от тех, которые определены в спецификации интерфейса. По этой причине создатели интерфейса, не зная, каким методам класса, реализующим интерфейс, может действительно потребоваться сгенерировать исключение, могут указать, что все методы могут вызывать по крайней мере один тип исключения. Пример: JDBC, где объявлено, что все и его «бабушка» выбрасывают SQLException.

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


1
IMHO, должно быть сокращенное обозначение языка для перехвата и повторного генерирования таких исключений, как ошибки времени выполнения. Обозначение такого сокращения прояснило бы как в коде, так и в поведении, что такие исключения следует рассматривать как представляющие «неожиданные» условия. Глотание - это плохо, поскольку, если возникнет исключение, что-то серьезно не так, и вызывающий код должен об этом узнать.
supercat

supercat: Мне нравится эта идея, но она может свести на нет (сомнительно ИМО) преимущество проверенных исключений.
Эрих Китцмюллер

Было бы лучше, если бы проверяемость была характеристикой сайтов перехвата / выброса и экземпляров исключений, а не классов исключений, чтобы проверенные исключения могли распространяться как непроверенные исключения того же класса, но даже перенос и повторное создание было бы значительным улучшением по сравнению с статус-кво, и его можно легко добавить в качестве новой функции. Это было бы особенно полезно в тех случаях, когда метод, который может генерировать проверенное исключение сам по себе, вызывает другие методы, которые объявлены как генерирующие то же исключение, но от которых не ожидается, что они когда-либо будут это делать . Если один из последних методов выбрасывает ...
supercat 09

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

1

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

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


1

Его ленивая практика - не что иное, как на самом деле.

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


1

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

Я бы сказал, что обертывание проверенного исключения в непроверенное (например, способ, которым Spring переносит проверенное исключение SQLException в экземпляр своей непроверенной иерархии) приемлемо.

Ведение журнала можно считать обработкой. Если бы пример был изменен на запись трассировки стека с использованием log4j вместо записи в консоль, сделало бы это приемлемым? Не так много изменений, ИМО.

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


1

Пожалуйста, никогда, никогда не заключайте проверенное исключение в непроверенное исключение.

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

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


Мне нравится перенос в AssertionError. AssertionError - это абстракция, которая применяется везде, ИМО.
Саке,

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

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

1
Не могу больше не согласиться с первичным утверждением этого ответа. У разработки есть свои достоинства.
Лоуренс Дол,

@CurtainDog: отмеченные исключения предназначены для «ожидаемых» проблем. Если метод x выдает (проверено) FooException в некоторых случаях, а метод y, вызывающий x, не ожидает возникновения таких случаев, то, если такие случаи действительно возникают, они представляют собой неожиданную проблему; даже если что-то еще выше по стеку вызовов ожидает a FooException, текущая ситуация не будет соответствовать тому, что вызывающий восходящий поток ожидает, когда FooExceptionбудет брошено a .
supercat

1

Потому что они еще не научились этому трюку:

class ExceptionUtils {
    public static RuntimeException cloak(Throwable t) {
        return ExceptionUtils.<RuntimeException>castAndRethrow(t);
    }

    @SuppressWarnings("unchecked")
    private static <X extends Throwable> X castAndRethrow(Throwable t) throws X {
        throw (X) t;
    }
}

class Main {
    public static void main(String[] args) { // Note no "throws" declaration
        try {
            // Do stuff that can throw IOException
        } catch (IOException ex) {
            // Pretend to throw RuntimeException, but really rethrowing the IOException
            throw ExceptionUtils.cloak(ex);
        }
    }
}

это хороший трюк, но лично я бы предпочел создать более конкретное исключение с такой причиной, как «выбросить новое исключение ServiceUnavailableException (ex)»
Грег

rethrow метод Apache ExceptionUtils делает именно это. Хороший трюк. github.com/apache/commons-lang/blob/master/src/main/java/org/…
Lluis Martinez

1

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

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

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


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

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

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

Как написать "правильный путь" чем?

  • Укажите каждый базовый класс исключений, которые могут быть выброшены в заголовке метода. AFAICR Eclipse может делать это автоматически.
  • Сделайте список бросков в прототипе метода значимым. Длинные списки бессмысленны, а "throw Exception" ленив (но полезен, когда вы не особо беспокоитесь об исключениях).
  • При написании "неправильного пути" простое "throw Exception" намного лучше и занимает меньше байтов, чем "try {...} catch (Exception e) {e.printStackTrace ();}".
  • При необходимости повторно скопировать связанное исключение.

0

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

Операторы try / catch, которые вы видели, есть только в коде, где программист явно решил обработать исключение на месте и, следовательно, не выдавать его дальше.


1
Неправда - если вы реализуете метод интерфейса или переопределяете метод суперкласса, у которого нет объявления throws, вы не можете просто добавить его. В таких случаях вам нужно либо заключить исключение в RuntimeException, либо проглотить его.
Мартин МакНалти

0

Я думаю, что разработчики также стараются учитывать важность «правильного поведения» в конкретном контексте. Часто, если выбросить код или распространить исключение вверх, ничего не даст, потому что исключение является фатальным, вы также можете сэкономить время и усилия, «сделав неправильные вещи».


0

Обычно вы проглатываете исключение, если не можете от него избавиться, но это не критично. Хорошим примером является IOExcetion, которое может быть выбрано при закрытии соединения с базой данных. Вы действительно хотите вылететь, если это произойдет? Вот почему в Jakarta DBUtils есть методы closeSilently.

Теперь для проверенных исключений, которые вы не можете восстановить, но которые являются критическими (обычно из-за ошибок программирования), не проглатывайте их. Я думаю, что исключения должны регистрироваться как можно ближе к источнику проблемы, поэтому я бы не рекомендовал удалять вызов printStackTrace (). Вы захотите превратить их в RuntimeException с единственной целью - не объявлять эти исключения в своем бизнес-методе. На самом деле нет смысла иметь высокоуровневые бизнес-методы, такие как createClientAccount (), генерирующие ProgrammingErrorException (читайте SQLException), вы ничего не можете сделать, если у вас есть опечатки в вашем sql или доступ к плохим индексам.


0

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

Я вообще-то увлекаюсь Fail-Fast / Fail-HARD, но хотя бы распечатайте это. Если вы на самом деле "съедите" исключение (то есть по-настоящему ничего не делать: {}), поиск его будет стоить кому-то ДНЕЙ.

Проблема в том, что Java заставляет вас ловить много вещей, которые разработчик знает, что они не будут выброшены, или не заботится о них. Самый распространенный - Thread.sleep (). Я понимаю, что здесь есть дыры, которые могут привести к проблемам с потоками, но обычно вы знаете, что не прерываете это. Период.


0

Может быть много причин, по которым можно использовать catch Exception. Во многих случаях это плохая идея, потому что вы также ловите RuntimeExceptions - и вы не знаете, в каком состоянии будут находиться базовые объекты после того, как это произойдет? Это всегда трудная вещь с неожиданными условиями: можете ли вы верить, что остальной код впоследствии не откажет.

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


0

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

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

BTW: если вы хотите повторно выбросить проверенное исключение, вы можете сделать это с помощью

try {
   // do something
} catch (Throwable e) {
   // do something with the exception
   Thread.currentThread().stop(e); // doesn't actually stop the current thread, but throws the exception/error/throwable
}

Примечание: если вы это сделаете, вы должны убедиться, что объявление throws для метода верное, поскольку компилятор не может сделать это за вас в этой ситуации.


Просто FTR: Thread#stop()устарел и пока что отключен (выкидывает UnsupportedOperationException).
maaartinus 02

-2

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


1
Тот факт, что большое количество разработчиков не занимается исключениями, не улучшает «возврат ошибок». Способ обработки ошибок C был и остается ужасно подверженным ошибкам механизмом. Проглатывание исключений (что и происходит в этом примере, но не втихаря) сродни вызову функции в C без проверки возвращаемого значения. Конечный результат тот же - программа «ошибается». Этот пример - всего лишь случай, когда обработка ошибок Java превращается в плохую C.
Лоуренс Дол
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.