Клас Exception Створення власних класів винятків.
Клас Exception є базовим для всіх класів користувача в програмі. Для того, щоб створити клас власного виключення, достатньо визначити його як похідний від класу Exception .
Тобто, при оголошенні класу MyExceptionClass на кшталт
class MyExceptionClass extends Exception
екземпляр цього класу зможе використовувати всі методи класу Throwable.
public class TrainException < public static void main(String[] args) < MyExceptionClass mc = new MyExceptionClass(); String str = mc.getMessage();out.println("str color: #0000ff;">main()
str = null
Отже, у власних реалізованих підкласах, які успадковані від класу Exception, не обов'язково реалізовувати власні методи.
2. Приклад створення класу Triangle. Обчислення площі трикутника з використанням власного класу виключення TriangleException
У прикладі демонструється створення власного класу TriangleException для обробки виключення "неправильно задані сторони трикутника".
Оголошується два класи:
- Triangle – реалізує трикутник з його боків a , b , c ;
- TriangleException – реалізує перехоплення виняткової ситуації «неправильно задані сторони трикутника».
Текст програми наступний:
import java.util.Scanner; // Створення власного класу винятку TriangleException // Створення власного класу Triangle, що реалізує трикутник з його боків
class
TriangleException extends Exception < // Перевизначити метод toString(), що описує виняток
public
String toString() < return
"Error. Bad sides of triangle."; > >
// оголосити клас трикутник
class
Triangle < // Сторони трикутника
private double
a,b,c; // конструктор за замовчуванням
public Triangle() < // рівносторонній трикутник із довжиною сторони 1
a = b = c = 1; >
// Параметризований конструктор
public Triangle(double
_a, double _b, double _c) < // Використання класу виключення TriangleException
try
< // чи можна зі сторін _a, _b, _c створити трикутник
if (((_a+_b)<_c)||((_a+_c)<_b)||((_b+_c)<_a)) throw new TriangleException(); > catch(TriangleException e) < System.out.println("Exception: "+e.toString()); return; >
// якщо сторони трикутника введені коректно, // записати їх у внутрішні змінні класу
a = _a; b = _b; c = _c; > // метод повертає площу трикутника
public double getArea() < // якщо сторони трикутника мають коректні розміри,
// Перевірку на корінь з негативного числа робити не потрібно
double
p, s; p = (a+b+c)/2; // напівпериметр s = Math.sqrt(p * (p-a)*(p-b)*(p-c)); // Формула Герона
return
s; > > public class Train04 < // функція main() тестує роботу класів Triangle та TriangleException
public static void main(String[] args) < double
a, b, c; Scanner in = new Scanner (System.in); // Введення a, b, c System.out.print("a color: #0000ff;">a = in.nextDouble(); System.out.print("b color: #0000ff;">b = in.nextDouble(); System.out.print("c color: #0000ff;">c = in.nextDouble(); // Обчислення площі Triangle tr = new Triangle(a,b,c); double
area = tr.getArea(); System.out.println("area color: #0000ff;">TriangleException успадкований від класу Exception, який у свою чергу, успадкований від класу Throwable. Таким чином, у класі TriangleException є доступними методи класу Throwable, у тому числі метод toString(), Який описує виняток. Клас TriangleException перевизначає метод toString() класу Throwable. Це робиться з метою більшої конкретизації опису самого винятку.
У функції main() для того, щоб ввести дані, використовується клас Scanner з пакета java.util. Тому на початку програми вводиться команда
import java.util.Scanner;
Результат роботи програми у разі, якщо ввести неправильні дані
a = 3 b = 1 c = 1 Exception: Error. Bad sides of triangle. area = 0.0
Результат роботи програми у разі, якщо ввести коректні дані
a = 2 b = 2 c = 2 area = 1.7320508075688772
9 кращих практик для обробки винятків у Java
Незалежно від того, новачок ви або професіонал завжди корисно освіжити в пам'яті методи обробки винятків, щоб переконатися, що ви і ваша команда можете впоратися з проблемами. Обробка винятків Java - непроста тема. Початківцям складно зрозуміти, і навіть досвідчені розробники можуть годинами обговорювати, як і які винятки слід створювати чи обробляти. Ось чому більшість команд розробників мають власний набір правил їх використання. І якщо ви новачок у команді, вас може здивувати, наскільки ці правила можуть відрізнятись від тих, які ви використовували раніше. Тим не менш, є кілька передових практик, які використовуються більшістю команд. Ось 9 найважливіших з них, які допоможуть вам розпочати роботу чи покращити обробку винятків.
1. Звільніть ресурси в блоці finally або використовуйте інструкцію "Try-With-Resource"
Досить часто ви використовуєте ресурс у своєму блоці try, наприклад InputStream, який вам потрібно закрити пізніше. Поширеною помилкою у таких ситуаціях є закриття ресурсу в кінці блоку try.
public void doNotCloseResourceInTry() < FileInputStream inputStream = null; try < File file = new File("./tmp.txt"); inputStream = новий FileInputStream(file); // використовуємо inputStream для читання файлу // не робіть цього inputStream.close(); >catch (FileNotFoundException e) <log.error(e); >catch (IOException e) <log.error(e); >>
Проблема в тому, що цей підхід працює відмінно доти, доки не генерується виняток. Усі оператори у блоці try будуть виконані, і ресурс буде закрито. Але ви недаремно додали блок try. Ви викликаєте один або кілька методів, які можуть викликати виняток, або, можливо, ви самі викликаєте виняток. Це означає, що ви можете не дійти до кінця блоку try. І, як наслідок, ви не закриєте ресурси. Тому слід помістити весь код очищення в блок finally або використовувати оператор try-with-resource.
Використовуйте блок Finally
На відміну від останніх кількох рядків вашого блоку try, блок finally завжди виконується. Це відбувається після успішного виконання блоку try, або після обробки виключення в блоці catch. Завдяки цьому ви можете бути впевнені, що звільніть усі захоплені ресурси.
public void closeResourceInFinally() < FileInputStream inputStream = null; try < File file = new File("./tmp.txt"); inputStream = новий FileInputStream(file); // використовуємо inputStream для читання файлу > catch (FileNotFoundException e) <log.error(e); >finally < if (inputStream != null) < try < inputStream.close(); >catch (IOException e) <log.error(e); >> > >
Оператор Java 7 "Try-With-Resource"
Інший варіант - це оператор try-with-resource, який я пояснив докладніше у введенні в обробку винятків Java. Ви можете використовувати його, якщо ваш ресурс реалізує інтерфейс AutoCloseable. Це те, що робить більшість стандартних ресурсів Java. Коли ви відкриваєте ресурс у пропозиції try, він автоматично закривається після виконання блоку try або обробки виняток.
public void automaticallyCloseResource() < File file = new File("./tmp.txt"); try (FileInputStream inputStream = new FileInputStream(file);) < // використовуємо inputStream для читання файлу > catch (FileNotFoundException e) < log.error(e); >catch (IOException e) <log.error(e); >>
2. Конкретні винятки краще
Чим конкретніший виняток, який ви генеруєте, тим краще. Завжди пам'ятайте, що колезі, який не знає вашого коду, а можливо, і вам через кілька місяців, необхідно викликати ваш метод та обробити виняток. Тому постарайтеся надати їм якнайбільше інформації. Це полегшує розуміння вашого API. В результаті метод, що викликає, зможе краще обробити виняток або уникнути його за допомогою додаткової перевірки. Тому завжди намагайтеся знайти клас, який найкраще підходить для вашої виняткової події, наприклад, генеруйте NumberFormatException замість IlegallegalArgumentException. І уникайте створення неспецифічного винятки.
public void doNotDoThis() throws Exception < . >public void doThis() throws NumberFormatException
3. Документуйте певні винятки
Щоразу, коли ви визначаєте виняток у сигнатурі вашого методу, ви також маєте задокументувати його у своєму Javadoc. Це має ту ж мету, що й попередня передова практика: надати зухвалому якомога більше інформації, щоб він міг уникнути або обробити виняток. Отже, не забудьте додати оголошення @throws у свій Javadoc і описати ситуації, які можуть викликати виняток.
/** * Цей метод робить щось надзвичайно корисне. * * @param input * @throws MyBusinessException, якщо . відбувається */ public void doSomething(String input) throws MyBusinessException
4.Генерування винятків із описовими повідомленнями
Ідея, що лежить в основі цієї передової практики, аналогічна двом попереднім. Але цього разу ви не надаєте інформацію, що викликає стороні вашого методу. Повідомлення про виключення читають усі, хто повинен розуміти, що сталося, коли виняток був зареєстрований у файлі журналу або у вашому інструменті моніторингу. Отже, він повинен якнайточніше описати проблему і надати найбільш актуальну інформацію для розуміння виняткової події. Не зрозумійте мене неправильно; Ви не повинні писати абзац тексту. Але вам слід пояснити причину виключення у 1-2 коротких реченнях. Це допомагає вашій групі експлуатації зрозуміти серйозність проблеми, а також полегшує аналіз будь-яких інцидентів, пов'язаних з обслуговуванням. Якщо ви виберете конкретний виняток, його ім'я класу, швидше за все, вже описуватиме тип помилки. Таким чином, вам не потрібно надавати багато додаткової інформації. Хорошим прикладом цього є NumberFormatException. Воно викликається конструктором класу java.lang.Long, коли ви надаєте String у неправильному форматі.
try <new Long("xyz"); >catch (NumberFormatException e)
Назва класу NumberFormatException вже каже вам про тип проблеми. Його повідомлення має містити лише рядок введення, який викликав проблему. Якщо ім'я класу виключення не таке виразне, вам необхідно надати необхідну інформацію в повідомленні.
17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: Для введення string: "xyz"
5. Спочатку перехопіть найбільш конкретний виняток
Більшість IDE допоможуть вам у цій найкращій практиці. Вони повідомляють про недосяжний блок коду, коли ви спочатку намагаєтеся перехопити менш конкретний виняток.Проблема в тому, що виконується лише перший блок catch, що відповідає винятку. Отже, якщо ви спочатку зловите IlegallegalArgumentException, ви ніколи не досягнете блоку catch, який повинен обробляти більш конкретне NumberFormatException, тому що це підклас IlegallegalArgumentException. Завжди спочатку перехоплюйте найбільш конкретний клас виключення та додавайте менш конкретні блоки перехоплення до кінця вашого списку. Приклад такого оператора try-catch представлений у наступному фрагменті коду. Перший блок catch обробляє все NumberFormatException, а другий - все IlegallegalArgumentException, які не є NumberFormatException.
public void catchMostSpecificExceptionFirst() < try < doSomething("Повідомлення"); >catch (NumberFormatException e) <log.error(e); >catch (IllegalArgumentException e) < log.error(e) >>
6. Не перехоплюйте Throwable
Throwable - це суперклас всіх винятків та помилок. Ви можете використовувати його в catch пропозиції, але ніколи не повинні цього робити! Якщо ви використовуєте Throwable у реченні catch, він не тільки перехопить усі винятки; він також перехопить усі помилки. JVM видає помилки, щоб вказати на серйозні проблеми, які не призначені для обробки програмою. Типовими прикладами цього є OutOfMemoryError або StackOverflowError. І те, й інше викликано ситуаціями, які знаходяться поза контролем додатка і не можуть бути оброблені. Отже, краще не перехоплюйте Throwable, якщо ви не абсолютно впевнені, що знаходитесь у винятковій ситуації, в якій ви можете або зобов'язані обробляти помилку.
public void doNotCatchThrowable() < try < // робить щось >catch (Throwable t) < // не робить цього! >>
7. Не ігноруйте виняток
Ви коли-небудь аналізували звіт про помилку, в якому виконувалася лише перша частина сценарію використання? Часто це викликано винятком, що ігнорується. Розробник, ймовірно, був упевнений, що воно ніколи не буде викликане, і додав блок catch, який не обробляє та не реєструє його. І коли ви знайдете цей блок, ви, найімовірніше, навіть знайдете один із відомих коментарів «Цього ніколи не буде».
public void doNotIgnoreExceptions() < try < // робить щось >catch (NumberFormatException e) < // це ніколи не виконається >>
Що ж, можливо, ви аналізуєте проблему, в якій сталося неможливе. Тому, будь ласка, ніколи не ігноруйте виняток. Ви не знаєте, як код зміниться у майбутньому. Хтось може видалити перевірку, яка запобігла винятковій події, не усвідомлюючи, що це створює проблему. Або код, який генерує виняток, змінюється і тепер генерує кілька винятків одного і того ж класу, а код, що викликає, не запобігає їх усі. Ви повинні хоча б написати повідомлення в журналі, яке повідомляє всім, що сталося немислиме і хтось повинен це перевірити.
public void logAnException() < try < // робить щось >catch (NumberFormatException e) < log.error("Це ніколи не повинно відбуватися:" + e); >>
8. Не пишіть у лог згенеровані винятки
Це, ймовірно, найчастіше ігнорована передова практика у списку. Ви можете знайти безліч фрагментів коду і навіть бібліотек, у яких виняток перехоплюється, реєструється та повторно генерується.
try <new Long("xyz"); >catch (NumberFormatException e)
Може здатися інтуїтивно зрозумілим реєструвати виняток, коли воно сталося, а потім повторно генерувати його, щоб той, хто викликає, міг обробити його відповідним чином.Але, в такому випадку, додаток буде писати в балку кілька повідомлень про помилки для одного і того ж винятку.
17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: Для введення string: "xyz" Опис в thread "main" java.lang.NumberFormatException: Для введення string: "xyz" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Long.parseLong(Long.java:589) at java.lang.Long.(Long.java:965) at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63) at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
Повторні повідомлення також не додають жодної інформації. Як пояснюється у найкращій практиці №4, повідомлення про виключення має описувати виняткову подію. А трасування стека повідомляє вам, в якому класі, методі та рядку було згенеровано виняток. Якщо вам потрібно додати додаткову інформацію, ви повинні перехопити виняток і обернути його на користувальницьке. Але обов'язково дотримуйтесь передової практики номер 9.
public void wrapException(String input) throws MyBusinessException < try < // робить щось >catch (NumberFormatException e) < throw new MyBusinessException("Повідомлення з описом помилки.", e); >>
Отже, перехоплюйте виняток лише якщо ви хочете його обробити. В іншому випадку вкажіть це в сигнатурі методу і дозвольте стороні подбати про це.
9. Оберніть виняток, не обробляючи його
Іноді краще зловити стандартний виняток і перетворити його на настроюваний. Типовим прикладом такого виключення є бізнес-виключення для конкретної програми або платформи. Це дозволяє додавати додаткову інформацію, а також ви можете реалізувати спеціальну обробку для вашого класу виключення.Коли ви це зробите, обов'язково встановіть вихідний виняток як причину. Клас Exception надає певні методи конструктора, які приймають Throwable як параметр. В іншому випадку ви втратите трасування стека і повідомлення про вихідне виключення, що ускладнить аналіз виняткової події, що викликала ваш виняток.
public void wrapException(String input) throws MyBusinessException < try < // робить щось >catch (NumberFormatException e) < throw new MyBusinessException("Повідомлення з описом помилки.", e); >>
Резюме
Як ви бачили, є багато різних речей, які ви повинні враховувати, коли генеруєте чи перехоплюєте виняток. Більшість з них мають на меті покращити читаність вашого коду або зручність використання вашого API. Найчастіше винятки є одночасно механізмом обробки помилок та засобом зв'язку. Тому вам слід обов'язково обговорити передові практики та правила, які ви хочете застосовувати, зі своїми колегами, щоб усі розуміли загальні концепції та використовували їх однаково.
Винятки в Java (Java Exception)
У повсякденному житті іноді виникають ситуації, що ми не планували. Наприклад, встаєш вранці на роботу, шукаєш зарядний пристрій до телефону, а його немає. Ідеш у ванну, щоб вмитися – відключили воду. Сів у машину – не заводиться. Але людина може легко справитися з такими непередбаченими ситуаціями. А як із ними справляються Java-програми, постараємося розібратися у цій статті.
Що таке винятки (exceptions java)
У світі програмування виникнення помилок та непередбачених ситуацій під час виконання програми називають винятком (exception).У програмі виключення можуть виникати внаслідок неправильних дій користувача, відсутності ресурсу на диску, або втрати з'єднання з сервером по мережі. Причинами винятків під час виконання програми можуть бути помилки програмування або неправильне використання API. На відміну від нашого світу, програма має чітко знати, як чинити в такій ситуації. Для цього Java передбачено механізм винятків.
Коротко про ключові слова try, catch, finally, throws
- try – визначає блок коду, у якому може статися виняток;
- catch - визначає блок коду, в якому відбувається обробка виключення;
- finally – визначає блок коду, який є необов'язковим, але за його наявності виконується у разі незалежно від результатів виконання блоку try.
- throw - використовується для порушення виключення;
- throws - використовується в сигнатурі методів для попередження, що метод може викинути виняток.
//метод зчитує рядок із клавіатури public String input() throws MyException catch (IOException e) < System.out.println(e.getMessage()); // у блоці finally закриваємо потік читання >finally < // при закритті потоку теж можливе виняток, наприклад, якщо він був відкритий, тому “обертаємо” код у блок try try < reader.close(); // пишемо обробку виключення при закритті потоку читання catch (IOException e) < System.out.println(e.getMessage()); >> if (s.equals("")) < // ми вирішили, що порожній рядок може порушити надалі роботу нашої програми, наприклад, на результаті цього методу нам треба викликати метод substring(1,2), тому ми змушені перервати виконання програми з генерацією свого типу виключення MyException за допомогою throw throw new MyException("String can not be empty!"); >return s; >
Навіщо потрібен механізм винятків?
Подивимося на приклад із реального світу.Уявіть, що автомобільна дорога має ділянку з аварійним мостом з обмеженою вантажопідйомністю. Якщо по ньому поїде автомобіль із масою, що перевищує вантажопідйомність моста, він може зруйнуватися, і ситуація для водія може стати, м'яко кажучи, винятковою. Щоб цього не сталося, дорожня служба завчасно встановлює попереджувальні знаки на дорозі. Водій автомобіля, дивлячись на попереджувальний знак, порівнюватиме масу свого автомобіля з дозволеною для проїзду мостом. Якщо вона перевищує її – він поїде об'їзною колією. Завдяки діям дорожньої служби водії вантажного транспорту, по-перше, отримали можливість заздалегідь змінювати свій шлях, по-друге, попереджено про небезпеку на головному шляху, і, нарешті, попереджено про неможливість використання мосту за певних умов.
Можливість попередження та вирішення виняткової ситуації в програмі для її продовження – одна з причин використання винятків Java. Механізм винятків також дозволяє захистити написаний вами код (програмний інтерфейс) від неправильного використання користувачем за рахунок валідації (перевірки) вхідних даних. Давайте тепер на секунду побудемо дорожньою службою. По-перше, ви повинні знати місця, де автомобілістів можуть чекати на неприємності. По-друге, вам потрібно заготовити та встановити попереджувальні знаки. І, нарешті, вам потрібно передбачити об'їзні маршрути у разі небезпеки на основному шляху. У Java механізм виключень працює схожим чином. На стадії розробки програми ми «захищаємо» небезпечні ділянки коду щодо виключень за допомогою блоку try<>, передбачаємо «запасні» шляхи за допомогою блоку catch<>, у блоці finally<> ми пишемо код, який виконується у програмі за будь-якого результату.У випадках, коли ми не можемо передбачити «запасний шлях» або навмисно хочемо надати право вибору користувача, ми повинні принаймні попередити його про небезпеку. Чому? А ви тільки уявите обурення водія, який доїде до аварійного мосту, яким не можна проїхати, не зустрівши по дорозі жодного попереджувального знака! У програмуванні при написанні своїх класів та методів ми не завжди можемо передбачати контекст їх використання іншими розробниками у своїх програмах, тому не можемо передбачати на 100% правильний шлях для вирішення виняткової ситуації. У той же час правило хорошого тону — попередити користувачів нашого коду про можливість виняткової ситуації. Механізм виключень Java дозволяє нам зробити це за допомогою throws – по суті, оголошення загальної поведінки нашого методу, що полягає у викиданні виключення, і надаючи таким чином написання коду обробки виключення в Java користувачеві методу.
Попередження про «неприємності»
Коли ви не плануєте обробляти виняток у своєму методі, але хочете попередити користувачів методу про можливі виняткові ситуації, використовуйте ключове слово throws. Це ключове слово в сигнатурі методу означає, що за певних умов метод може викинути виняток. Таке попередження є частиною інтерфейсу методу та надає право користувачеві на власний варіант реалізації обробника виключення. Після throws ми вказуємо тип виключення, що викидається. Зазвичай, це спадкоємці класу Exception Java. Оскільки Java є об'єктно-орієнтованою мовою, всі винятки в Java є об'єктами.
Ієрархія винятків Java
- Ситуації, за яких відновлення подальшої нормальної роботи програми неможливе
- Відновлення можливе.
Створення виключення
При виконанні програми виняток генерується JVM або вручну за допомогою оператора throw . При цьому в пам'яті створюється об'єкт виключення та виконання основного коду програми переривається, а обробник винятків JVM намагається знайти спосіб обробити виняток.
Обробка виключення
Створення блоків коду, для яких ми передбачаємо обробку винятків Java, проводиться в програмі за допомогою конструкцій try<>catch, try<>catch<>finally, try<>finally<>.
При збудженні виключення в блоці try обробник виключення шукається в наступному блоці catch. Якщо в catch є обробник цього виду виключення - управління переходить до нього. Якщо ні, то JVM шукає обробник цього типу виключення в ланцюжку викликів методів доти, доки не буде знайдено відповідний catch. Після виконання блоку catch керування передається в необов'язковий блок finally. У випадку, якщо відповідний блок catch не знайдений, JVM зупиняє виконання програми і виводить стек викликів методів – stack trace , виконавши перед цим код блоку finally за його наявності. Приклад обробки винятків:
public class Print < void print(String s) < if (s == null) < throw new NullPointerException("Exception: s is null!"); >System.out.println("Inside method print: " + s); > public static void main(String[] args) < Print print = new Print(); Listlist=Arrays.asList("first step", null, "second step"); for (String s:list) <try <print.print(s); >catch (NullPointerException e) < System.out.println(e.getMessage()); System.out.println("Exception був processed. Program continues"); >finally < System.out.println("Inside block finally"); >System.out.println("Go program. "); System.out.println("-----------------"); > > >
Inside method print: перший Step Inside blosk finally Go program. ----------------- Exception: s is null! Висновок був processed. Program continues Inside blosk finally Go program. ----------------- Inside method print: second step Inside blosk finally Go program. -----------------
Блок звичайно зазвичай використовується для того, щоб закрити відкриті в блоці try потоки або звільнити ресурси. Однак при написанні програми не завжди можна встежити за закриттям усіх ресурсів. Для полегшення нашого життя розробники Java запропонували нам конструкцію try-with-resources, яка автоматично закриває ресурси, відкриті у блоці try. Наш перший приклад можна переписати так за допомогою try-with-resources:
public String input() throws MyException < String s = null; try(BufferedReader reader = новий BufferedReader(новий InputStreamReader(System.in)))< s = reader.readLine(); >catch (IOException e) < System.out.println(e.getMessage()); >if (s.equals("")) < throw new MyException ("String can not be empty!"); >return s; >
Завдяки можливостям Java, починаючи з версії 7, ми також можемо поєднувати перехоплення різнотипних винятків в одному блоці, роблячи код компактнішим і читабельнішим. Наприклад:
public String input() < String s = null; try (BufferedReader reader = новий BufferedReader(новий InputStreamReader(System.in))) < s = reader.readLine(); if (s.equals("")) < throw new MyException("String can not be empty!"); >> catch (IOException | MyException e) < System.out.println(e.getMessage()); >return s; >
Підсумки
Використання винятків у Java дозволяє підвищити стійкість до відмови програми за рахунок використання «запасних» шляхів, відокремити логіку основного коду від коду обробки виняткових ситуацій за рахунок використання блоків catch, а також дає нам можливість перекласти обробку виключень на користувача нашого коду за допомогою throws.