Назад Зміст Вперед

9.2. Блоки try, catch, finally

Перехоплення винятків із використанням try та catch

Виняток — це помилка виконання програми. Винятки виникають при:
  •  неправильно написаному програмному коді;
  • виконанні неприпустимих арифметичних операцій (наприклад, при діленні на нуль);
  • неналежному використанні порожнього посилання;
  • виході елементу поза межі массиву;
  • переповненні пам'яті та ін.
Коли при виконанні програми виникає помилка, то створюється об'єкт з її описом, який перехоплюється та обробляється. У звичайних ситуаціях, коли виникає виняток, то програма припиняє своє виконання і в консоль, в середовище розробки чи у файл виводяться відомості про помилку. У мові Java є можливість обробки винятків, коли при виникненні помилки виконання програма не завершується, а здійснює певні операції, вказані програмістом.

Відслідковування та перехоплення винятків

Інструкція try для відслідковування винятків

Щоб виділити код, який потрібно відслідковувати на виникнення винятків, використовують ключове слово try. Після слова try пишеться блок, в якому розміщується програмний код, де можливе виникнення винятку(помилки виконання):
try {
  програмний_код
}

Інструкція catch для перехоплення винятків

Після інструкції try обов'язково має бути інструкція перехоплення винятків, яка позначається ключовим словом catch. Після слова catch в дужках (параметри) вказується перехоплений об'єкт винятку, який був створений у блоці try. Після параметрів пишеться блок коду, який має виконатися при перехопленні винятку:
try { /* всередині цього блоку пишеться програмний код, який буде відслідковуватися на
виникнення винятків */
  програмний_код
}
catch (об'єкт_винятку) { /* всередині цього блоку пишеться код, який має виконатися при перехоленні вказаного в параметрах об'єкту винятку */
  програмний_ код
}

Об'єкти винятків

Усі класи винятків є спадкоємцями класу Trowable, що знаходиться в пакеті java.lang. Від класу Trowable безпосередньо походять два класи:
  • клас Error містить об'єкти винятків, які описують помилки, що сталися всередині самого середовища виконання. Це такі помилки як переповнення стеку, нестача пам'яті комп'ютера для виконання програми, помилки всередині віртуальної машини. Ці винятки не мають безпосереднього відношення до коду програми, тому такими винятками програмісти користуються не часто. 
  • клас Exception містить винятки, які описують помилки всередині програми, яку пише програміст. Саме цими винятками часто користуються програмісти. Ось деякі підкласи похідного від Exception класу RuntimeException: 
-IndexOutOfBoundsException — індексування елементу масиву вийшло за розміри масиву;
-NullPointerException — над порожнім посиланням здійснюються неприпустимі операції;
-ClassNotFoundException — здійснюється доступ до класу, якого не існує;
-ArithmeticException — здійснюється неприпустима арифметична операція;
-SecurityException — порушення безпеки доступу.

Програма TryCatch.java, яка показує відслідковування та перехоплення винятків:
package inwe;

class TryCatch {
  public static void main(String args[]) {
    int masuv[] = new int[14]; /* створюється масив masuv типу int, що складається із 14 елементів */
    int zminna = 45;
      
    try {
      // zminna = 1 / 0;
      masuv[14] = 28; /* робиться спроба здійснити доступ до 15 елемента масиву, якого не існує */     
    }
    catch(IndexOutOfBoundsException vunjatok) { /* інструкція catch перехоплює об’єкт класу IndexOutOfBoundsException */
    /* Якщо даний об'єкт перехоплюється інструкцією сatch, то виводиться повідомлення про помилку та вміст об'єкту винятку */
      System.out.println(Помилка виконання програми:  + vunjatok);
    }
   
    /* Хоч у програмі виникла помилка, завдяки інструкціям try та catch програма не припиняє виконання, тому наступне повідомлення теж виведеться в консоль */
    System.out.println(zminna =  + zminna);}
  }
}
Результат:
Помилка виконання програми: java.lang.ArrayIndexOutOfBoundsException: 14
zminna = 45

У цій програмі в блоці try здійснюється відслідковування винятку класу IndexOutOfBoundsException, тобто помилки індексації елементу поза межами масиву. Так як така помилка наявна в блоці try, інструкція catch перехоплює даний виняток, тип якого(IndexOutOfBoundsException) вказаний у її параметрах.

Також в блоці try програми TryCatch.java закоментований вираз, в якому присутня арифметична помилка ділення на нуль. Якщо розкоментувати цю стрічку, то програма припинить виконання і виведе повідомлення про помилку. Це відбувається тому, що інструкція catch в цій програмі не перехоплює виняток типу ArithmeticException, тому програма виконається так, як і в звичайних ситуаціях із помилками — відразу припинить виконання і виведе повідомлення про помилку.

Інструкція finally

Інструкція finally застосовується після інструкцій try-catch в тому випадку, коли незалежно від того, чи перехоплюється виняток, чи не перехоплюється, чи програма прининяє своє виконання, потрібно щоб виконався певний програмний код (програма TryFinally.java):
package inwe;
import java.util.Scanner;
/* В програму включається клас InputMismatchException, який перехоплює помилкy неправильного типу введених даних */
import java.util.InputMismatchException;

public class TryFinally {
  public static void main(String args[]) {
    int quslo = 0, index = 0, diljnuk = 1;
    int[] masuv = new int[10];
    Scanner vvid = new Scanner(System.in); /* створюється клас для отримання введених у консоль даних */
   
    while(true) { /* оголошується "вічний" цикл, який прининяється ключовим словом break всередині циклу */
      System.out.println(Введіть число 1, 2 або 3: );
      try { // відслідковується правильність типу введених даних
        quslo = vvid.nextInt(); /* якщо введені дані не належать до типу int, то створюється виняток, який перехоплюється наступною інструкцією catch */
      }
      catch(InputMismatchException vunjatok) {
        System.out.println(Введені неправильні дані);
        vvid.next(); // об’єкт vvid очищається від неправильних даних
        continue; // цикл while починається спочатку
      }
      if(quslo == 1) {
        index = 25;
        break; // цикл завершується
      } else if(quslo == 2) {
        diljnuk = 0;
        break; // цикл завершується
      } else if(quslo == 3) break;
      else {
        System.out.println(Введене неправильне число);
      }
    }
   
    try {
      masuv[index] =  100; /* якщо змінній index було присвоєно число 25, то виникне помилка виходу за межі масиву(ArrayIndexOutOfBoundsException), так як масив masuv складається з десяти елементів */
      quslo = 10/diljnuk; /*  якщо змінній diljnuk було присвоєно число 0, то виникне помилка ArithmeticException(ділення на нуль) */
     
      /* Якщо в інструкціїї try не виникне помилка, то в консоль виведеться наступне повідомлення */
      System.out.println(Помилок не виявлено);
      return; // завершення методу main()
    }
    catch(ArrayIndexOutOfBoundsException | ArithmeticException vunjatok) {
        System.out.println(Причина помилки:  + vunjatok);
    }
    finally { /* код в інструкції finally виконається незалежно від того, чи було перехоплено помилку, чи ні */
      System.out.println(Завершення роботи об'єкту vvid.);
      vvid.close(); // об’єкт vvid закривається
    }
  }
}
Можливий результат виконання програми:
Введіть число 1, 2 або 3:
g
Введені неправильні дані
Введіть число 1, 2 або 3:
5
Введене неправильне число
Введіть число 1, 2 або 3:
1
Причина помилки: java.lang.ArrayIndexOutOfBoundsException: 25
Завершення роботи об'єкту vvid.

Цю програму можна виконати по-різному, де в інструкції try буде виникати помилка або ні, але закривання об'єкту vvid та виведення повідомлення про закривання об'єкту будуть здійснюватися все одно, так як вони знаходяться в блоці інструкції finally. Вміст блоку finally в цій програмі виконується навіть тоді, коли в інструкції try при відсутності помилок за допомогою ключового слова return здійснюється завершення методу main().

Вкладені інструкції try

Всередині блоку try може знаходитися ще один блок try:
try { // зовнішній try
  ...
  try { // внутрішній(вкладений) try
    ...
  }
}

Внутрішня інструкція try працює так само, як і зовнішня, але із одним доповненням — винятки внутрішнього try можуть перехоплюватися інструкціями catch зовнішнього try:
try { // зовнішній try
  ...
  try { // внутрішній(вкладений) try
    ...
  }
}
catch(Exception vunjatok) { /* ця інструкція catch може перехоплювати винятки як
зовнішньої, так і внутрішньої інструкцій try*/
  ...
}

Вкладені всередину методів інструкції try

Якщо інструкція try знаходиться в методі та цей метод виконується у блоці try, то try всередині методу буде працювати як внутрішній по відношенню до try, в якому виконується метод:
package inwe;

public class TryMetod {
  static int a = 0;
  static boolean bool = true;
  static void vnytriwnijTry() {
    try {
      // a = a / 0;
      a = bool;
    }
    catch(ArithmeticException vunjatok) {
      System.out.println(Сталася помилка ділення на нуль);
    }
  }
     
  public static void main(String args[]) {
    try {
      vnytriwnijTry(); /* інструкція try всередині цього методу працює як вкладена по відношенню до даної інструкції try */
    }
    catch(Exception vunjatok) { /* ця інструкція також перехоплює винятки інструкції try всередині методу vnytriwnijTry() */
      System.out.println(Сталася помилка:  + vunjatok);
    }
  }
}
Результат:
Сталася помилка: java.lang.RuntimeException: Uncompilable source code - incompatible types: boolean cannot be converted to int

При виконанні цієї програми в блоці try всередині методу vnytriwnijTry() може виникнути два винятки: ділення на нуль (перехоплюється всередині методу, якщо розкоментувати вираз a=a/0;) або несумісність типів ( перехоплюється інструкцією catch ззовні методу vnytriwnijTry()).

Зчеплені винятки

В мові Java винятки можна пов'язувати між собою таким чином, щоб один виняток виступав причиною виникнення іншого. Для того, щоб вказати причину винятку, можна користуватися конструкторами:
// Конструктор створює виняток із зчепленим причиною-винятком
Throwable(Throwable причина_винятку)
// Конструктор створює виняток із описом винятку та із зчепленим причиною-винятком
Throwable(String опис_винятку, Throwable причина_винятку)
Також можна зчепити причину-виняток із винятком за допомогою метода initCause():
Throwable initCause(Throwable причина_винятку)
Щоб отримати доступ до причини-винятку, використовують метод getCause():
Throwable getCause() // повертає причину-виняток для винятку, що викликав цей метод
Програма, що показує використання зчеплення винятків(ZqepleniVunjatku.java):
package inwe;
import java.util.Scanner;
import java.util.InputMismatchException;
import java.io.IOException;
  
class ZqipleniVunjatku {
  public static void main(String args[]) {
    double dilene = 1, diljnuk = 1;
    ArithmeticException arufmVunjatok =
      new ArithmeticException(ділення на нуль);
    // об'єкту arufmVunjatok прив'язується як причина виняток IOException
    arufmVunjatok.initCause(new IOException(помилка вводу даних));
   
    try(Scanner vvid = new Scanner(System.in)) { /* об'єкт vvid автоматично закриється після завершення блоку try-with-resources */
      for(;;) {
        System.out.println(***Ділення одного числа на інше***);
        System.out.print(Введіть значення діленого: );
        dilene = vvid.nextInt();
        System.out.print(Введіть значення дільника: );
        diljnuk = vvid.nextInt();
        // Якщо має відбутися ділення на нуль
        if(diljnuk == 0) throw arufmVunjatok;
        System.out.println(Значення  + dilene + / + diljnuk +  =  +
          (dilene / diljnuk));
      }
    }
    catch(ArithmeticException vunjatok) {
      System.out.println(Помилка виконання програми:  + vunjatok);
      // Виводиться в консоль опис причини-винятку об'єкту vunjatok
      System.out.println(Початкова помилка виконання:  + vunjatok.getCause());
    }
    catch(InputMismatchException vunjatok) { // якщо введені дані не числового типу
      System.out.println(Введені неправильні дані);
    }
  }
}
Можливий результат виконання:
***Ділення одного числа на інше***
Введіть значення діленого: 0
Введіть значення дільника: 0
Помилка виконання програми: java.lang.ArithmeticException: ділення на нуль
Початкова помилка виконання: java.io.IOException: помилка вводу даних

Програма вивела в консоль як виняток ділення на нуль, так і причину-виняток введеного з клавіатури нульового значення дільника, що й призвело до арифметичної помилки.

.