C, PHP, VB, .NET

Дневникът на Филип Петров


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

Публикувано на 24 септември 2009 в раздел ПИК3 Java.

Понякога е удачно да пишем методи, които вместо да обработват сами изключенията на операторите вътре в тях, да прехвърлят изключенията на извикващите ги методи. Това е нещо като "прехвърляне на отговорността". Такава функционалност се получава чрез оператора "throw". Ето един тривиален пример - метод, който отпечатва съдържанието на файл на екрана:

  public static void showFileContents(String filename)
         throws java.io.FileNotFoundException, java.io.IOException {

    java.io.BufferedReader in =
            new java.io.BufferedReader(
                        new java.io.FileReader(filename));

    int readbyte;
    while ((readbyte = in.read()) != -1) {
        System.out.print((char)readbyte);
    }
    in.close();
  }

  public static void main(String[] args) {
    try{
      showFileContents("input.txt");
    }
    catch (java.io.FileNotFoundException e){
      System.err.println("File not found");
    }
    catch (java.io.IOException e){
      System.err.println("IO error has occured");
    }
  }

Виждате, че в метод showFileContents ние никъде не се погрижихме за търсене на изключения. Ако методите използвани от BufferedReader достигнат до FileNotFoundException или IOException - те просто ги прехвърлят към метода main.

Изключенията обаче могат да бъдат прехвърляни и през няколко нива. Например:

  public static java.io.BufferedReader openFile(String filename)
         throws java.io.FileNotFoundException, java.io.IOException{

    java.io.BufferedReader in =
            new java.io.BufferedReader(
                        new java.io.FileReader(filename));

    return in;
  }

  public static void showFileContents(String filename)
         throws java.io.IOException {

    java.io.BufferedReader in = null;
    try{
       in = openFile(filename);

      int readbyte;
      while ((readbyte = in.read()) != -1) {
        System.out.print((char)readbyte);
      }
    }
    catch(java.io.FileNotFoundException e){
      System.err.println("File Not Found");
    }
    finally{
      try{
        if (in!=null) in.close();
      }
      catch (java.io.IOException e){}
    }
  }

  public static void main(String[] args) {
    try{
      showFileContents("input.txt");
    }
    catch (java.io.IOException e){
      System.err.println("IO error has occured");
    }
  }

От този пример виждаме, че ако в метод openFile възникне някаква грешка с отварянето на файл, то тя ще бъде прехвърлена към извикващия метод - showFileContents. Той от своя страна се грижи за обработката на FileNotFoundException, но не и за IOException. Така ако се получи IOЕxception (независимо дали в метод openFile или showFileContents) тя ще бъде хвърлена към метода main, който вече е длъжен да я обработи. Така получихме т.нар. "каскадно" обработване на грешка.

Предвидена е и още една възможност - самите ние да "хвърлим" изключение към викащия метод. Нека напишем метод, който получава като аргумент масив и число и дели всички елементи на масива на това число. При подадено число 0, то методът ще хвърли ArithmeticException:

  public static void divArray(int[] arr, int divisor)
         throws java.lang.ArithmeticException{

    if (divisor == 0) throw new java.lang.ArithmeticException("You divide by 0");

    for (int i=0; i<arr.length; i++){
      arr[i] /= divisor;
    }
  }

  public static void main(String[] args) {
    int[] arr = {1,3,6,0,9};
    try{
      divArray(arr, 0);
    }
    catch (java.lang.ArithmeticException e){
      System.err.println(e.getMessage());
      System.err.println("We will only print the original array:");
    }
    for (int i: arr){
      System.out.print(i+" ");
    }
  }

Специално сме удебелили една важна функционалност - методът "getMessage()". Виждате, че при създаването на изключението ние подадохме като параметър текст "You divide by 0". Когато прихващаме това изключение в метода main, то чрез методът "getMessage()" ние взимаме именно този текст и в случая го отпечатваме в стандартния поток за грешки. Изключенията имат такъв текст и по подразбиране, но по този начин е ясно, че можем да сме по-гъвкави.

След като знаем за тази функционалност, нека се върнем към "лошия пример" от предишна статия и да покажем как можем да я използваме:

    // Izchisliava x ot a.x + b = 0
    double a = 0;
    double b = 4;
    try{
      if (a == 0){
        if (b == 0) throw new java.lang.ArithmeticException("Vsiako x e reshenie");
        else throw new java.lang.ArithmeticException("Niama reshenie");
      }
      System.out.println("x = "+(-b/a));
    }
    catch (java.lang.ArithmeticException e){
      System.out.println(e.getMessage());
    }

Естествено пак ще повторим казаното в предишната статия, че работата с изключения трябва максимално да се отбягва. Подобни примери могат да доведат само до загуба на производителност и нищо полезно. Затова използвайте и хвърляйте изключения само когато е необходимо! С този пример просто демонстрираме възможностите на throw и нищо друго.

 



Добави коментар

Адресът на електронната поща няма да се публикува


*