Both sides previous revisionPrevious revisionNext revision | Previous revision |
java:exceptions_try_catch_block [2017/02/06 14:17] – gthanos | java:exceptions_try_catch_block [2021/03/23 07:29] (current) – [Try with resources block] gthanos |
---|
</code> | </code> |
| |
Εντός του **try** block βάζουμε τον κώδικα που μπορεί να δημιουργηθεί μια εξαίρεση. Κάθε **catch** block ορίζει ένα διαφορετικό τύπο εξαίρεσης μέσα σε παρένθεση (''ExceptionTypeOne'', ''ExceptionTypeTwo'', κλπ). Εάν η εξαίρεση που παράγεται συμπίπτει __ως προς τον τύπο__ της με ένα αντικείμενο που ορίζεται εντός της παρενθέσεως ενός **catch** block, τότε αυτό το block θα εκτελεστεί. Θα εκτελεστεί επομένως ο κώδικας αντιστοιχεί στον τύπο δεδομένων ο οποίος παράχθηκε από την εκάστοτε εξαίρεση. | Εντός του **try** block βάζουμε τον κώδικα που μπορεί να δημιουργηθεί μια εξαίρεση. Κάθε **catch** block ορίζει ένα διαφορετικό τύπο εξαίρεσης μέσα σε παρένθεση (''ExceptionTypeOne'', ''ExceptionTypeTwo'', κλπ). Εάν η εξαίρεση που παράγεται συμπίπτει __ως προς τον τύπο__ της με ένα αντικείμενο που ορίζεται εντός της παρενθέσεως ενός **catch** block, τότε αυτό το block θα εκτελεστεί. Θα εκτελεστεί επομένως ο κώδικας αντιστοιχεί στον τύπο δεδομένων ο οποίος παράχθηκε από την εκάστοτε εξαίρεση. |
| |
| ==== Παράδειγμα ==== |
| |
==== Παράδειγμα 1ο - Διαίρεση με μηδέν (0) ==== | Στο προηγούμενο πρόγραμμα ας δούμε ποιοι τύποι εξαιρέσεων προκύπτουν με βάση τη λανθασμένη είσοδο του χρήστη. Στην περίπτωση που ο χρήστης εισάγει γράμματα αντί για ψηφία θα προκύψει μία εξαίρεση του τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/InputMismatchException.html|InputMismatchException]], ενώ στην περίπτωση που ο χρήστης πατήσει το συνδυασμό πλήκτρων CTRL+D θα προκύψει μία εξαίρεση του τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/NoSuchElementException.html|NoSuchElementException]]. |
| |
| Παρατηρήστε επίσης ότι η κλάση [[https://docs.oracle.com/javase/7/docs/api/java/util/InputMismatchException.html|InputMismatchException]] είναι απόγονος της κλάσης [[https://docs.oracle.com/javase/7/docs/api/java/util/NoSuchElementException.html|NoSuchElementException]]. |
| |
Ας δούμε ένα μικρό τμήμα κώδικα που εντοπίζει ένα είδος εξαίρεσης το οποίο συναντήσαμε και παραπάνω: | Παρακάτω δίνεται το προηγούμενο πρόγραμμα εμπλουτισμένο με κώδικα για τη διαχείριση των πιθανών εξαιρέσεων. |
| |
<code java TestDivideByZero.java> | <code java ExceptionHandling.java> |
import java.io.*; | |
import java.util.Scanner; | import java.util.Scanner; |
public class TestDivideByZero { | import java.util.NoSuchElementException; |
| import java.util.InputMismatchException; |
public static void main (String[] args) { | |
int x, y, result; | |
| |
Scanner input = new Scanner(System.in); | |
| |
System.out.print( "Enter first integer: " ); | public class ExceptionHandling { |
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"); | |
} | |
} | |
| |
} | |
} | |
</code> | |
| |
Όπως βλέπουμε έχουμε το προηγούμενο παράδειγμα, εμπλουτισμένο όμως με κώδικα διαχείρισης της διαίρεσης με το μηδέν. Ο κώδικας θα αναγνωρίσει ότι δημιουργείται ''ArithmeticException'' και συγκεκριμένα διαίρεση με το μηδέν (0) και θα εκτυπώσει τα σχετικά μηνύματα. | |
| |
==== Παράδειγμα 2ο - Ανοίγοντας ένα αρχείο για διάβασμα ==== | |
| |
Παρακάτω δίνεται ένα πιο σύνθετο παράδειγμα μία μεθόδου που διαβάζει ένα αρχείο κειμένου και το επιστρέφει στη μορφή ενός String. | |
| |
<code java WholeFileReader.java> | |
import java.io.*; | |
import java.lang.*; | |
| |
public class WholeFileReader { | |
| |
public String readFile(String path) { | public static void main(String []args) { |
| |
| Scanner sc = new java.util.Scanner(System.in); |
| |
try { | try { |
File file = new File (path); | System.out.print("Width: "); |
FileReader fReader = new FileReader(file); | int width = sc.nextInt(); |
BufferedReader in = new BufferedReader(fReader); | System.out.print("Height: "); |
String inputLine; | int height = sc.nextInt(); |
StringBuffer strDocument = new StringBuffer(); | double ratio = width / (double)height; |
while ((inputLine = in.readLine()) != null) { | System.out.format("Ratio: %.2f", ratio); |
strDocument.append(inputLine); | sc.close(); |
//throw new IOException(); | } catch(InputMismatchException ex) { |
} | System.out.println("Input doen not match integer value."); |
System.out.println("Closing File!"); | } catch(NoSuchElementException ex) { |
fReader.close(); | System.out.println("You have closed the input from command line."); |
return strDocument.toString(); | |
} | } |
catch(FileNotFoundException ex) { | |
System.out.println("The specified file was not found at "+ path); | |
return ""; | |
} | |
catch(IOException ex) { | |
System.out.println("IOException occured while reading from file "+path); | |
} | |
return "Nothing to return.."; | |
} | } |
| |
public static void main(String args[]) { | |
WholeFileReader wfr = new WholeFileReader(); | |
try { | |
System.out.println(wfr.readFile(args[0]) ); | |
} | |
catch(IndexOutOfBoundsException ex) { | |
System.out.println("No file has been specified from command line!\n"); | |
} | |
} | |
| |
} | } |
</code> | </code> |
| |
Παρατηρήστε ότι εμφανίζονται δύο **catch** blocks. Το πρώτο catch block αφορά [[http://docs.oracle.com/javase/7/docs/api/java/io/IOException.html|ΙΟException]] objects, ενώ το δεύτερο catch block αφορά [[http://docs.oracle.com/javase/7/docs/api/java/io/FileNotFoundException.html|FileNotFoundException]] objects. Από τους συνδέσμους που παρατίθενται θα παρατηρήσετε ότι το ''FileNotFoundException'' είναι υποκλάση του ''IOException''. Ο παραπάνω κώδικας θα μπορούσε να παραλείπει τις γραμμές | <WRAP tip 80% center round> |
| Η σειρά διαχείρισης των εξαιρέσεων έχει σημασία. Εφόσον, δημιουργηθεί μία εξαίρεση, το JVM ελέγχει εάν πρόκειται για εξαίρεση του πρώτου catch block, δηλαδή του τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/InputMismatchException.html|InputMismatchException]]. Εάν δεν ταιριάζει ο τύπος στο πρώτο catch block θα πάει στο επόμενο, δηλαδή θα ελέγξει εάν είναι εξαίρεση του τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/NoSuchElementException.html|NoSuchElementException]]. |
| |
<code java> | Εάν επιχειρήσετε να αντιμεταθέσετε τα δύο catch blocks αυτό που θα συμβεί είναι να λάβετε ένα μήνυμα λάθους από τον μεταγλωττιστή. Ο λόγος είναι ότι επειδή η κλάση InputMismatchException είναι υποκλάση της [[https://docs.oracle.com/javase/7/docs/api/java/util/NoSuchElementException.html|NoSuchElementException]], η αντιστροφή των catch block συνεπάγεται ότι ακόμη και εάν συμβεί μία εξαίρεση του τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/InputMismatchException.html|InputMismatchException]], το σχετικό catch block δεν θα κληθεί ποτέ. Ο λόγος είναι ότι το πρώτο catch block για τη διαχείριση των εξαιρέσεων του τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/NoSuchElementException.html|NoSuchElementException]] θα διαχειριστεί και τις εξαιρέσεις του υπο-τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/InputMismatchException.html|InputMismatchException]]. |
catch(FileNotFoundException ex) { | </WRAP> |
System.out.println("The specified file was not found at "+ path); | |
return ""; | |
} | |
</code> | |
| |
καθώς ο μη εντοπισμός αρχείου θα ενέπιπτε σε ''IOException'' που είναι η γονική κλάση του ''FileNotFoundException''. Σε αυτή την περίπτωση όμως όταν θα συνέβαινε κάποιο Exception δεν θα γνωρίζαμε εάν υπάρχει το αρχείο αλλά δεν μπορούμε να το διαβάσουμε ή δεν υπάρχει καθόλου το αρχείο που θέλουμε να διαβάσουμε στο filesystem. | |
| |
Επίσης, δεν θα είχε κανένα νόημα να βάλουμε τα catch blocks με ανάποδη σειρά δηλ. | ===== Finaly Block ===== |
<code java> | |
catch(IOException ex) { | |
System.out.println("IOException occured while opening file or reading from file "+path); | |
} | |
catch(FileNotFoundException ex) { | |
System.out.println("The specified file was not found at "+ path); | |
return ""; | |
} | |
</code> | |
| |
Σε αυτή την περίπτωση, εάν παραχθεί ένα Exception του τύπου ''FileNotFoundException'' θα διαχειριστεί από το πρώτο block που διαχειρίζεται τα ''IOExcetpions''. | Στον παραπάνω κώδικα, εμφανίζεται η κλήση της μεθόδου [[https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html#close()|Scanner.close()]] στο τέλος του try block η οποία κλείνει το αντικείμενο τύπου [[https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html|Scanner]]. |
| |
Τέλος, στο παραπάνω παράδειγμα βγάλτε τα σχόλια από την γραμμή ''throw new IOException();'' | |
<code java> | |
while ((inputLine = in.readLine()) != null) { | |
strDocument.append(inputLine); | |
//throw new IOException(); | |
} | |
</code> | |
για να δημιουργήσετε ένα ''IOException'' και παρατηρήστε από τα μηνύματα που εκτυπώνονται κατά την εκτέλεση του προγράμματος την πορεία του προγράμματος. | |
| |
===== Finaly Block ===== | |
| |
Εκτός από τα **catch** blocks τα οποία εκτελούνται όταν έχουμε κάποιο exception, μπορούμε να προσθέσουμε ένα **finaly block** το οποίο θα εκτελεστεί __σε κάθε περίπτωση__. Το **finaly block** θα εκτελεστεί στις παρακάτω περιπτώσεις: | Η κλήση της μεθόδου όμως συμβαίνει μόνο εάν δεν παραχθεί εξαίρεση. Στην περίπτωση που παραχθεί, το αντικείμενο θα παραμείνει ενεργό, πράγμα που σε ένα μεγαλύτερο πρόγραμμα δεν θα ήταν επιθυμητό (στο παρόν πρόγραμμα, το αντικείμενο θα κλείσει αμέσως μετά με την ολοκλήρωση του προγράμματος). Για το σκοπό αυτό εισάγεται η έννοια του finally block, ως ένα block που έπεται των catch blocks και σκοπό έχει να εκτελεστεί σε όλες τις περιπτώσεις, δηλαδή: |
* Εάν προκύψει η εξαίρεση που έχουμε φροντίσει να διαχειριστούμε (στο παρακάτω παράδειγμα //FileNotFoundException//). | * Εάν προκύψει η εξαίρεση που έχουμε φροντίσει να διαχειριστούμε (στο παρακάτω παράδειγμα //FileNotFoundException//). |
* Εάν προκύψει μια εξαίρεση ενός τύπου που δεν έχουμε φροντίσει να διαχειριστούμε. | * Εάν προκύψει μια εξαίρεση ενός τύπου που δεν έχουμε φροντίσει να διαχειριστούμε, εντός των υφιστάμενων catch blocks. |
* Εάν δεν προκύψει καμία απολύτως εξαίρεση. | * Εάν δεν προκύψει καμία απολύτως εξαίρεση. |
| |
Δείτε το παρακάτω παράδειγμα όπου ενσωματώνει ένα **finally** block. | Δείτε το προηγούμενο παράδειγμα, αλλαγμένο ώστε να ενσωματώνει ένα **finally** block. Στο παρακάτω πρόγραμμα το αντικείμενο sc θα κλείσει είτε συμβεί εξαίρεση, είτε όχι. |
<code java WholeFileReader.java> | |
import java.io.*; | |
import java.lang.*; | |
| |
public class WholeFileReader { | <code java ExceptionHandling.java> |
| import java.util.Scanner; |
| import java.util.NoSuchElementException; |
| import java.util.InputMismatchException; |
| |
| public class ExceptionHandling { |
| |
public String readFile(String path) { | public static void main(String []args) { |
| |
FileReader fReader = null; | Scanner sc = new java.util.Scanner(System.in); |
| |
try { | try { |
File file = new File (path); | System.out.print("Width: "); |
fReader = new FileReader(file); | int width = sc.nextInt(); |
BufferedReader in = new BufferedReader(fReader); | System.out.print("Height: "); |
String inputLine; | int height = sc.nextInt(); |
StringBuffer strDocument = new StringBuffer(); | double ratio = width / (double)height; |
while ((inputLine = in.readLine()) != null) { | System.out.format("Ratio: %.2f", ratio); |
strDocument.append(inputLine); | } catch(InputMismatchException ex) { |
//throw new IOException(); | System.out.println("Input doen not match integer value."); |
} | } catch(NoSuchElementException ex) { |
return strDocument.toString(); | System.out.println("You have closed the input from command line."); |
} | |
catch(FileNotFoundException ex) { | |
System.out.println("The specified file was not found!"); | |
return ""; | |
} | |
catch(IOException ex) { | |
System.out.println("IOException occured while reading from file "+path); | |
} | } |
finally { | finally { |
if(fReader!=null) { | sc.close(); |
try{ | |
System.out.println("Closing file!"); | |
fReader.close(); | |
} | |
catch(IOException ex) { | |
System.out.println("IOException occured while closing file "+path); | |
} | |
} | |
else { | |
System.out.println("File already closed!"); | |
} | |
| |
} | } |
return ""; | } |
} | } |
| </code> |
| |
| ===== Try with resources block ===== |
| |
| Η Java παρέχει τη δυνατότητα για αυτόματo κλείσιμο των αντικειμένων που οι κλάσεις τους υλοποιούν το interface [[https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html|AutoClosable]]. Οι κλάσεις που υλοποιούν το συγκεκριμένο interface συνδέονται με την διαχείριση πόρων όπως αρχεία, διάβασμα και γράψιμο στην κονσόλα, άνοιγμα υποδοχέων (sockets) ή άλλων πόρων για την επικοινωνία με απομακρυσμένα σημεία. |
| |
| Η κλάση [[https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html|Scanner]] υλοποιεί το συγκεκριμένο interface. Ας δούμε πως διαμορφώνεται ο προηγούμενος κώδικας εκμεταλλευόμενος την επιπλέον δυνατότητα του interface [[https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html|AutoClosable]]. |
| |
| <code java ExceptionHandling.java> |
| import java.util.Scanner; |
| import java.util.NoSuchElementException; |
| import java.util.InputMismatchException; |
| |
| public class ExceptionHandling { |
| |
public static void main(String args[]) { | public static void main(String []args) { |
WholeFileReader wfr = new WholeFileReader(); | |
try { | try (Scanner sc = new java.util.Scanner(System.in)){ |
System.out.println(wfr.readFile(args[0]) ); | System.out.print("Width: "); |
} | int width = sc.nextInt(); |
catch(IndexOutOfBoundsException ex) { | System.out.print("Height: "); |
System.out.println("No file has been specified from command line!\n"); | 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."); |
} | } |
} | } |
| |
} | } |
</code> | </code> |
| |
Ο λόγος που συνήθως χρησιμοποιήσουμε το **finally** block είναι για να συμπεριλάβουμε κώδικα που θέλουμε να εκτελεστεί σε όλες τις περιπτώσεις, όπως για παράδειγμα να κλείσουμε ελεγχόμενα τα αρχεία του προγράμματος ή να κλείσουμε δικτυακές συνδέσεις (π.χ. συνδέσεις με βάσεις δεδομένων κ.α.). Στο παραπάνω παράδειγμα παραλλάσσεται η μέθοδος ReadFile του προηγούμενου παραδείγματος, ώστε στο //finally// block η μέθοδος κλείνει το αρχείο που άνοιξε. Η διαφορά σε σχέση με το προηγούμενο παράδειγμα είναι ότι ακόμη και εάν δημιουργηθεί ένα exception την ώρα που διαβάζουμε, η ροή του προγράμματος θα περάσει από το //finally block// και το αρχείο τελικά θα κλείσει. Αυτό δεν ισχύει στο παράδειγμα που δώσαμε προηγούμενα. | Τα αντικείμενα που θέλουμε να κλείσουν αυτόματα τα δημιουργούμε μέσα σε παρενθέσεις, αμέσως μετά το try block. Η μεταβλητή sc έχει εμβέλεια εντός του try block και όχι έξω από αυτό. Με την ολοκλήρωση του try block, είτε συμβεί εξαίρεση, είτε δεν συμβεί, το αντικείμενο sc (τύπου Scanner) θα κλείσει αυτόματα. |
| |
Στο παραπάνω παράδειγμα δείτε την πορεία του κώδικα βγάζοντας τα σχόλια και βάζοντας σε σχόλια την γραμμή που δημιουργεί το ''IOException''. | |
<code java> | |
while ((inputLine = in.readLine()) != null) { | |
strDocument.append(inputLine); | |
//throw new IOException(); | |
} | |
</code> | |
| |
|Προηγούμενο: [[:java:exceptions_intro| Εξαιρέσεις ]] | Επόμενο: [[:java:exceptions_thrown_by_methods | Χειρισμός της εξαίρεσης σε υψηλότερο επίπεδο ]]| | |Προηγούμενο: [[:java:exceptions_intro| Εξαιρέσεις ]] | [[ :toc | Περιεχόμενα ]] | Επόμενο: [[:java:exceptions_thrown_by_methods | Χειρισμός της εξαίρεσης σε υψηλότερο επίπεδο ]]| |
| |
| |