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

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

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

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

![](https://3165743208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MUbVVR-jiMUx7iVRyw6%2Fuploads%2FUyHemP2jxWGjq8bDI8gA%2Fimage.png?alt=media\&token=aff31d08-ebb1-4baf-934b-265842a0472b)

Изключенията и грешките се разделят на два основни вида проверени (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 *(*&#x53;omeException objec&#x74;*){*

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*

&#x20;       *at bg.tu\_varna.sit.examples.contr.Example.main(Example.java:7)*

*This will printing ever.*

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

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

В дадената програма се опитваме да достъпим поле извън рамките на масива. Това изключение е прихванато в 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...");
    }
}
```
