выдает исключение времени выполнения в приложении Java

12

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

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

Зачем когда-либо генерировать непроверенное исключение в бизнес-приложении?

Из моего прошлого опыта об исключениях во время выполнения:

1) Непроверенные исключения делают код непредсказуемым, потому что они не отображаются даже в Javadoc.

2) Бросать непроверенные исключения в бизнес-приложении не имеет смысла, потому что, когда вы бросаете его, и оно идет прямо в лицо пользователям, как вы объясните это пользователю? Я видел достаточно веб-приложений, которые показывают, 500 -Internal Error. Contact Administratorчто ничего не значит для конечного пользователя или для группы поддержки, управляющей приложением.

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

randominstanceOfLivingThing
источник
Можете ли вы объяснить, что такое уровень обслуживания? Является ли слой наиболее удаленным от пользователя или ближайшим к пользователю?
Маной Р
Почему этот вопрос не подходит для SO?
Бьярке Фрейнд-Хансен
@Manoj R: Ни то, ни другое. Сервисный уровень или уровень - это место, где бизнес-правила и логика рассматриваются, отделенные как от деталей технологии БД, так и от представления клиента.
Майкл Боргвардт
6
Непроверенные исключения [...] не отображаются даже в Javadoc. => они появятся, если вы задокументируете их с помощью @throwsтега, что, кстати, является хорошей практикой.
assylias
4
@SureshKoya Effective Java, Item 62: Документируйте все исключения, создаваемые каждым методом. Извлечение: используйте @throwsтег Javadoc для документирования каждого непроверенного исключения, которое может вызвать метод, но не используйте ключевое слово throws, чтобы включить непроверенные исключения в объявление метода.
assylias

Ответы:

13

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

Что касается ваших очков:

1) Проверенные исключения делают код грязным и не менее непредсказуемым, потому что они появляются везде .

2) Как проверенное исключение показывается пользователю лучше? Единственная реальная разница между проверенными и непроверенными исключениями является технической и затрагивает только исходный код.

3) Когда-нибудь слышали о следах стека? Они точно сообщают вам, где было сгенерировано исключение, независимо от того, установлено оно или нет. На самом деле, проверенные исключения, как правило, хуже для отладки, потому что они часто переносятся, что приводит к более длинным и уродливым трассировкам стека, или даже вообще теряются, потому что перенос был сделан неправильно.

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

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

  • Различие действительно зависит от вызывающего , а не от кода, который выдает исключение
  • Это полностью ортогонально семантическому значению исключения, но привязка его к иерархии классов заставляет вас смешивать два
  • Весь смысл исключений в том, что вы можете решить, на каком уровне их перехватывать, не рискуя молча потерять ошибку или не испортить код на промежуточных уровнях или; проверенные исключения теряют это второе преимущество.
Майкл Боргвардт
источник
1. Ну, в случае непроверенного исключения, вы даже не узнаете, может ли вызов привести к непроверенному. 2. Когда вы сказали «Это полностью ортогонально семантическому значению исключения, но привязка его к иерархии классов вынуждает вас смешивать два», я предполагаю, что вы имели в виду иерархию вызовов вместо иерархии классов.
randominstanceOfLivingThing
2
@Suresh Koya: Нет, я имел в виду иерархию классов, тот факт, что объявление SQLException extends Exceptionозначает, что выбор выбрасывать, SQLExceptionпотому что ошибка связана с доступом к БД, автоматически означает, что исключение проверено. И вы можете отлично документировать непроверенные исключения в комментариях Javadoc. Хорошо работает для всех других языков.
Майкл Боргвардт
1
@MichaelBorgwardt «Проверенные исключения являются неудачным объяснением в языковом дизайне», это ваше личное мнение, если бы я не хотел больше об этом читать. Любая подлинная ссылка будет отличным помощником.
Випин
2
@Vipin: Это мое личное мнение, но многие разделяют его, например, Брюс Экель: mindview.net/Etc/Discussions/CheckedExceptions - и тот факт, что ни один другой язык не принял проверенные исключения в течение 20 лет с момента появления Java, говорит о для себя ...
Майкл Боргвардт
2

Я считаю, что тип создаваемого исключения зависит от того, что делает ваш код.

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

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

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

Я предполагаю, что философия, которую вы упоминаете, исходит из одного из двух источников:

  • Ленивые программисты стараются не делать работу.
  • Или разработчики, которые должны были поддерживать код, который пошел другим путем. то есть чрезмерная обработка ошибок. Код, который содержит большое количество обработки исключений, большая часть которых ничего не делает, или даже хуже, используется для управления потоком и других неправильных целей.
drekka
источник
Если что-то случается довольно часто, это не исключение. Но согласился, что не следует выдавать ошибку, о которой пользователь понятия не имеет.
Маной Р
Я согласен с вашей философией. В приложении, которое вы разрабатываете для пользователей, что нужно для создания исключения Runtime?
randominstanceOfLivingThing
Также, даже если выдается исключение Runtime, я предпочитаю соглашение, на которое указывает @assylias.
randominstanceOfLivingThing
1

Если вы не хотите непроверенных исключений времени выполнения, то почему вы используете Java? Существует возможность исключений во время выполнения почти везде - в частности, исключения NullPointerException, ArithmeticException, ArrayIndexOutOfBounds и т. Д.

И когда вы однажды прочитаете файлы журналов какой-либо системы J2EE, например, установки SAP NetWeaver, вы увидите, что такие исключения случаются буквально все время.

Инго
источник
Из спецификации языка Java: «Классы исключений времени выполнения (RuntimeException и его подклассы) освобождаются от проверки во время компиляции, потому что, по мнению разработчиков языка программирования Java, необходимость объявления таких исключений не поможет в значительной степени установить правильность программ. Многие операции и конструкции языка программирования Java могут привести к исключениям во время выполнения. "
randominstanceOfLivingThing
1
Вы говорите об исключительной ситуации во время выполнения, которая возникает в Java, поскольку во время выполнения Java система не имеет понятия, почему вы пытаетесь выполнить конкретный вызов. Например: NullPointerException возникнет, если вы вызываете метод с нулевым значением. В этом случае система времени выполнения Java не имеет правильного слова для глупых программистов и будет бить вызывающего абонента с NullPointerException. Грустно то, что звонящий продал вам продукт Netweaver, и вы, как пользователь, стали жертвой плохого программирования. В равной степени виноваты тестеры, так как они не сделали все граничные тесты.
randominstanceOfLivingThing
1

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

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

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

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

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

Одним из способов обработки исключения является создание другого исключения, которое предоставляет описание проблемы более высокого уровня (например, « failed to initialize» вместо « index out of bounds»). Это хороший шаблон, если вы не потеряете информацию о причине исключения; используйте подробное исключение, чтобы инициализировать исключение causeболее высокого уровня или зарегистрируйте подробности (как обсуждалось выше). Ведение журнала наиболее уместно, когда вы собираетесь пересечь межпроцессную границу, такую ​​как вызов IPC, потому что нет никакой гарантии, что низкоуровневый класс исключений будет вообще присутствовать на другом конце соединения. Хранение в качестве прикрепленной причины наиболее целесообразно при пересечении внутренней границы.

Другой шаблон, который вы видите, это «поймай и отпусти»:

try {
    // ...
} catch (FooException e) {
    throw e;
}

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

Нет реальной разницы между проверенными исключениями и непроверенными, за исключением того факта, что вы должны объявлять проверенные исключения, которые пересекают границы метода. Хорошей идеей будет документировать неконтролируемые исключения (с @throwsкомментариями javadoc), если вы знаете, что они намеренно генерируются вашим кодом. Не преднамеренно выбрасывайте java.lang.Errorили его подклассы (если вы не пишете реализацию JVM).

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

Donal Fellows
источник
0

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

Как пример,

Если ваше приложение загружает данные из файла или базы данных. Потом,..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

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

try {
  Connection con = DriverManager.getConnection( host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}
Hendrix
источник
1
Сол погиб в автокатастрофе 3 года назад. Потерпеть неудачу! ;-)
Donal Fellows