java:exceptions_try_catch_block2

Διαχείριση Εξαιρέσεων

Στην προηγούμενη υποενότητα αναφερθήκαμε συνοπτικά στο αντικείμενο της εξαίρεσης. Εδώ θα δόυμε τον τρόπο με τον οποίο διαχειριζόμαστε τις εξαιρέσεις.

Try-Catch Block

Κάθε τμήμα του κώδικα που υπάρχει περίπτωση να πυροδοτήσει μια εξαίρεση θα πρέπει να την εμπερικλείουμε σε ένα try block. Το block αυτό του κώδικα θα περιγράφει τον τρόπο αντιμετώπισης της κάθε πιθανής εξαίρεσης που μπορεί να προκύψει. Ο ορισμός του block είναι ο εξής:

try{
 
   /* 
    * code that may throw an exception here.
    */
 
}catch (ExceptionTypeOne ex) {
 
   /* 
    * exception handler for ExceptionTypeOne objects.
    */
 
}catch (ExceptionTypeTwo ex) {
 
   /* 
    * exception handler for ExceptionTypeOne objects.
    */
 
}

Εντός του try block βάζουμε τον κώδικα που μπορεί να δημιουργηθεί μια εξαίρεση. Κάθε catch block ορίζει ένα διαφορετικό τύπο εξαίρεσης μέσα σε παρένθεση (ExceptionTypeOne, ExceptionTypeTwo, κλπ). Εάν η εξαίρεση που παράγεται συμπίπτει ως προς τον τύπο της με ένα αντικείμενο που ορίζεται εντός της παρενθέσεως ενός catch block, τότε αυτό το block θα εκτελεστεί. Θα εκτελεστεί επομένως ο κώδικας αντιστοιχεί στον τύπο δεδομένων ο οποίος παράχθηκε από την εκάστοτε εξαίρεση.

Στο προηγούμενο παράδειγμα, ας προσθέσουμε ένα τμήμα κώδικα που εντοπίζει το είδος εξαίρεσης το οποίο συναντήσαμε παραπάνω:

TestDivideByZero.java
import java.io.*;
import java.util.Scanner;
public class TestDivideByZero {
 
   public static void main (String[] args) {
      int x, y, result;
 
      Scanner input = new Scanner(System.in);
 
      System.out.print( "Enter first integer: " );
      x = input.nextInt();
 
      System.out.print( "Enter second integer: " );
      y = input.nextInt();
 
      try {
           result = x/y;
           System.out.printf( "Product is %d\n", result );
      } catch (ArithmeticException ae) {
           System.out.println("ArithmeticException occured!");
           if(y == 0){
              System.out.println("Division by zero in particular");
           }
      }
 
   }
}

Όπως βλέπουμε έχουμε το προηγούμενο παράδειγμα, εμπλουτισμένο όμως με κώδικα διαχείρισης της εξαίρεση που παράγεται από τη διαίρεση ενός ακεραίου με το μηδέν. Ο κώδικας θα αναγνωρίσει ότι υπάρχει δημιουργείται ArithmeticException και συγκεκριμένα διαίρεση με το μηδέν (0) και θα εκτυπώσει τα σχετικά μηνύματα.

Άλλο παράδειγμα - Προσθέτοντας τους αριθμούς double που βρίσκονται μέσα σε ένα αρχείο κειμένου

Παρακάτω δίνεται ένα πιο σύνθετο παράδειγμα μία μεθόδου που ανοίγει ένα αρχείο κειμένου για διάβασμα, το οποίο περιέχει μόνο αριθμούς κινητής υποδιαστολής και επιστρέφει το άθροισμα των αριθμών αυτών.

MyFileReader.java
import java.io.*;
import java.util.*;
 
public class MyFileReader {
 
  public static double sumFileContents(String path) {
 
    try {
      File file = new File (path);
      Scanner sc = new Scanner(file);
      double sum = 0.0;
      while(sc.hasNextDouble())
        sum += sc.nextDouble();
      System.out.println("Closing File!");
      sc.close();
      return sum;
    }
    catch(FileNotFoundException ex) {
      System.out.println("The specified file was not found at "+ path);
    }
    catch(NoSuchElementException ex) {
      System.out.println("The specified type of element was not found!");
    }
    return 0.0;
  }
 
  public static void main(String args[]) {
    try {
      double result = sumFileContents(args[0]);
      System.out.println("Result is: "+result);
    }
    catch(IndexOutOfBoundsException ex) {
      System.out.println("No file has been specified from command line!\n");
    }
  }
 
}

Παρατηρήστε ότι στη μέθοδο sumFileContents εμφανίζονται δύο catch blocks. Το πρώτο catch block αφορά FileNotFoundException objects, ενώ το δεύτερο catch block αφορά NoSuchElementException objects.

Επεξήγηση της συνάρτησης main

Η συνάρτηση main διαβάζει ένα όνομα αρχείου από τη γραμμή εντολών και προσπαθεί να το ανοίξει για διάβασμα. Η γραμμή εντολών μας δίνει τη δυνατότητα να προσθέσουμε σε ένα πρόγραμμα παραμέτρους κατά την εκτέλεση του. Στο συγκεκριμένο παράδειγμα δίνουμε στο πρόγραμμα το όνομα ενός αρχείου από το οποίο θα διαβάσει.

Ενδεικτικό περιεχόμενο για το αρχείο input.txt που δίνεται ως όρισμα είναι το παρακάτω

8.23
1.31
2.85
9.63
7.853

Στη μέθοδο main εμφανίζεται ένα catch block για μία εξαίρεση του τύπου IndexOutOfBoundsException. Η συγκεκριμένη εξαίρεση παράγεται όταν προσπελάζεται μία θέση εκτός ορίων οποιουδήποτε πίνακα.

Στο συγκεκριμένο παράδειγμα, εάν ο χρήστης παραλείψει να προσθέσει μία παράμετρο στη γραμμή εντολών κατά την εκτέλεση, ο πίνακας args θα έχει μέγεθος 0 και η προσπέλαση args[0] θα είναι εκτός ορίων του πίνακα.

Πέρασμα παραμέτρων μέσω της γραμμής εντολών στο NetBeans

Το πέρασμα παραμέτρων μέσα από τη γραμμή εντολών στο NetBeans γίνεται ως εξής:

  • Επιλέγεται File → Project Properties.
  • Σας ανοίγει το παράθυρο που φαίνεται παρακάτω.
  • Επιλέγετε Run στο παράθυρο αυτό και συμπληρώνετε τα ορίσματα της γραμμής εντολών στο κουτάκι Arguments (είναι μαρκαρισμένο κόκκινο).

Finally Block

Εκτός από τα catch blocks τα οποία εκτελούνται όταν έχουμε κάποιο exception, μπορούμε να προσθέσουμε ένα finaly block το οποίο θα εκτελεστεί σε κάθε περίπτωση. Το finaly block θα εκτελεστεί στις παρακάτω περιπτώσεις:

  • Εάν προκύψει η εξαίρεση που έχουμε φροντίσει να διαχειριστούμε (στο παρακάτω παράδειγμα FileNotFoundException).
  • Εάν προκύψει μια εξαίρεση ενός τύπου που δεν έχουμε φροντίσει να διαχειριστούμε.
  • Εάν δεν προκύψει καμία απολύτως εξαίρεση.

Δείτε το παρακάτω παράδειγμα όπου ενσωματώνει ένα finally block.

MyFileReaderUsingFinallyBlock.java
import java.io.*;
import java.util.*;
 
public class MyFileReaderUsingFinallyBlock {
 
  public static double sumFileContents(String path) {
 
    Scanner sc=null;
    try {
      File file = new File (path);
      sc = new Scanner(file);
      double sum = 0.0;
      while(sc.hasNextDouble())
        sum += sc.nextDouble();
    }
    catch(FileNotFoundException ex) {
      System.out.println("The specified file was not found at "+ path);
    }
    catch(NoSuchElementException ex) {
      System.out.println("The specified type of element was not found!");
    }
    finally {
      System.out.println("Closing File!");
      if(sc!=null)
        sc.close();
    }
    return 0.0;
  }
 
  public static void main(String args[]) {
    try {
      double result = sumFileContents(args[0]);
      System.out.println("Result is: "+result);
    }
    catch(IndexOutOfBoundsException ex) {
      System.out.println("No file has been specified from command line!\n");
    }
  }
 
}

Ο λόγος που συνήθως χρησιμοποιήσουμε το finally block είναι για να συμπεριλάβουμε κώδικα που θέλουμε να εκτελεστεί σε όλες τις περιπτώσεις, όπως για παράδειγμα να κλείσουμε ελεγχόμενα τα αρχεία του προγράμματος ή να κλείσουμε δικτυακές συνδέσεις (π.χ. συνδέσεις με βάσεις δεδομένων κ.α.). Στο παραπάνω παράδειγμα παραλλάσσεται η μέθοδος sumFileContents του προηγούμενου παραδείγματος, ώστε στο finally block η μέθοδος κλείνει το αντικείμενο τύπου Scanner που άνοιξε. Η διαφορά σε σχέση με το προηγούμενο παράδειγμα είναι ότι ακόμη και εάν δημιουργηθεί ένα exception την ώρα που διαβάζουμε, η ροή του προγράμματος θα περάσει από το finally block και το αρχείο τελικά θα κλείσει. Αυτό δεν ισχύει στο παράδειγμα που δώσαμε προηγούμενα.

Στο παραπάνω παράδειγμα δείτε την πορεία του κώδικα εάν το αρχείο από το οποίο διαβάζετε δεν περιέχει αριθμό αλλά μία λέξη.

java/exceptions_try_catch_block2.txt · Last modified: 2019/04/20 04:41 by gthanos