JavaRush /Java блог /Random UA /Типові помилки в обробці винятків
Ve4niY
14 рівень

Типові помилки в обробці винятків

Стаття з групи Random UA
Виняток – це порушення нормального ходу виконання Java (або будь-якої іншої) програми. Це порушення може статися через порушення доступу до пам'яті, поділ на нуль, проблеми ініціалізації, виконання забороненої інструкції або будь-якої іншої фатальної помилки. Java здатний обробляти всі такі небажані сценарії елегантно, але коли справа доходить до використання цих можливостей Java розробниками, може виникнути кілька проблем. У цій статті я не збираюся обговорювати, як працює обробка винятків ( Exception Handling ) у Java. Я припускаю, що читач вже знайомий з ієрархією винятків, виключеннями ( checked exceptions ), неперевіреними винятками ( unchecked exceptions ), винятковими робочими ситуаціями при виконанні програми ( runtime exceptions ). Тут ми обговоримо найпоширеніші помилки, яких слід уникати.
Зацікавлені сторони у винятках
Щоразу, коли потік виконання переривається, інформація про це має повернутися до виконавця програми. Цей потік може стартувати з екрана, з пакетної задачі чи будь-яким іншим способом. Всі ці тригери очікують будь-яких порушень у потоці передачі повідомлення певного формату. Тригер на основі екрана може м'яко повідомити нам, що є деякі технічні проблеми і слід звернутися за допомогою та консультацією до служби підтримки. Пакетні процеси або автономні Java-програми можуть очікувати, що звіт про помилку постане у вигляді будь-якого журналу, абсолютно незрозумілого кінцевому користувачеві. Але ділові люди повинні отримувати повідомлення зрозумілою їм мовою. Страшний NullPointerException у головах користувачів сильно дратує. Іноді ці помилки – наслідок неправильного використання програми, але іноді – проблеми, які потребують виправлення. Другою важливою зацікавленою стороною є людина, якій доведеться вирішувати проблеми, що виникли з появою винятків. Ця людина не може спостерігати фактичне виконання програми під час виключення. Він спиратиметься на дві речі - по-перше, на інформацію, надану користувачем, у якого виникла проблема, а по-друге - на свого роду журнал, що генерується з появою винятку. До того ж, людина, яка аналізуватиме проблему - не та сама людина, яка написала програму, і, отже, розробник програми повинен надати достатньо інформації для аналізу. Єдиний спосіб спілкування між розробником програми та її відладчиком – журнал. Крім того, іноді з міркувань безпеки, лог журналу іноді не можуть містити всіх фактичних деталей виконання програми, коли з'явився виняток. Розгляньмо можливі помилки в обробці винятків. Деякі з них виглядають досить безглуздо, але є люди. які не звертають на них увагу, доки ці помилки не призводять до проблем.
Порожній блок виключення
Це одна із найбільш небажаних помилок у обробці виняткових ситуацій. І тут виняток просто ігнорується. Рішення не містить нічого, крім лінії коментаря залишити виняток. try{ }catch(SQLException sqe){ // do nothing } У деяких окремих випадках, наприклад, у фінальному блоці, при відключенні бази даних, допустима поява винятків. У цьому випадку вони можуть бути проігноровані. try{ }catch(SQLException sqe){ ... ... }finally{ try{ conn.close(); }catch(Exception e){ //leave it. } }
Неправильне узагальнення повідомлення про виключення
Цей момент має суб'єктивний характер. Після перехоплення виключення, ви можете повідомити про це кінцевого користувача у вигляді дружнього послання. При цьому цілком можливо, що деталі вихідного повідомлення будуть втрачені при перетворенні вихідних повідомлень на одне загальне. Повідомлення має довести належну інформацію до кінцевого споживача так, щоб, зв'язавшись із командою підтримки, для надання інформації користувачеві достатньо було увійти до відповідного журналу. try{ File file = new File(""); file.getCanonicalPath(); }catch(IOException ioe){ throw new Exception("File Processing Failed",ioe); } У багаторівневому додатку кожен шар ловить винятки та створює новий тип виключення. Іноді необхідно при цьому перетворити виняток певного типу: - Data Access Layer -> створює DataAccessException - Business Implementation Layer -> створює BusinessException - Application Service Layer -> створює ApplicationServiceException Все виглядає логічно, чи не так? Тут нам, можливо, доведеться вибирати між ApplicationServiceException і BusinessException, оскільки обидва можуть представляти ту саму інформацію. Одне із цих перетворень винятків виглядає зайвим.
Знищення класу StackTrace
У процесі трансформації виключення в новий спеціальний тип може зникнути трасування стека, і тоді відстеження фактичної причини виключення стає кошмаром наяву. У коді нижче ви можете побачити, як новий виняток створюється без отримання будь-якої інформації від вихідного виключення. try{ File file = new File(""); file.getCanonicalPath(); }catch(IOException ioe){ throw new MyException("Problem in data reading."); }
Перезапис причин виключення
Кожне повідомлення про виключення містить інформацію про причину виключення. Однак при створенні нового виключення інформація про старе може бути видалена безповоротно. Це схоже на наведений вище приклад, де StackTrace може бути видалено через появу нового виключення.
Відсутність пошуку певних винятків
Іноді розробники люблять діяти, напевно, в обробці винятків. Іноді це легко здійснити. Але ловити java.lang.Exception замість конкретного винятку не є прийнятним. try{ File file = new File(""); file.getCanonicalPath(); }catch(Exception exe){ throw new MyException("Problem in data reading."); }
Непотрібні catch та throw
Перехоплюйте винятки, якщо ви хочете перетворити їх на інший тип, або якщо це допоможе вам додати будь-яку інформацію в повідомлення про виключення для кінцевого користувача або аналітика. В іншому випадку просто викидайте їх далі в сигнатурі методу (за допомогою throws) замість їх перехоплення.
Лов RuntimeException
Я рекомендував би уникати перехоплення RuntimeException. Натомість краще ловити конкретні винятки і лікувати їх.
Пошук неперевірених винятків
Це спірна тема – ловити неперевірені винятки чи ні. NullPointerException і ArrayIndexOutOfBound можуть бути кращими прикладами неперевірених винятків. Замість того, щоб ловити ці винятки, ви можете змінити код обробки цих сценаріїв. Наприклад, щоб уникнути NullPointerException, забезпечити ініціалізацію всіх змінних, щоб уникнути винятків ArrayIndexOutOfBound, визначити масив правильної довжини і т.д.
Проблеми, які стосуються реєстрації винятків
Журнал допоможе другій зацікавленій, тобто людині, яка аналізує проблеми. Іноді розробник переборщує з файлуми логів, і журнали створюються кожному рівні програми. Журнал має реєструвати виняток на тому рівні, на якому він стався. Оптимальним видається попередження, що сталося виняток, з пропозицією зупинити чи продовжити виконання програми, і обов'язкового протоколювання виключення у журналі.
Виняток для керування потоком
Виняток є порушенням нормального перебігу роботи програми, а це означає, що потрібно перервати цей потік і проінформувати про це кінцевого користувача. Якщо ми додамо виклик методу, який є альтернативним потоком, це призведе до величезних проблем обслуговування. try{ myObject.getValue(); }catch(NullPointerException npe){ alternateMethod(); }
Повсюдне використання java.lang.Exception
Не найкращою ідеєю бачиться і повсюдне використання для будь-якого виключення java.lang.Exception. Натомість краще використовувати специфічні винятки програми, або найбільш підходящі стандартні Java винятки. Вихідна стаття: Common Mistakes in Exception Handling Перекладено та озвучено: Ve4niY
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ