Изключения(exceptions)

Грешки в една програма могат да бъдат открити :
        - по време на компилация или
        - по време на изпълнение

Езици като C обработват грешките като използват дума на състоянието, която трябва да бъде проверяване по време на изпълнението на програмата.

Този подход крие два риска:
        - да не се проверяват всички възможни грешки
        - да се обърне програмата в кошмар от проверки

В обектно-ориентираното програмиране е избран един друг подход - обработка на изключения. Изключенията сменят последователността на изпълнение на инструкциите, при наличие на неочаквано събитие, обикновено грешка. В този случай управлението се предава на друга част от програмата, която прави опит да реагира адекватно на грешката.

Предимства на подхода:
     - Не е необходима проверка на всички критични точки в програмата. Всичко, което трябва да се направи е да се опише възможната реакция на програмата на специално място на речено  « exception handler» По този начин може да се отдели кода на програмата от обработката на възможните проблеми и да се направи по - четлива.
    - Не е необходимо вземането на решение в текущия контекст. В този случай може да се генерира (throw) изключение и да се остави решението на друг контекст от програмата.

Видове грешки в Java

Throwable Class, ErrorException

Няколко понятия:
 

Действие

Понятие

Грешка по време на изпълнението на програмата
Exception
Генериране на изключение
Throwing
Прихващане на изключение в друга част от програмата
Catching
Програмния код за обработка на изключението
Catchblock
Последователността от ``call statement`` , която завършва с метода където е генерирано изключението
Stack trace

Няколко предефинирани изключения:

Exception

    ClassNotFoundException

    IllegalAccessException

    InterrupredException

    NoSuchMethodException

    RuntimeException

        ArithmeticException

        ArrayStoreException

        ClassCastException

        NegativeArraysizeException

        NullPointerException

        SecurityException

        IndexOutOfBoundsException

        String IndexOutOfBoundsException

        Array IndexOutOfBoundsException

        IllegalArgumentException

        NumberFormatException

        IllegalThreadStateException
 

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

В програмата могат да се въведат "проследявани" блокове:

try {
        //опасен проследяван код, който може да предизвика изключение
}

catch(type1 id1) { // може да има нула или повече "catch" блокове
        //обработва изключения от тип "type1" в проследявания блок
}
catch(type2 id2) {
        //обработва изключения от тип "type2"
}…

finally { //може да има нула или повече  "finally" блокове
        //изпълнява се винаги, независимо дали има изключение или не
}

Обработващите блокове ("catch blocs") трябва да се намират непосредствено след « try » блока

Правила

1. За всеки try блок може да има един или повече catch блокове, но само един finally блок.

2.  catch и  finally блокове могат да има само ако са свързани с  try блок.

3. Всеки try блок трябва да бъде последван или от най-малко един catch блок  или един finally блок.

4. Реда в 
catch блоковете трябва да бъде от най-специфичното към най-общото изключение.

Прекратяване или продължаване

Има два подхода в теорията на изключенията. Първият (възприет в Java) приема че изключенията представят сериозни грешки и изпълнението на програмата трябва да се прекрати. Следователно генерирането на изключение причинява обикновено прекратяването на засегнатия метод.

Вторият подход приема, че след обработката на изключението, може да се продължи изпълнението на метода ("resumption"). Вторият подход се реализира на  Java чрез подходящо избран « try-catch » блок обхващащ кода където се появява грешката и ако трябва всичко може да се сложи в « while » клауза.

Генериране на изключение(Throwing)

Изключенията се генерират автоматично при появила се грешка в програмата или могат да бъдат генерирани чрез инструкция.

Нека обекта 'q' да не е още инициализиран. Този факт може да се провери преди използването на обекта и обработката на ситуацията да се остави на друг контекст на програмата (създадено собствено изключение MyNullPointerException):

    if( q = = null)
        throw new MyNullPointerException();

Възможно е генерирането на изключение посредством конструктор с един аргумент(низ от символи):

    if(q == null)
        throw new MyNullPointerException("q = null");

Всички системни изключения имат по два конструктора - първият е подразбиращият се конструктор(без аргументи), а вторият е с един аргумент - низ от символи, който може да бъде анализиран в кода за обработка на изключението (exception handler ).

Когато в даден метод се генерира изключение се извършват следните действия :




Създаване на собствени изключения
Потребителят може да създава собствени изключения чрез наследяване на съществуващи. Създаваните изключения трябва да наследяват най-близкия по смисъл клас. Ако има колебание се използва директно класът  Exception.

Примери

 

Сумиране на две цели числа- без предвидено изключение
 
 
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sum extends JFrame {
    JTextField textField1,textField2,rez;
    JLabel l;
    int value1=0,value2=0,sum=0;
    Sum(){
        setLayout(new FlowLayout());
        textField1 = new JTextField(5);
        textField2 = new JTextField(5);
        textField1.addActionListener(new Enter());
        textField2.addActionListener(new Enter());
        l = new JLabel(" Type a number in each box!");
        add(l);
        rez= new JTextField(18);
        add(textField1);
        add(textField2);
        add(rez);
        textField1.setText("0");
        textField2.setText("0");
        setSize(230,150);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
    class Enter implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            value1= Integer.parseInt(textField1.getText());
            value2= Integer.parseInt(textField2.getText());
            rez.setText(value1+value2+"");
        }
    }
    public static void main(String arg[]){
        new Sum();
    }
}

 
 Сумиране на две цели числа - изключението е прихванато
  
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SumEx extends JFrame {
    JTextField textField1,textField2,rez;
    JLabel l;
    int value1=0,value2=0,sum=0;
    SumEx(){
        setLayout(new FlowLayout());
        textField1 = new JTextField(5);
        textField2 = new JTextField(5);
        textField1.addActionListener(new Enter());
        textField2.addActionListener(new Enter());
        l = new JLabel(" Type a number in each box!");
        add(l);
        rez= new JTextField(18);
        add(textField1);
        add(textField2);
        add(rez);
        textField1.setText("0");
        textField2.setText("0");
        setSize(230,150);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
    class Enter implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            String rz="";
            try{
                value1= Integer.parseInt(textField1.getText());
                value2= Integer.parseInt(textField2.getText());
                rz=value1+value2+"";
            }
            catch(NumberFormatException ex){
                rz="integers in each box please!";
            }
            finally{
                rez.setText(rz);
            }
        }
    }
    public static void main(String arg[]){
        new SumEx();
    }
}
 

Собствено изключение - прихванато във функцията където възниква

class NoNote extends Exception{
    String message;
    NoNote(String message){
        this.message = message;
        System.out.println(message);
    }
}
import java.util.*;
public class Exc3 {
    static Scanner sc=new Scanner(System.in);
    public static void main(String arg[]){
        System.out.println("Note: "+Note());
    }
    static int Note(){
        boolean ok;
        int note=200;
        do{
            ok = true;
            System.out.print("next note:");
            try{
                String s= sc.nextLine();
                note = Integer.parseInt(s);
                if((note>6)||(note <2)){
                    throw new NoNote("outside [2,6]");
                }
            }
            catch(NumberFormatException ie){
                System.out.println("Integer please!");
                ok=false;
                continue;
            }
            catch(NoNote ex){
                ok = false;
            }         

        }while(!ok);
        return note;
    }
}

Собствено изключение - прихванато извън функцията където възниква

class NoNote extends Exception{
    String message;
    NoNote(String message){
        this.message = message;
        System.out.println(message);
    }
}
import java.util.*;
public class Exc4 {
    static Scanner sc=new Scanner(System.in);
    public static void main(String arg[]){
        int note=0;       //initialization
        boolean ok;
        do{
            ok=true;
            try{
                note = Note();
            }       
            catch(NoNote ex){
                ok = false;
            }
            catch(NumberFormatException im){
                ok=false;
                System.out.println ("Integer please");
               
            }
        }while(!ok);
        System.out.println("Note: "+note);
    }

    static int Note() throws NoNote{
        int note;
        System.out.print("next note:");
        note = Integer.parseInt(sc.nextLine());
        
        if((note>6)||(note <2)){
            throw new NoNote("outside [2,6]");
        }
        return note;
    }
}

Друг пример:

 
class Bull extends Exception {
    public String s;
    Bull(String par) {
        s = par;
    }
}
enum Age {
    YOUNG, ADULT
}
enum Sex {
    MALE, FEMALE
}
enum Run {
    FAST, SLOW
}

class Animal {
    private Age age;
    private Sex sex;
    Animal(Age age, Sex sex) {
        this.age = age;
        this.sex = sex;
    }
    public Age age() {
        return age;
    }
    public Sex sex() {
        return sex;
    }
    public String toString() {
        return " animal: " + age + ", " + sex;
    }
}

class Herbivore extends Animal {
    public Run run;
    Herbivore(Age age, Sex sex, Run run) {
        super(age, sex);
        this.run = run;
    }
    public String toString() {
        return super.toString() + ",herbivore:" + run;
    }
}

class Carnivore extends Animal {
    private boolean starving;
    Carnivore(Age age, Sex sex) {
        super(age, sex);
        starving = true;
    }
    public Herbivore[] tear(Herbivore[] herd) throws Bull,
    ArrayIndexOutOfBoundsException {
        if (starving) {
            if (herd[herd.length - 1].age() == Age.ADULT &&
            herd[herd.length - 1].sex() == Sex.MALE)
                throw new Bull("BULL FOUND!!");
            Herbivore[] buffer = new Herbivore[herd.length - 1];
            System.arraycopy(herd, 0, buffer, 0, herd.length - 1);
            starving = false;
            return buffer;
        }
        return herd;
    }
    public void sleep() {
        starving = true;
    }
    public String toString() {
        return super.toString() + " wolf , starving:" + starving;
    }
}

public class MyException {
    public static void prt(Animal[] animal, Animal wolf) {
        System.out.println("\n\n\n animals: \n");
        for (int i = 0; i < animal.length; i++)
            System.out.println("" + animal[i]);
        System.out.println("\n" + wolf);
    }
    public static void main(String[] arg) {
        Herbivore[] cows = new Herbivore[(int) (Math.random() * 10)];
        for (int i = 0; i < cows.length; i++) {
            cows[i] = new Herbivore(
                    Math.random() > 0.5 ? Age.YOUNG : Age.ADULT,
                    Math.random() > 0.5 ? Sex.MALE : Sex.FEMALE,
                    Math.random() > 0.5 ? Run.FAST : Run.SLOW);
        }
        Carnivore wolf = new Carnivore(Age.ADULT, Sex.MALE);
        prt(cows, wolf);
        for (;;) {
            try {
                cows = wolf.tear(cows);
            }
            catch (Bull e) {
                System.out.println(e.s);
                System.out.println("\nEnd of the program");
                System.exit(1);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("NO MORE COWS!");
                System.out.println("\nEnd of the program");
                System.exit(1);
            }
            wolf.sleep();
            prt(cows, wolf);
        }
    }
}
 animals:

 animal: YOUNG, MALE,herbivore:FAST
 animal: YOUNG, FEMALE,herbivore:SLOW
 animal: YOUNG, MALE,herbivore:FAST
 animal: YOUNG, MALE,herbivore:SLOW
 animal: ADULT, MALE,herbivore:FAST
 animal: ADULT, MALE,herbivore:FAST
 animal: ADULT, MALE,herbivore:FAST
 animal: ADULT, FEMALE,herbivore:SLOW

 animal: ADULT, MALE wolf , starving:true



 animals:

 animal: YOUNG, MALE,herbivore:FAST
 animal: YOUNG, FEMALE,herbivore:SLOW
 animal: YOUNG, MALE,herbivore:FAST
 animal: YOUNG, MALE,herbivore:SLOW
 animal: ADULT, MALE,herbivore:FAST
 animal: ADULT, MALE,herbivore:FAST
 animal: ADULT, MALE,herbivore:FAST

 animal: ADULT, MALE wolf , starving:true
BULL FOUND!!

End of the program

_____________________________

   animals:

 animal: ADULT, FEMALE,herbivore:SLOW

 animal: ADULT, MALE wolf , starving:true



 animals:


 animal: ADULT, MALE wolf , starving:true
NO MORE COWS!

End of the program

Илюстриране на "finally clause"
 
 
class MyException extends Exception {}

public class Fn {
  static int count = -2;
  public static void main(String[] args) {
    while(true) {
      try {
        // Post-increment ( begin with zero):
        if(++count  == 0)
          throw new MyException();
        System.out.println("count = "+count+" No exception");
      } catch(MyException e) {
               System.out.print("count = "+count+" ");
               System.out.println(e);
               break;      // out of "while
      } finally {
            System.out.println("\"finally clause\" executing");
      }
    }
  }
}


 

count = -1 No exception
"finally clause" executing
count = 0 MyException
"finally clause" executing




Задачи:

  1. В програмата за събиране на две цели числа да се въведат два бутона - + ( за събиране) и / ( за делене). Да се хване изключението делене на нула.
  2. В предната задача при възникване на изключение да се указва в кое поле е проблемът.
  3. В програмата със стадото да се въведе изключение NotCatch, което да възниква ако последното животно в стадото е fast и adult. Изключението да се обявява, след което да прекъсва програмата.
  4. Изключението от горния пример да предизвиква промяна на тактиката на хищника - да атакува първото, а не последното животно. Ако и тогава не може да го хване програмата да се прекратява.