Обработка на изключения

Изключения и грешки

В Java всички изключения се представят в класове, наследници на родителския клас на всички класове – Object. От своя страна класът Throwable e родител на всички изключения. Изключение(Exception) е събитие, което настъпва по време на изпълнение на програмата и затруднява нейното нормално завършване.

Класът Error, е наследник на класа Throwable. Грешките от този клас са критични за изпълнението на програмата и при тяхната поява програмата не може да бъде възстановена и трябва принудително да завърши. Грешките от този клас се „хвърлят” от runtime system и обикновено са следствие от някакво некоректно условие, което се е появило по време на изпълнение на програмата.

Изключенията и грешките се разделят на два основни вида проверени (checked), възникващи по време на компилация и непроверени (unchecked)- възникващи по време на изпълнение.

Checked (проверени) са изключения, които задължително трябва да спазват принципа "хвани или изхвърли" и това се гарантира от компила­тора. Тези изключения наследяват класа java.lang.Exception, но не наследяват java.lang.RuntimeException.

Checked са изключения, които една добре написана програма трябва да очаква и би трябвало да може да се възстанови от тях. Ако се опитваме да четем от файл, който не съществува програмата ще поучи IOException, тази грешка може да се обработи и да се съобщи на потребителя, че няма подобен файл

Unchecked (непроверени) изключения, са изключения, които не са задъл­жени да спазват принципа "хвани или изхвърли". Тези изключения наследяват класа RuntimeException. Възникването на такова изключение най-често означава бъг в програмата или неправилна употреба на някоя библиотека.

Грешката NullPointerException е типичен представител на unchecked изключенията. Може да възникне по невнимание, когато се обърнем към обект, който няма стойност. Прихва­щането и обработването на такива проблеми не е задължително, но е възможно.

Throwable – методи

Ето най-основните методи на изключенията (класът Throwable):

- Методът getMessage() връща текстово описание на изключението. Всяко изключение само решава какво съобщение да върне. Най-често се позволява на хвърлящият изключението да сложи това описание.

- Методът getCause() връща вътрешното / обвитото изключение.

- Методът getStackTrace() връща целия стек, който се пази в изключението. Съществува от Java версия 1.4.

- Методът initCause() метод използван преди Java 1.4 за задаване на вътреш­ното / обвитото изключение. Сега се използват конструкто­рите, но този метод също продължава да работи.

- Методът printStackTrace() отпечатва класът на изключението, съобщението и стека на грешката и стека на цялата верига от извикани методи, заедно с цялата верига на вложени едно в друго изключения.

Прихващане на изключение

Когато се налага да прихванем дадено изключение, кодът генериращ това изключение трябва да бъде поставен в блок ограден с фигурни скоби след думата try.

След това използваме един или повече catch блока, където кодът в този блок се изпълнява при прихванато изключение от този клас.

try{

some code

}

catch (SomeException object){

some code to catch

}

public class Example {
    public static void main(String[] args) {
        int[] array = new int[2];
        try {
            System.out.println(array[5]); 
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("This will printing ever.");
        }
    }
}

Изход:

java.lang.ArrayIndexOutOfBoundsException: 5

at bg.tu_varna.sit.examples.contr.Example.main(Example.java:7)

This will printing ever.

Кода от който очакваме да се появи дадено изключение, се поставя в try блока.

Важно е да се спомене, че последователността на прихващане на изключенията е от изключителна важност, поради техния обвиващ характер. Това означава, че прихващането на изключенията трябва да става от по-конкретен към по-общ клас(от наследник към родител). Ако дадено изключение не бъде прихванато, но е прихванато по-общо изключение от него, то по-частното ще се прихване от блока на по-общото. Ако им разменим местата, то ще настъпи компилационна грешка.

В дадената програма се опитваме да достъпим поле извън рамките на масива. Това изключение е прихванато в try блока и обработено в catch блока. Ключовата дума finally гарантира, че блокът след нея ще се изпълни независимо от това дали ще бъде прихванато изключение или не.

Хвърляне на изключения

Когато в даден метод се появи изключение, методът може или да обработи даденото изключение или да го „хвърли” на викащия го метод. Това означава, че изключението ще бъде обработено във викащия метод. Изключенията се „ хвърлят” чрез ключовата дума throws.

public class Example {
public static void main(String[] args) {
    try {
        division(100, 0);
    } catch (ArithmeticException e) {
        System.out.println("You can’t divide by zero!");
    }
}
public static void division(int a, int b) throws ArithmeticException {
    int c = a / b;
    System.out.println(c);
}
}

В програмата главният метод main извиква метода devision, който сме декларирали, че хвърля изключение от тип ArithmeticException. От това, че throws присъства в декларацията на метода, следва че, където и да го извикаме ще трябва да бъде прихванато и обработено или отново изхвърлено даденото изключение. Когато декларираме даден метод и има вероятност той да предизвика множество изключения, то те се декларират, изброени със запетая след throws.

В определени ситуации е необходимо програмиста сам да реши кога да хвърли дадено изключение. Това е възможно чрез ключовата дума throw. Метода отново е необходимо да показва, че хвърля изключение от определен тип. Ако пренапишем горния пример:

public class Example {
    public static void main(String[] args) {
        try {
            division(100, 0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
    }

    public static void division(int a, int b) throws ArithmeticException {
        if (b == 0) {
            throw new ArithmeticException();
        } else {
            int c = a / b;
            System.out.println(c);
        }
    }
} 

Персонализирани изключения:

В Java можем да създаваме собствени изключения, които наследяват Exception класа. Тези изключения се наричат персонализирани(собствени, custom) изключения, те се създават за да отговарят на изискванията на потребителя. Кога е необходимо да създаваме собствени изключения:

- Когато искаме да предоставим по-специфична обработка на вече съществуващи изключения в Java.

- Когато дефинираме изключения , които отговарят на бизнес логиката и работния процес, на проблема който решаваме.

За да можем да създадем персонализирани изключения, класа трябва да наследява Exception класа. В този пример създаваме нова грешка InvalidAgeException, която се генерира, когато потребител е на възраст по-малка от 18 години.

class InvalidAgeException extends Exception {
    public InvalidAgeException(String str) {
        super(str);
    }
}

public class Example {
    static void validate(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException("age is not valid to vote");
        } else {
            System.out.println("welcome to vote");
        }
    }

    public static void main(String args[]) {
        try {
            validate(13);
        } catch (InvalidAgeException ex) {
            System.out.println("Caught the exception");
            System.out.println("Exception occured: " + ex);
        }
        System.out.println("rest of the code...");
    }
}

Last updated