Перехоплення винятків із використанням try та catch
Виняток — це помилка виконання програми.
Винятки виникають при:
- неправильно написаному програмному коді;
- виконанні неприпустимих арифметичних операцій (наприклад, при діленні на нуль);
- неналежному використанні порожнього посилання;
- виході елементу поза межі массиву;
- переповненні пам'яті та ін.
Відслідковування та перехоплення винятків
Інструкція try для відслідковування винятків
Щоб виділити код, який
потрібно відслідковувати на виникнення винятків, використовують ключове
слово try. Після слова try пишеться блок, в якому розміщується
програмний код, де можливе виникнення винятку(помилки виконання):
try {
програмний_код
}
Інструкція catch для перехоплення винятків
Після інструкції try обов'язково має бути
інструкція перехоплення винятків, яка позначається ключовим словом catch. Після слова catch в дужках (параметри)
вказується перехоплений об'єкт винятку, який був створений у блоці try. Після параметрів пишеться блок
коду, який має виконатися при перехопленні винятку:
try { /* всередині цього блоку пишеться програмний код, який буде
відслідковуватися на
виникнення винятків */
програмний_код
}
catch (об'єкт_винятку) { /* всередині цього блоку пишеться код, який має виконатися при перехоленні вказаного в
параметрах об'єкту винятку */
програмний_ код
}
Об'єкти винятків
Усі класи винятків є
спадкоємцями класу Trowable,
що знаходиться в пакеті java.lang.
Від класу Trowable безпосередньо
походять два класи:
- клас Error містить об'єкти винятків, які описують помилки, що сталися всередині самого середовища виконання. Це такі помилки як переповнення стеку, нестача пам'яті комп'ютера для виконання програми, помилки всередині віртуальної машини. Ці винятки не мають безпосереднього відношення до коду програми, тому такими винятками програмісти користуються не часто.
- клас Exception містить винятки, які описують помилки всередині програми, яку пише програміст. Саме цими винятками часто користуються програмісти. Ось деякі підкласи похідного від Exception класу RuntimeException:
-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: помилка вводу даних
Програма вивела в консоль
як виняток ділення на нуль, так і причину-виняток введеного з клавіатури
нульового значення дільника, що й призвело до арифметичної помилки.