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.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ