User Tools

Site Tools


java:exceptions_try_catch_block

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

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

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 θα εκτελεστεί. Θα εκτελεστεί επομένως ο κώδικας αντιστοιχεί στον τύπο δεδομένων ο οποίος παράχθηκε από την εκάστοτε εξαίρεση.

Παράδειγμα

Στο προηγούμενο πρόγραμμα ας δούμε ποιοι τύποι εξαιρέσεων προκύπτουν με βάση τη λανθασμένη είσοδο του χρήστη. Στην περίπτωση που ο χρήστης εισάγει γράμματα αντί για ψηφία θα προκύψει μία εξαίρεση του τύπου InputMismatchException, ενώ στην περίπτωση που ο χρήστης πατήσει το συνδυασμό πλήκτρων CTRL+D θα προκύψει μία εξαίρεση του τύπου NoSuchElementException.

Παρατηρήστε επίσης ότι η κλάση InputMismatchException είναι απόγονος της κλάσης NoSuchElementException.

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

ExceptionHandling.java
import java.util.Scanner;
import java.util.NoSuchElementException;
import java.util.InputMismatchException;
 
public class ExceptionHandling {
 
  public static void main(String []args) {
 
    Scanner sc = new java.util.Scanner(System.in);
 
    try {
      System.out.print("Width: ");
      int width = sc.nextInt();
      System.out.print("Height: ");
      int height = sc.nextInt();
      double ratio = width / (double)height;
      System.out.format("Ratio: %.2f", ratio);
      sc.close();
    } catch(InputMismatchException ex) {
      System.out.println("Input doen not match integer value.");
    } catch(NoSuchElementException ex) {
      System.out.println("You have closed the input from command line.");
    }
  }
}

Η σειρά διαχείρισης των εξαιρέσεων έχει σημασία. Εφόσον, δημιουργηθεί μία εξαίρεση, το JVM ελέγχει εάν πρόκειται για εξαίρεση του πρώτου catch block, δηλαδή του τύπου InputMismatchException. Εάν δεν ταιριάζει ο τύπος στο πρώτο catch block θα πάει στο επόμενο, δηλαδή θα ελέγξει εάν είναι εξαίρεση του τύπου NoSuchElementException.

Εάν επιχειρήσετε να αντιμεταθέσετε τα δύο catch blocks αυτό που θα συμβεί είναι να λάβετε ένα μήνυμα λάθους από τον μεταγλωττιστή. Ο λόγος είναι ότι επειδή η κλάση InputMismatchException είναι υποκλάση της NoSuchElementException, η αντιστροφή των catch block συνεπάγεται ότι ακόμη και εάν συμβεί μία εξαίρεση του τύπου InputMismatchException, το σχετικό catch block δεν θα κληθεί ποτέ. Ο λόγος είναι ότι το πρώτο catch block για τη διαχείριση των εξαιρέσεων του τύπου NoSuchElementException θα διαχειριστεί και τις εξαιρέσεις του υπο-τύπου InputMismatchException.

Finaly Block

Στον παραπάνω κώδικα, εμφανίζεται η κλήση της μεθόδου Scanner.close() στο τέλος του try block η οποία κλείνει το αντικείμενο τύπου Scanner.

Η κλήση της μεθόδου όμως συμβαίνει μόνο εάν δεν παραχθεί εξαίρεση. Στην περίπτωση που παραχθεί, το αντικείμενο θα παραμείνει ενεργό, πράγμα που σε ένα μεγαλύτερο πρόγραμμα δεν θα ήταν επιθυμητό (στο παρόν πρόγραμμα, το αντικείμενο θα κλείσει αμέσως μετά με την ολοκλήρωση του προγράμματος). Για το σκοπό αυτό εισάγεται η έννοια του finally block, ως ένα block που έπεται των catch blocks και σκοπό έχει να εκτελεστεί σε όλες τις περιπτώσεις, δηλαδή:

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

Δείτε το προηγούμενο παράδειγμα, αλλαγμένο ώστε να ενσωματώνει ένα finally block. Στο παρακάτω πρόγραμμα το αντικείμενο sc θα κλείσει είτε συμβεί εξαίρεση, είτε όχι.

ExceptionHandling.java
import java.util.Scanner;
import java.util.NoSuchElementException;
import java.util.InputMismatchException;
 
public class ExceptionHandling {
 
  public static void main(String []args) {
 
    Scanner sc = new java.util.Scanner(System.in);
 
    try {
      System.out.print("Width: ");
      int width = sc.nextInt();
      System.out.print("Height: ");
      int height = sc.nextInt();
      double ratio = width / (double)height;
      System.out.format("Ratio: %.2f", ratio);
    } catch(InputMismatchException ex) {
      System.out.println("Input doen not match integer value.");
    } catch(NoSuchElementException ex) {
      System.out.println("You have closed the input from command line.");
    }
    finally {
      sc.close();
    }
  } 
}

Try with resources block

Η Java παρέχει τη δυνατότητα για αυτόματo κλείσιμο των αντικειμένων που οι κλάσεις τους υλοποιούν το interface AutoClosable. Οι κλάσεις που υλοποιούν το συγκεκριμένο interface συνδέονται με την διαχείριση πόρων όπως αρχεία, διάβασμα και γράψιμο στην κονσόλα, άνοιγμα υποδοχέων (sockets) ή άλλων πόρων για την επικοινωνία με απομακρυσμένα σημεία.

Η κλάση Scanner υλοποιεί το συγκεκριμένο interface. Ας δούμε πως διαμορφώνεται ο προηγούμενος κώδικας εκμεταλλευόμενος την επιπλέον δυνατότητα του interface AutoClosable.

ExceptionHandling.java
import java.util.Scanner;
import java.util.NoSuchElementException;
import java.util.InputMismatchException;
 
public class ExceptionHandling {
 
  public static void main(String []args) {
 
    try (Scanner sc = new java.util.Scanner(System.in)){
      System.out.print("Width: ");
      int width = sc.nextInt();
      System.out.print("Height: ");
      int height = sc.nextInt();
      double ratio = width / (double)height;
      System.out.format("Ratio: %.2f", ratio);
    } catch(InputMismatchException ex) {
      System.out.println("Input doen not match integer value.");
    } catch(NoSuchElementException ex) {
      System.out.println("You have closed the input from command line.");
    }
  }
}

Τα αντικείμενα που θέλουμε να κλείσουν αυτόματα τα δημιουργούμε μέσα σε παρενθέσεις, αμέσως μετά το try block. Η μεταβλητή sc έχει εμβέλεια εντός του try block και όχι έξω από αυτό. Με την ολοκλήρωση του try block, είτε συμβεί εξαίρεση, είτε δεν συμβεί, το αντικείμενο sc (τύπου Scanner) θα κλείσει αυτόματα.

java/exceptions_try_catch_block.txt · Last modified: 2021/03/23 07:29 by gthanos