1. Види винятків
Усі винятки поділяються на 4 види, які насправді є класами, успадкованими один від одного.
Клас Throwable
Найбільш базовим класом для всіх винятків є клас Throwable
. Клас Throwable
містить код, який записує поточний стек-трейс викликів функцій у масив. Що таке стек-трейс викликів ми вивчимо трохи згодом.
В оператор throw можна передати лише об’єкт класу-спадкоємця Throwable
. І хоча теоретично можна написати код виду throw new Throwable();
— так зазвичай ніхто не робить. Головна мета існування класу Throwable
— єдиний клас-предок для всіх винятків.
Клас Error
Наступним класом винятків є клас Error
— прямий спадкоємець класу Throwable
. Об’єкти типу Error
(і його класів-спадкоємців) створює Java-машина у разі будь-яких серйозних проблем. Наприклад, збій у роботі, брак пам’яті тощо.
Зазвичай ви як програміст нічого не можете зробити у ситуації, коли в програмі виникла помилка типу
Error
: занадто серйозна така помилка. Все, що ви можете зробити, — повідомити користувача, що програма
аварійно завершується, або записати всю відому інформацію про помилку в лог програми.
Клас Exception
Винятки типу Exception
(і RuntimeException
) — це звичайні помилки, що виникають під час роботи
багатьох методів. Мета кожного викинутого винятку — бути захопленим тим блоком catch
, який знає, що потрібно зробити в цій ситуації.
Коли певний метод не може виконати свою роботу з якоїсь причини, він відразу повинен повідомити про це метод, що викликає, викидаючи виняток відповідного типу.
Інакше кажучи, якщо якась змінна виявилася рівною null
, метод викине NullPointerException
,
якщо метод передали неправильні аргументи — викине InvalidArgumentException
, якщо в методі випадково було
ділення на нуль — ArithmeticException
.
Клас RuntimeException
RuntimeException
— це різновид (підмножина) винятків RuntimeException
. Можна навіть сказати, що
RuntimeException
— це полегшена версія звичайних винятків (Exception
): до таких винятків
висувається менше вимог та обмежень
Про відмінність Exception
та RuntimeException
ви дізнаєтесь далі
2. Винятки, що перевіряються: throws
, checked exceptions
Усі винятки в Java поділяються на 2 категорії —ті, що перевіряються (checked) і ті, що не перевіряються (unchecked).
Всі винятки, успадковані від класів RuntimeException
та Error
, вважаються unchecked-винятками, решта — checked-винятками.
Через 20 років після введення винятків, що перевіряються, майже всі Java-програмісти вважають це помилкою. 95 % усіх винятків у популярних сучасних фреймворках — ті, що не перевіряються. Та сама мова C#, яка мало не скопіювала Java повністю, не стала додавати checked-винятки.
У чому ж основна відмінність checked-винятків від unchecked?
До checked-винятків є додаткові вимоги. Звучать вони приблизно так.
Вимога 1
Якщо метод викидає checked-виняток, він повинен містити тип цього винятку у своєму заголовку (сигнатурі методу). Щоб усі методи, що викликають цей метод, знали про те, що в ньому може виникнути такий «важливий виняток».
Вказувати checked-винятки треба після параметрів методу після ключового слова throws
(не плутати з throw
). Виглядає це приблизно так:
тип метод (параметри) throws виняток
Приклад:
checked-виняток | unchecked-виняток |
---|---|
|
|
У прикладі праворуч наш код викидає unchecked-виняток — жодних додаткових дій не потрібно. У прикладі ліворуч метод викидає checked-виняток, тому в сигнатуру методу додали ключове слово throws
і вказали тип винятку.
Якщо метод планує викидати кілька checked-винятків, всі їх потрібно вказати після ключового слова throws
через кому. Порядок неважливий. Приклад:
public void calculate(int n) throws Exception, IOException
{
if (n == 0)
throw new Exception("n дорівнює нулю!");
if (n == 1)
throw new IOException("n дорівнює одиниці");
}
Вимога 2
Якщо ви викликаєте метод, у якого в сигнатурі прописані checked-винятки, то ви не можете проігнорувати цей факт.
Ви повинні або перехопити всі ці винятки, додавши блоки catch
для кожного з них, або додати їх в throws
свого методу.
Ми ніби говоримо собі: ці винятки настільки важливі, що ми обов’язково повинні їх перехопити. А якщо ми не знаємо, як їх перехопити, ми повинні повідомити тих, хто буде викликати наш метод, що в ньому можуть виникнути такі винятки.
Приклад:
Припустимо, що ми пишемо метод, який має створити світ, населений людьми. Початкова кількість людей передається як параметр. Тоді ми маємо додати винятки, якщо людей занадто мало.
Створюємо Землю | Примітка |
---|---|
|
Метод потенційно кидає два checked-винятки:
|
Виклик цього методу можна обробити 3 способами:
1. Не перехоплюємо винятки, що виникають
Найчастіше це робиться у разі, коли в методі не відомо, як правильно обробити цю ситуацію.
Код | Примітка |
---|---|
|
Метод, що викликає, не перехоплює винятки і змушений інформувати про них інших: додати їх собі в throws |
2. Перехоплювати частину винятків
Обробляємо зрозумілі помилки, незрозумілі — прокидаємо в метод, що викликає. Для цього потрібно додати їх назву в throws:
Код | Примітка |
---|---|
|
Метод, що викликає, перехоплює лише один checked-виняток – СамотнійСвіт , другий він повинен додати у свою сигнатуру: вказати після слова throws |
3. Перехоплюємо всі винятки
Якщо метод не прокидає винятки методу, що викликає, метод, що викликає, завжди буде впевнений, що все було виконано добре. І не зможе вжити жодних дій, щоб виправити ситуацію.
Код | Примітка |
---|---|
|
У цьому методі перехоплюються всі помилки. Метод, що викликає, буде впевнений, що все пройшло чудово. |
3. Множинне перехоплення винятків
Програмісти дуже не люблять дублювання коду. Навіть вигадали такий принцип розробки — DRY: Don’t Repeat Yourself. Однак час обробки винятків часто виникають ситуації, коли після блоку try
слідує кілька блоків catch
з однаковим кодом.
Або може бути, наприклад, 3 catch
-блоки з одним кодом і ще 2 catch
-блоки з іншим. Стандартна
взагалі-то ситуація, коли у вас у проєкті відповідально ставляться до обробки виключень.
Починаючи з 7-ї версії, в мову Java додали можливість вказати кілька типів винятків в одному блоці catch
.
Виглядає це приблизно так:
try
{
код, де може виникнути помилка
}
catch(ТипВинятку1 | ТипВинятку2 | ТипВинятку3 имя)
{
код обробки винятків
}
Блоків catch
може бути скільки завгодно. Однак в одному блоці catch
не можна вказати винятки,
які успадковуються один від одного. Тобто не можна написати catch
(Exception
|
RuntimeException
e), оскільки клас RuntimeException успадкований від Exception
.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ