JavaRush /Java блог /Random UA /Винятки в Java
Roman
33 рівень

Винятки в Java

Стаття з групи Random UA
Коли зіткнувся з темою "Виключення", то виникло багато питань, на які довелося шукати відповіді з різних закутків інтернету, щоб зрозуміти в деталях, як все це працює. В результаті, я склав власне пояснення, яке може виявитися більш зрозумілим для новачків, які тільки-но зіткнулися з цим явищем. Винятки в Java - 1У комп'ютерах Переривання- це вхідний сигнал для процесора, про те, що відбувається подія, яка вимагає негайної реакції. Сигнал переривання вимагає від процесора призупинити програму з такою умовою, щоб вона могла бути продовжена трохи пізніше, тобто комп'ютер повинен запам'ятати всю інформацію, пов'язану з виконанням програми. Такі переривання тимчасові, а то й носять фатальний характер. Такі переривання можуть бути викликані як програмним кодом, так і деякою функціональністю заліза (наприклад, просто натисканням клавіш на клавіатурі; таймери наприклад, для автоматичного відключення комп'ютера). Кількість перериваньобмежено певним числом, закладеним при виробництві того чи іншого процесора, тобто під це виділяються спеціальні «канали» зв'язку, що дозволяють звернутися до процесора в обхід решти всіх процесів. Переривання так само автоматично генеруються при виникненні помилки у програмному коді, що виконується (наприклад, якщо зустрічається поділ на нуль). Такі переривання традиційно називаються traps або exceptions . У таких випадках прийнято говорити: "Exception was thrown", тобто спрацювало Виняток або Виняток було запущено (кинуто), тобто запит на Перериванняіз запитанням «що робити?» посланий процесору. У цей момент процесор зупиняє роботу, запам'ятовуючи точку, в якій він зупинився, точніше кластер наступного осередку, інформація з якої має бути виконана. Запам'ятовується весь ланцюжок виконаних інструкцій та НЕ виконаних. Після чого процесор зчитує з пам'яті інструкції для дій при подібній помилці. Відповідно до цієї інструкції, він може занести нові значення до певних кластерів, додати деякі ланцюжки дій або новий цикл (наприклад, цикл повернення або кільцювання) і т.д., тобто в залежності від помилки виконуються раніше закладені інструкції. У систему комп'ютера саме по собі вбудовано багато автоматичних переривань, які запускаються через певну кількість часу, наприклад, контролю запущених на комп'ютері процесів чи виконання закладених будильників, збору вхідних зовнішніх сигналів, різних конвертерів даних. Варто пам'ятати, що велика кількість переривань з ряду причин може остаточно «повісити» систему. Помилка в програмному коді викликає в автоматичному режимі Переривання в процесорі, яке спробує обробити відповідно до закладених інструкцій. Але не для всіх переривань закладено їх обробку або вона може видавати процедуру, яка нас не влаштовує, наприклад, просто згорне додаток. Тому в програмуванні існує можливість організувати власне переривання для певної ділянки коду, в якому програміст потенційно бачить можливість помилки. У цьому випадку помилка обробиться всередині програми і не звертатиметься за інструкціями з обробки до процесора. Завдання таких блоків організується створенням об'єкта"Виняток" (Exception) . Цей об'єкт автоматично створюється у блоці try-catch. У блоці >tryпроводиться перевірка на наявність помилки і, якщо вона є, програма слідує в блок catch, де виконуються дії, що попереджають або нівелюють помилку. Наприклад, якщо ми вводимо з клавіатури Числа , які повинні складатися і відніматися, то введення з клавіатури Букв призведе до неможливості їх складання з Числами (позначимо суму цих двох змінних буквою S). Тому в командіtryми повинні перевірити чи здатне число А, що містить Цифри, бути складено з числом Б, що містить Букви (тобто S = А + Б ), і якщо це неможливо, а це неможливо, то повинні бути вжиті певні заходи, щоб Помилки НЕ сталося до процесора не полетіло нове переривання з питанням «що робити?». У разі відсутності у програмі Винятки, її виконання перерветься процесором. За наявності Винятки, коли його «піймано» командою try, управління переходить команді catch, яка може задати альтернативний варіант рішення, наприклад, не будемо складати ці два числа, а задамо S = А.
int a = 4;
String b = “hello”;
int S = 0;
 try {
   S = a + b;
   int r = 1;
 } catch (Exception igogo1) {
   S = a;
 }
 return S;
/* рядок "int r = 1;" не виконується, оскільки сталася помилка і програма перенаправляє роботу відразу до оброблювача виключення блок catch*/ Таким чином, наявність Винятків – це можливість вирішити проблему всередині програми, не закидаючи її на рівень процесора. Об'єкт «Виключення», який автоматично створюється в блоці tryпри виявленні помилки, заноситься значення типу помилки. Назвемо його «НашеВиключення» – для нашого конкретного випадку з описом нашої конкретної помилки. Творці мови Java, заздалегідь створабо певний список типових помилок та типові варіанти їх виправлення, тобто у java існує деяка бібліотека Винятків, До якої ми можемо звернутися для обробки помилки, щоб самим не писати код обробки і тому НашеВиключення швидше за все вже кимось було описано, тому нам треба просто знати назву якого з цих винятків вставити в нашу програму для обробки коду, де потенційно може трапиться збій. Якщо ми помабомося і виберемо з бібліотеки неправильне Виняток , то обробник його не «зловить», помилка не знайде рішення всередині програми та полетить запит до процесора. Але існує шлях для лінивих. Якщо ми не знаємо назву потрібного нам виключення з бібліотеки, то можемо взяти спільну назву « Exception », як у вищеописаному прикладі. Це Винятокздатне обробити будь-який тип помилки, тільки здатне видати конкретну інформацію про подію, яку ми могли б занести в логи. Бібліотека раніше написаних Винятків складається з перевірених (checked) та неперевірених (unchecked) Винятків . Перевірені – це ті, які можна виправити, не обриваючи роботу програми, тобто, якщо ми намагаємося відкрити файл у папці, в якій його немає, система дасть нам про це знати, ми можемо закинути файл у потрібну папку і продовжити роботу програми. Тобто за фактом до процесора надіслано запит на переривання, але без питання: «Шукай, що робити з цією проблемою?!?!». Ми надіслали Переривання, яке самі виявабо, з готовою інструкцією, яку процесор обробив і продовжив виконання програми. Неперевірені – це помилки, які не можна виправити і програма буде закрита до свого завершення, тобто до процесора полетить запит на Переривання, яке у будь-якому випадку обірве виконання програми. Єдиний сенс прописувати такі винятки у програмі – це дати користувачеві зрозуміти, що сталося, оскільки, зловивши це переривання, ми можемо вивести інформаційне повідомлення на екран, через що програма згорнулася. Другою причиною ловити такі переривання є можливість занести їх у логи для подальшого аналізу (вас хакнули, зате ви знаєте через яке місце). Наслідком наявності таких бібліотек є необхідність не забути їх підключити. (Список перевірених та неперевірених Винятків з бібліотеками можна подивитися, наприклад, тут ) Якщо ми не знаємо точно яку бібліотеку включити або існує кілька варіантів помилки, то можемо в кількохcatchперерахувати потрібні Винятки. Система сама вибере правильний обробник, якщо він є у списку. Замість конкретного Виняток можна написати загальне « Exception », яке може обробити будь-який тип Виняток , якщо воно не обробилося у попередніх блоках.
int a = 4;
String b = “hello”;
int S = 0;
 try {
   S = a + b;
   int r = 1;
 }
catch(NullPointerException blabla2) {
   System.out.println("Exception handling code for the NullPointerException.");
 }
catch (ArithmeticException ex1) {
   S = a;
 }
catch(Exception uups1) {
   System.out.println("Exception occured");
 }
 return S;
У разі наявності блоку tryВиняток створюється автоматично. Якщо нам треба примусово в якийсь момент викликати Виняток , то використовується команда throw. Тобто ми самостійно створюємо об'єкт new throw… після чого програма зупиняє свою роботу, посилає процесору запит на Переривання і переноситься до розділу програми catch, звідки намагається почерпнути інструкції з подальших дій. Створюючи вручну Виняток , ми можемо вказати його конкретний тип бібліотеки:

throw new ArithmeticException("Access denied - You must be at least 18 years old.");
тоді обробник шукатиме блок catch, з саме цим Винятком – шукати по всій програмі, з усіх боків catch. Після команди throwобробки Винятки, весь код програми НЕ буде виконаний, за винятком того, який знаходиться в блоці catch. Якщо обробник у програмі не знайдено, процесору запитує: «вирішуй сам, що робити» і той перериває програму. Виклик new throw… може здійснюватися як у блоці >try, так і поза ним (у будь-якому місці програми)
try {
   /* функция або действие, в котором есть сомнения. То есть: «попробуй выполнить это, а если не получится, а, если не получится, запускай режим исключения» */
   throw new CallForException(); /* Назначаем исключение, которое будет работать в случае наличия ошибки в функции, описанной выше. Здесь исключение «CallForException» - берется из библиотеки существующих исключений */
} catch (CallForException ee1) {
   /* Корректируем ошибку, чтобы программа не «отвалилась» або выводим сообщение об ошибке або что-то ещё */
} finally {
   /* этот блок работает всегда независимо от того была ошибка або нет. А если была, то сработало ли решение в catch або нет */
   /* часто используется для подчистки хвостов, например, для закрытия запущенного файлу або базы данных */
   /* в ряде случаев блок catch вообще может быть опущен и оставлен только блок finally и наоборот finally может быть опущен и оставлен только catch */
   /* Не допускается использование этого блока в ряде случаев, например, когда функция System.exit() запущена або другие системные Исключения, типа «отключение электроэнергии» и т.п. */
}

Оповіщення про наявність Винятки

Раніше написані кимось методи можуть включати виклик Винятків. Просто про всяк випадок програміст, який писав код, попередив наступних програмістів, що у написаному їм методі може статися помилка. Так, наприклад, метод створення файлу, описаний нижче, передбачає, що при створенні файлу може статися помилка (немає файлу по заданому шляху), а отже, потрібен буде її обробник:
public void createFile(String path, String text) throws IOException {
    FileWriter writer = new FileWriter(path, true);
    writer.write(text);
    writer.close();
}
Але при цьому самого оброблювача немає, а отже тепер просто викликати написаний метод у своїй програмі у звичайному режимі ми не зможемо. Тепер ми зобов'язані написати обробник помилки і викликати цей метод у блоці try:
String filePath = "hello.txt";
String text = "Hello World";

try {
    createFile(filePath, text);
} catch (IOException ex) {
    System.err.println("Error creating file: " + ex);
}

Власні винятки

Існує можливість написати власні винятки для обробки тих чи інших помилок, якщо нам недостатньо вже існуючих бібліотек. Для цього ми просто створюємо клас-спадкоємець від класу Винятки
public class StudentNotFoundException extends Exception {

    public StudentNotFoundException (String message) {
        super(message);
    }
}
При створенні власних винятків слід враховувати два правила:
  1. Назва нашого класу має закінчуватись на «Exception»
  2. Клас повинен містити конструктор з рядковою змінною, що описує деталі проблеми Винятки. У конструкторі викликається супер-конструктор із передачею повідомлення.
Приклад використання створеного виключення:
public class StudentManager {
    public Student find(String studentID) throws StudentNotFoundException {
        if (studentID.equals("123456")) {
            return new Student();
        } else {
            throw new StudentNotFoundException(
                "Could not find student with ID " + studentID);
        }
    }
}
Відловлюємо це Виняток кодом:
public class StudentTest {
    public static void main(String[] args) {
        StudentManager manager = new StudentManager();
         try {
            Student student = manager.find("0000001");
        } catch (StudentNotFoundException ex) {
            System.err.print(ex);
        }
    }
}
Результатом виконання програми буде: StudentNotFoundException: Could not find student with ID 0000001

Навіщо потрібно писати Винятки

У 1996 році розбилася ракета Ariane 5 через неправильну конвертацію змінної float в змінну integer. Винятків та обробників цієї ситуації передбачено не було. Якщо при завантаженні файлу стався обрив зв'язку з інтернетом, то Виняток дозволить продовжити завантаження, після того як з'єднання відновиться. За відсутності Винятки завантаження доведеться починати заново.

Використана література:

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ