Чому люди 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 повністю . Я б просто перекинув його як необробленого ака RuntimeException(або, що ще краще AssertionError), а потім ловив і реєстрував у найвідповіднішому місці: найвіддаленіша петля фреймворка.

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

56
Я б сказав, що це насправді не тихо, коли друкується стек :)
willcodejavaforfood

13
Можна цитувати Ісаака Уоллера "Але ваша програма не припиняє роботу і продовжує свою роботу, швидше за все, у невизначеному стані"
Саке,

15
@willcodejavaforfood, Ваш коментар набрав стільки голосів, і це ще більше бентежить мене щодо типового мислення Java.
Sake

13
@willcodejavaforfood - це тихо з точки зору абонента. Той, хто телефонує, навіть не підозрює, що щось сталося. Тепер, якщо це те, що передбачається, це добре, але загалом це погані новини. Також - подумайте про те, що відбувається в аплеті - користувач ніколи не побачить вихідних даних (якщо у них випадково не відкрита консоль Java, що ніколи). Гаразд, отже, ніхто більше не використовує аплети;) Подібно до servletm, хоча - кінцевий користувач не знає, що щось трапилося - він просто схований у журналі.
Скотт Стенчфілд,

4
Викиди в Java настільки поширені, що більшість кодерів Java ігнорують їх, тому що якщо ви правильно вловлюєте помилки, то вам потрібно буде написати набагато більше коду ... так що в основному всі ці програмісти Java роблять це, тому що вони ліниві.
Тревор Бойд Сміт,

Відповіді:


203

Я завжди думав, що це схоже на наступний сценарій:

"Чоловіка розстрілюють.

Він затамував подих і має достатньо сил, щоб сісти на автобус.

Через 10 миль чоловік виходить з автобуса, проходить пару кварталів і вмирає ".

Коли поліція дістається до тіла, вона не здогадується про те, що щойно сталося. Зрештою вони можуть, але це набагато складніше.

Краще це:

"Чоловіка розстрілюють і він миттєво помирає, а тіло лежить саме там, де щойно сталося вбивство".

Коли приїжджає поліція, усі докази є на місці.

Якщо система має збій, краще - це швидко

Вирішення питання:

  1. Невігластво.
      +
  2. Лінивець

РЕДАГУВАТИ:

Звичайно, розділ лову корисний.

Якщо щось можна зробити за винятком, саме тут це слід робити.

Можливо, це НЕ виняток для даного коду, можливо, це те, що очікується (і, за моєю аналогією, це як куленепробивна куртка, і чоловік чекав пострілу в першу чергу).

І так, улов може бути використаний для кидання винятків, відповідних абстракції


2
Ковтання винятків не підводить, неважливо, швидко. З іншого боку, якщо у вашому контракті методу є параметр, який є рядком цифр, і ви аналізуєте його на ціле число, ви можете проковтнути ParseException. Якщо ви пишете певну функціональність, визначте спеціальний виняток для цієї функції, який не вдається, а потім ловіть винятки та викидайте власні винятки (з оригінальним винятком як аргумент). Потім він може швидко вийти з ладу десь, де може відбутися значуща поведінка (відображення користувачеві, надсилання електронної пошти, просто реєстрація, вихід із цього потоку сервера тощо).
JeeBee

21
Зачекайте хвилинку ... Спочатку мені потрібно перестати сміятися ... Гаразд ... Це, безумовно, була найцікавіша відповідь на запитання, яке я бачив на сьогодні. Це дуже усвідомлює суть. Хоча може бути дуже корисно передавати виняток "по ланцюжку" для обробки, краще спочатку принаймні зловити виняток, а потім передати його, щоб можна було включити деяку корисну інформацію.
Містер Вілл

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

13
@Oscar, +100, якби міг. Погана обробка винятків - це результат НЕВІДОМЛЕННЯ і ЛИХИ, а не лінощів. Лінь = якомога менше зусиль. Дурість у роботі з винятками спричиняє більше роботи, а не зменшує її ... звичайно, вона, як правило, спричиняє більше роботи для когось іншого, саме тому вона здається «лінивою».
James Schek

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

33

Зазвичай це пов’язано з тим, що IDE пропонує корисне «швидке виправлення», яке обертає код, що порушує, у блок спробного лову з такою обробкою винятків. Ідея полягає в тому, що ви насправді щось робите, а ледачі розробники - ні.

Це погана форма, без сумніву.


2
Погоджено, наведений приклад виглядає як швидке виправлення за замовчуванням Eclipse, з вилученим // FIXME: коментарем.
JeeBee

2
Ну, ви сказали: "Це погана форма, без сумніву ", і я на 100% погоджуюсь. Однак деякі інші відповіді та коментарі роблять мене дуже цікавим у філософії цієї дуже великої частини (я вважаю) розробників Java.
Sake

4
Я дуже хочу, щоб Eclipse зробив там щось кардинальне. Мовляв, e.printStackTrace (); System.exit (1);
Адам Яскевич

4
Я змінив свої налаштування IDE, щоб сформувати тіло catch, яке відновлює його як RuntimeException. Це набагато краще за замовчуванням.
Еско Луонтола

6
Якщо я впевнений, що виняток ніколи не повинен статися, я зазвичай викидаю AssertionError замість RuntimeException як спосіб задокументувати свій намір.
Еско Луонтола

20

Це класичний аргумент солом’яної людини . printStackTrace()є допоміжним засобом для налагодження. Якби ви бачили його в блозі або в журналі це було тому , що письменник був більш зацікавлений в ілюструванні точки інший , ніж обробка винятків. Якщо ви бачили це у виробничому коді, розробник цього коду був невігласом або лінивим, не більше того. Це не повинно сприйматися як приклад загальноприйнятої практики у "світі Java".


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

8
@ABCDE: Я абсолютно не згоден. Ви вважаєте за краще написати поганий код у виробництві? Це код, який повинен працювати! Статті в блогах та журналах повинні ілюструвати певний момент. Якщо справа не в обробці помилок, то це може бути просто безлад. Багато авторів використовують значно спрощену обробку помилок або взагалі відсутні у статтях. Це не приклад для наслідування.
Білл Ящірка

19
  1. Java змушує вас обробляти всі винятки явно. Якщо метод, який викликає ваш код, оголошено для викиду FooException та BarException, ваш код ПОВИНЕН обробляти (або кидати) ці винятки. Єдиним винятком з цього є RuntimeException , який мовчить, як ніндзя.
  2. Багато програмістів ледачі (в тому числі і я), і дуже легко просто надрукувати трасування стека.

7
Але ваша програма не припиняє роботу і продовжує працювати, швидше за все, у невизначеному стані.
Ісаак Уоллер,

5
Як не визначено стан, якщо ви знаєте тип винятку? Це мила маленька цитата, але, як правило, програма МОЖЕ І МОЖЕ продовжувати продовжувати. Ви не хочете, щоб уся програма розбилася на виняток - скажіть користувачеві та попросіть його зробити все, що було знову.
GreenieMeanie

2
Додаток не вийде з ладу, якщо виняток оброблено належним чином на останньому рівні.
Саке

2
Принаймні половина винятків - це "RuntimeExceptions". Ви робите це так, ніби існує лише один. І це не безшумно, і незахоплений RutimeException надрукує трасування стека і виведе вашу програму з ладу.
Білл К

@greenieMeanie Як правило, під час розробки ви хочете, щоб він сильно розбився з найменшою проблемою - не дозволяйте розробникам уникнути, зіпсувавши дзвінок - не прощайте ні про що. З іншого боку, коли ви відправляєте його, ви хочете піти на протилежне. Записуйте кожен виняток невидимо і намагайтеся з усіх сил відновити та продовжити (що в Java насправді цілком вдале).
Білл К

12

Я вважаю, що часто це робиться з двох причин

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

Я не вірю, що це явище, обмежене Java. Я часто бачив таке кодування в C # та VB.Net.

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

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

Цей код виглядає краще, але якби SomeMethod () мав сказати повернення результату HResult, він семантично не відрізнявся б від проковтування винятку.


3
Я думаю, що причина 1 набагато більш поширена, ніж причина 2, зокрема для цього питання
matt b

2
@matt b, погодився. Лінощі відповідають за значну частину помилок, які я виправляю в наші дні.
JaredPar

ігнорування коду помилки на біс швидше і використовує набагато менше типового коду!
gbjbaanb

11

оскільки перевірені винятки - це невдалий експеримент

(можливо printStackTrace () - справжня проблема ? :)


17
Вони не є невдалим експериментом. Власне, вони мені подобаються! Як вказували інші відповіді, програмісти, як правило, лінуються і просто ігнорують випадки помилок. Оскільки перевірені винятки змушують вас мати справу з ними, у вас буде кращий код загалом. (Якщо Програміст не ігнорує їх мовчки). Крім того, навіть ви не ліниві, ви не можете забути вказати важливий виняток, тому що компілятор скаже вам це зробити ...
Малакс,

5
перевірених винятків є помилкою з кількох причин: перевірте деякі з них mindview.net/Etc/Discussions/CheckedExceptions
dfa

2
+1 не міг більше погодитись. Перевірені винятки часто роблять код менш безпечним, і це один із прикладів того, як.
cletus

2
Перевірені винятки, безумовно, не ідеальні, але вони змушують вас зіткнутися з реальністю, що іноді все йде не за планом.
Неперевірені

2
@Peter Lawrey: перевірені винятки - це невдача, тому що дуже розумні люди, такі як ті, хто прийшов із дивовижною структурою Swing, б'ються на них і відмовляються їх використовувати. І я дуже сумніваюся, що такі люди, як Джошуа Блох або ті, хто стоїть за рамками Spring, "не знають, як їх правильно використовувати". Звичайно, ви можете правильно впоратися з перевіреним винятком. Справжня розбитість полягає в тому, що хтось десь вважав, що кидати його - прийнятна практика.
SyntaxT3rr0r

6

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

Крім того, як вже зазначалося в інших місцях, є зрозумілі розчарування примусовим декларуванням Java перевірених винятків, хоча особисто у мене з цим проблем немає.

З чим, на мою думку, у мене проблема, ви переглядаєте купу статей та фрагментів коду в Інтернеті, не турбуючись про те, щоб розглянути контекст. Правда полягає в тому, що коли ви пишете технічну статтю, намагаючись пояснити, як працює якийсь конкретний API, або як почати з чогось, тоді ви, швидше за все, пропустите деякі аспекти коду - обробка помилок, яка не безпосередньо пов'язаний з тим, що ви демонструєте, є ймовірним кандидатом на утилізацію, особливо якщо виняток навряд чи відбудеться у прикладі сценарію.

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

У мене є проблеми з вступними статтями дуже високого рівня, які не зважають на такі проблеми, ніколи не повертаючись до них, але майте на увазі, що якихось особливих "мислення" програмістів щодо обробки помилок немає; Я знаю багатьох ваших улюблених програмістів на C #, які теж не турбуються, вирішуючи всі свої проблеми.


Але писати статтю на високому рівні, використовуючи try {...} catch (IOException e) {}просто дурне (і ТОДО НЕ допомагає). Це напевно введе в оману багатьох початківців робити те саме. Більше того, це набагато більше, ніж писатиthrows IOException , що є правильним у більшості випадків. Можливо, один раз на папір це неможливо, і тоді написання throw wrap(e)(без нудного визначення wrap) є хорошим рішенням. Однією з перших речей, на які я нахилився в Java, є те, що мовчки ковтати - це жахлива річ, і я пильно дбаю, щоб не робити цього навіть тимчасово (навіть збій - це набагато краще в довгостроковій перспективі).
maaartinus

5

Друк System.out або e.printStackTrace () - що передбачає використання System.out - це, як правило, червоний прапорець, що означає, що хтось не потрудився зробити старанну роботу. За винятком програм Java для настільних комп'ютерів, більшість програм Java краще використовувати реєстрацію.

Якщо режим відмови для методу не виконує жодних операцій, цілком чудово з’їсти виняток, незалежно від того, чи фіксуєте ви причину (та існування) чи ні. Однак, як правило, клаузула catch повинна вчинити якусь виняткову дію.

Повторне відновлення винятку - це те, що найкраще зробити, коли ви використовуєте улов для очищення частини роботи на рівні, де необхідна інформація все ще доступна, або коли вам потрібно перетворити виняток на тип винятку, який більше підходить для абонента.


3

Він споживається безшумно, лише якщо блок catch справді порожній.

Що стосується статей, вони, мабуть, цікавіші в доведенні деяких інших моментів, крім того, як поводитися з винятками. Вони просто хочуть перейти до суті та мати найкоротший можливий код.

Очевидно, що ви маєте рацію, винятки слід принаймні реєструвати, якщо вони будуть "проігноровані".


3

Як зазначали інші, причина, по якій ви бачите це, полягає в одній з трьох причин:

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

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

Найкращий опис того, що слід робити в блоці catch, можна знайти в розділі 9 « Ефективна Java » Джошуа Блоха .


2

У C # всі винятки є винятками часу виконання, але в Java у вас є винятки виконання та перевірені винятки, які вам доведеться або зловити, або оголосити у своїх методах. Якщо ви викликаєте будь-який метод, що має в кінці "кидки", вам доведеться або вловити згадані там винятки, або ваш метод також повинен оголосити ці винятки.

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


2

Ви повинні це дуже часто бачити, якщо програміст правильно виконує свою роботу. Ігнорування винятків - це погана, погана практика! Але є кілька причин, чому деякі можуть зробити це, і більш відповідні рішення:

  • "Цього не трапиться!" Звичайно, колись ти "знаєш", що цього винятку не станеться, але все ж більш доцільно відновити виняток часу виконання з наявним винятком як "причиною", а не просто ігнорувати його. Б'юся об заклад, це відбудеться колись у майбутньому. ;-)

  • Прототипування коду Якщо ви просто набираєте свої матеріали, щоб перевірити, чи це вдасться, ви можете проігнорувати всі винятки, які можуть виникнути. Це єдиний випадок, коли я роблю ледачий улов (Throwable). Але якщо код виявиться чимось корисним, я включаю належну обробку винятків.

  • "Я не знаю, що робити!" Я бачив багато коду, особливо бібліотечного коду, який ковтає наявні винятки, оскільки в цьому рівні програми неможливо виконати належну обробку. НЕ РОБІТЬ ЦЕ! Просто відновіть виняток (або додавши пропозицію throws до підпису методу, або обернувши виняток у бібліотечну).


3
Побачивши улову (Throwable), я злякався.
Білл Ящірка

2
Абсолютно. Я просто використовую або catch (Throwable), або кидаю Throwable в код, якщо я граю. "Справжній" код не повинен його включати! період.
Малакс,

Домовились. Я скучаюся, бо це вже бачив. Якщо інший розробник побачить, що ви написали catch (Throwable), це зайшло занадто далеко! :)
Білл Ящірка

1
Ви начебто хочете зловити (Throwable) на найвищому рівні, навіть якщо код вимикає процес. Спроба ковтатись разом із відсутнім одним потоком, може бути не найкращою ідеєю.
Том Хоутін - таклін

2

Ви завжди повинні пересилати його або обробляти це належним чином у реальному контексті. Багато статей та підручників спростять їх код, щоб зрозуміти важливіші моменти, і одне з найпростіших речей - це обробка помилок (якщо тільки ви не пишете статтю про обробку помилок, яка є :)). Оскільки java-код перевірятиме обробку винятків, тоді введення простого мовчазного (або оператора реєстрації) блоку catch - це найпростіший спосіб надати робочий приклад.

Якщо ви знайдете це в чому-небудь, крім прикладу коду, сміливо пересилайте код на TDWTF, хоча у них на даний момент може бути занадто багато прикладів :)


2

Я боюся, що більшість програмістів Java не знають, що робити з винятками, і майже завжди розглядають це як досаду, яка уповільнює їх кодування "номінального" випадку. Звичайно, вони абсолютно помиляються, але їх важко переконати, що ВАЖЛИВО правильно поводитися з винятками. Кожного разу, коли я стикаюся з таким програмістом (це трапляється часто), я даю йому два записи для читання:

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

До речі, я категорично погоджуюсь, що дурно ловити набраний виняток, щоб відновити його, вбудований у RuntimeException:

  • якщо ви зловите, ВРЯТАЙТЕ.
  • в іншому випадку змініть підпис методу, щоб додати можливі винятки, з якими ви б / не змогли впоратися, щоб ваш абонент мав шанс зробити це самостійно.

Я на 100% не довіряв би Брюсу Еккелю з цього питання :)
CurtainDog

2

Поєднання перевірених винятків та інтерфейсів призводить до ситуації, що код повинен обробляти вилучення, які ніколи не викидаються. (Те саме стосується і звичайного успадкування, але це частіше і простіше пояснити за допомогою інтерфейсів)

Причина: Реалізація інтерфейсу не може видавати (перевіряти) винятки, крім визначених у специфікації інтерфейсу. З цієї причини творці інтерфейсу, не знаючи, які методи класу, що реалізує інтерфейс, можуть насправді потребувати викиду, можуть визначити, що всі методи можуть створювати принаймні один виняток. Приклад: JDBC, де оголошено, що все та його бабуся кидають SQLException.

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


1
ІМХО, має бути скорочення мови, щоб ловити та відновлювати такі винятки, як помилки виконання. Позначення такого скороченого тексту дасть зрозуміти як у коді, так і в поведінці, що такі винятки слід розглядати як такі, що представляють "несподівані" умови. Ковтати - це погано, оскільки якщо справді викидається виняток, щось серйозно неправильно, і про це повинен дізнатися телефонний код.
supercat

supercat: Мені подобається ця ідея, але вона може скасувати (сумнівну перевагу IMO) перевірених винятків.
Еріх Кіцмюллер

Було б краще, якби перевіреність була характеристикою сайтів catch / throw та екземплярів винятків, а не класів винятків, так що перевірені винятки могли поширюватися як неперевірені винятки того самого класу, але навіть обертання та повернення було б серйозним покращенням на статус-кво, і його легко можна додати як нову функцію. Це було б особливо корисно у випадках, коли метод, який може викинути перевірений виняток на власні виклики інших методів, які оголошені як викидають той самий виняток, але не очікується, що коли-небудь насправді це робитимуть . Якщо один із останніх методів кидає ...
supercat

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

1

Як зазначалося, виклик printStackTrace () насправді не є тихим обробкою.

Причиною такого роду "проковтування" винятку є те, що якщо ви продовжуєте передавати виняток по ланцюжку, вам все одно доведеться десь обробляти виняток або дозволяти застосунку збій. Отже, обробка його на рівні, що відбувається за допомогою дампа інформації, не гірша, ніж обробка його на верхньому рівні за допомогою дампа інформації.


1

Його ледача практика - насправді це не щось інше.

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


1

Я не згоден з тим, що відновлення перевіреного винятку є кращою ідеєю. Ловити засоби обробки; якщо доводиться перекидати, не слід ловити. Я б додав речення throws до підпису методу в такому випадку.

Я б сказав, що обгортання перевіреного винятку у неперевірений (наприклад, спосіб, яким Spring обертає перевірений SQLException у примірник його неперевіреної ієрархії) є прийнятним.

Лісозаготівлю можна вважати обробкою. Якби приклад було змінено для реєстрації трасування стека за допомогою log4j замість запису на консоль, чи зробить це прийнятним? Не дуже багато змін, ІМО.

Справжнє питання полягає в тому, що вважається винятковим та прийнятною процедурою відновлення. Якщо ви не можете відновити виняток, найкраще, що ви можете зробити, - повідомити про помилку.


1

Будь ласка, ніколи, ніколи, ніколи не обертайте перевірений виняток у непровірений виняток.

Якщо ви стикаєтесь із винятками, які, на вашу думку, не повинні, тоді моя порада полягає в тому, що ви, мабуть, працюєте на неправильному рівні абстракції.

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


Упаковка в AssertionError для мене резонна. AssertionError - це абстракція, яка застосовується де завгодно, IMO.
Саке

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

Можливо, я серед людей, які, як ви сказали, впадають у повний безлад. Однак моя справжня думка полягає в тому, що чому дозволяти виконанню продовжувати, поки у вас є вибір перервати його. (з будь-якою AssertionError або, якщо хочете, змініть свій інтерфейс, щоб включити додатковий виняток.
Sake

1
Не міг не погодитися з основним твердженням цієї відповіді більше. Розробка має переваги.
Лоуренс Дол

@CurtainDog: Перевірені винятки стосуються "очікуваних" проблем. Якщо метод х кидків (перевірено) FooException в деяких випадках, і метод у, який викликає й, не очікує такі випадки виникають, то , якщо такі випадки дійсно виникають, вони представляють собою несподівану проблему; навіть якщо трапляється щось подальше в стеці викликів, яке очікує a FooException, нинішня ситуація не буде відповідати тому, що очікував би той, хто викликає, коли викине a FooException.
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);
        }
    }
}

це приємний трюк, але особисто я вважаю за краще створити більш конкретний виняток з причиною щось на кшталт 'кинути нову службуUnavailableException (колишня)'
Грег

метод rethrow Apache's ExceptionUtils робить саме це. Це хороший фокус. github.com/apache/commons-lang/blob/master/src/main/java/org/…
Луїс Мартінес

1

Справжній сенс винятків полягає у спрощенні обробки помилок та відділенні від виявлення помилок. Це суперечить поданню помилок за допомогою кодів помилок, де код обробки помилок розкиданий скрізь, і кожен виклик, який може провалитися, перевіряється на наявність коду повернення.

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

З усіх стратегій обробки помилок найбільш необізнаний приховує або просто друкує повідомлення про помилку і рухається вперед, оскільки нічого не сталося.


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

Але, як виявилося з багатьма невігласними програмістами Java, вони тепер ставляться до обробки винятків так, ніби це мовна помилка / "функція", яка потребує обхідного шляху і пише код найгіршим або майже найгіршим можливим способом:

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

Як написати "правильний шлях" ніж?

  • Вкажіть кожен базовий клас винятків, які можуть бути додані до заголовка методу. AFAICR Eclipse може зробити це автоматично.
  • Зробіть зміст списку мета в прототипі методу значущим. Довгі списки безглузді, а "кинути виняток" ліниво (але корисно, коли ви не дуже турбуєтесь про винятки).
  • Під час написання "неправильного шляху" простий "кинути виняток" набагато кращий і займає менше байтів, ніж "спробувати {...} catch (Виняток e) {e.printStackTrace ();}".
  • Повторне прив’язане виняток, якщо потрібно.

0

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

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


1
Неправда - якщо ви реалізуєте метод інтерфейсу або перевизначаєте метод суперкласу, який не має декларації "броски", тоді ви не можете просто додати його. У цих випадках вам або потрібно обернути виняток у RuntimeException, або проковтнути його.
Мартін МакНалті,

0

Я думаю, розробники також намагаються врахувати важливість "робити правильно" в певному контексті. Часто випадки для викидання коду або при розповсюдженні винятку вгору нічого не купують, оскільки виняток є фатальним, ви можете заощадити час і зусилля, "зробивши неправильну справу".


0

Зазвичай ви проковтуєте виняток, коли не можете від нього відновитись, але це не критично. Одним хорошим прикладом є IOExcetion, який можна використовувати під час закриття з'єднання з базою даних. Ви дійсно хочете розбитися, якщо це трапиться? Ось чому DBUtils в Джакарті мають методи closeSilently.

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


0

З досвіду ковтання винятку шкідливе здебільшого, коли воно не друкується. Це допомагає привернути увагу, якщо ви аварійно завершуєте роботу, і я іноді це роблю навмисно, але просто друк винятку та продовження дозволяє вам знайти проблему та виправити її, але, як правило, не впливає негативно на інших, що працюють на тій самій кодовій базі .

Я насправді люблю Fail-Fast / Fail-HARD, але принаймні роздрукуйте це. Якщо ви насправді "їсте" виняток (який по-справжньому нічого не робить: {}) Комусь знайдеться ДНІ, щоб знайти його.

Проблема в тому, що Java змушує вас ловити багато речей, про які розробник знає, що їх не кинуть, або їм байдуже, чи є вони. Найпоширенішим є Thread.sleep (). Я усвідомлюю, що тут є діри, які можуть спричинити проблеми з потоками, але загалом ви знаєте, що не перериваєте це. Період.


0

Причин, чому можна використовувати catch Exception, може бути багато. У багатьох випадках це погана ідея, тому що ви також ловите RuntimeExceptions - і ну ви не знаєте, в якому стані будуть лежати основні об'єкти після цього? Це завжди важка річ із несподіваними умовами: чи можете ви повірити, що решта коду згодом не зазнає збою.

У вашому прикладі друкується стек, щоб принаймні ви знали, що могла бути першопричиною. У більших програмних проектах краще зафіксувати ці речі. І дозволяє сподіватися, що компонент журналу не видає винятків, ані наш, ви можете опинитися в нескінченному циклі (що, ймовірно, вб’є вашу JVM).


0

Якщо у вас є перевірений виняток, і ви не хочете обробляти його в методі, вам просто потрібно, щоб метод кинув виняток. Ловіть і обробляйте винятки лише в тому випадку, якщо ви збираєтеся зробити з цим щось корисне. Просто ведення журналу не дуже корисно в моїй книзі, оскільки користувачі рідко встигають читати журнали, шукаючи винятки або знати, що робити, якщо виключення буде видано.

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

ДО: Якщо ви хочете повторно викинути перевірений виняток, ви можете зробити це за допомогою

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
}

Примітка: якщо ви зробите це, вам слід переконатися, що декларація "броски" для методу є правильною, оскільки компілятор не може зробити це за вас у цій ситуації.


Просто FTR: тим Thread#stop()часом застаріло та відключено (кидає UnsupportedOperationException).
maaartinus

-2

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


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