Προγραμματισμός

Εξαιρέσεις στην Java, Μέρος 1: Βασικά στοιχεία χειρισμού εξαιρέσεων

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

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

Σημειώστε ότι τα παραδείγματα κώδικα σε αυτό το σεμινάριο είναι συμβατά με το JDK 12.

λήψη Λήψη του κώδικα Λήψη του πηγαίου κώδικα για παράδειγμα εφαρμογές σε αυτό το σεμινάριο. Δημιουργήθηκε από τον Jeff Friesen για το JavaWorld.

Τι είναι οι εξαιρέσεις Java;

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

Ελεγμένες εξαιρέσεις

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

Χειριστές εξαίρεσης

Ενα χειριστής εξαιρέσεων είναι μια ακολουθία κώδικα που χειρίζεται μια εξαίρεση. Ανακρίνει το πλαίσιο - που σημαίνει ότι διαβάζει τιμές που έχουν αποθηκευτεί από μεταβλητές που βρίσκονταν στο πεδίο εφαρμογής τη στιγμή που προέκυψε η εξαίρεση - στη συνέχεια χρησιμοποιεί αυτό που μαθαίνει να επαναφέρει το πρόγραμμα Java σε μια ροή φυσιολογικής συμπεριφοράς. Για παράδειγμα, ένας χειριστής εξαιρέσεων μπορεί να διαβάσει ένα αποθηκευμένο όνομα αρχείου και να κάνει το χρήστη να αντικαταστήσει το αρχείο που λείπει.

Εξαιρέσεις χρόνου εκτέλεσης (μη επιλεγμένες)

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

Σχετικά με τις εξαιρέσεις χρόνου εκτέλεσης

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

Σφάλματα

Ορισμένες εξαιρέσεις είναι πολύ σοβαρές επειδή θέτουν σε κίνδυνο την ικανότητα ενός προγράμματος να συνεχίσει την εκτέλεση. Για παράδειγμα, ένα πρόγραμμα προσπαθεί να εκχωρήσει μνήμη από το JVM, αλλά δεν υπάρχει αρκετή ελεύθερη μνήμη για να ικανοποιήσει το αίτημα. Μια άλλη σοβαρή κατάσταση εμφανίζεται όταν ένα πρόγραμμα προσπαθεί να φορτώσει ένα classfile μέσω ενός Class.forName () μέθοδος κλήσης, αλλά το classfile είναι κατεστραμμένο. Αυτό το είδος εξαίρεσης είναι γνωστό ως λάθος. Δεν πρέπει ποτέ να προσπαθείτε να χειριστείτε τα λάθη μόνοι σας επειδή το JVM ενδέχεται να μην είναι σε θέση να ανακτήσει από αυτό.

Εξαιρέσεις στον πηγαίο κώδικα

Μια εξαίρεση μπορεί να αντιπροσωπεύεται στον πηγαίο κώδικα ως κωδικός λάθους ή ως αντικείμενο. Θα σας παρουσιάσω και τα δύο και θα σας δείξω γιατί τα αντικείμενα είναι ανώτερα.

Κωδικοί σφάλματος έναντι αντικειμένων

Οι γλώσσες προγραμματισμού όπως το C χρησιμοποιούν ακέραιο κωδικοί σφάλματος να αντιπροσωπεύει την αποτυχία και τους λόγους αποτυχίας - δηλαδή, εξαιρέσεις. Ακολουθούν μερικά παραδείγματα:

if (chdir ("C: \ temp")) printf ("Δεν είναι δυνατή η αλλαγή σε κατάλογο temp:% d \ n", errno); FILE * fp = fopen ("C: \ temp \ foo"); if (fp == NULL) printf ("Αδυναμία ανοίγματος foo:% d \ n", errno);

Γ chdir () Η συνάρτηση (αλλαγή καταλόγου) επιστρέφει έναν ακέραιο: 0 στην επιτυχία ή -1 στην αποτυχία. Ομοίως, C's fopen () Η συνάρτηση (open file) επιστρέφει μη μηδενική δείκτης (ακέραια διεύθυνση) σε α ΑΡΧΕΙΟ δομή επιτυχίας ή μηδενικός δείκτης (0) (αντιπροσωπεύεται από σταθερά ΜΗΔΕΝΙΚΟ) σε αποτυχία. Σε κάθε περίπτωση, για να προσδιορίσετε την εξαίρεση που προκάλεσε την αποτυχία, πρέπει να διαβάσετε την καθολική Έρνο κωδικός σφάλματος που βασίζεται σε ακέραιο.

Οι κωδικοί σφάλματος παρουσιάζουν ορισμένα προβλήματα:

  • Οι ακέραιοι είναι χωρίς νόημα. δεν περιγράφουν τις εξαιρέσεις που αντιπροσωπεύουν. Για παράδειγμα, τι σημαίνει το 6;
  • Η συσχέτιση του περιβάλλοντος με έναν κωδικό σφάλματος είναι περίεργη. Για παράδειγμα, ίσως θέλετε να εξάγετε το όνομα του αρχείου που δεν μπορούσε να ανοίξει, αλλά πού θα αποθηκεύσετε το όνομα του αρχείου;
  • Οι ακέραιοι είναι αυθαίρετοι, κάτι που μπορεί να προκαλέσει σύγχυση κατά την ανάγνωση του πηγαίου κώδικα. Για παράδειγμα, καθορίζοντας αν (! chdir ("C: \ temp")) (! σημαίνει ΟΧΙ) αντί για εάν (chdir ("C: \ temp")) ο έλεγχος για αποτυχία είναι σαφέστερος. Ωστόσο, το 0 επιλέχθηκε για να δείξει την επιτυχία, και έτσι εάν (chdir ("C: \ temp")) πρέπει να καθοριστεί για δοκιμή αστοχίας.
  • Οι κωδικοί σφαλμάτων είναι πολύ εύκολο να αγνοηθούν, γεγονός που μπορεί να οδηγήσει σε κώδικα λάθη. Για παράδειγμα, ο προγραμματιστής θα μπορούσε να καθορίσει chdir ("C: \ temp"); και αγνοήστε το εάν (fp == NULL) έλεγχος. Επιπλέον, ο προγραμματιστής δεν χρειάζεται να εξετάσει Έρνο. Χωρίς δοκιμή για αποτυχία, το πρόγραμμα συμπεριφέρεται ακανόνιστα όταν οποιαδήποτε λειτουργία επιστρέφει μια ένδειξη αστοχίας.

Για την επίλυση αυτών των προβλημάτων, η Java υιοθέτησε μια νέα προσέγγιση στον χειρισμό εξαιρέσεων. Στην Java, συνδυάζουμε αντικείμενα που περιγράφουν εξαιρέσεις με έναν μηχανισμό που βασίζεται στη ρίψη και τη σύλληψη αυτών των αντικειμένων. Ακολουθούν ορισμένα πλεονεκτήματα της χρήσης αντικειμένων έναντι του κωδικού σφάλματος για να υποδηλώσετε εξαιρέσεις:

  • Ένα αντικείμενο μπορεί να δημιουργηθεί από μια τάξη με ένα νόημα όνομα. Για παράδειγμα, FileNotFoundException (στο java.io πακέτο) είναι πιο νόημα από το 6.
  • Τα αντικείμενα μπορούν να αποθηκεύσουν περιβάλλον σε διάφορα πεδία. Για παράδειγμα, μπορείτε να αποθηκεύσετε ένα μήνυμα, το όνομα του αρχείου που δεν μπόρεσε να ανοίξει, την πιο πρόσφατη θέση όπου απέτυχε μια λειτουργία ανάλυσης ή / και άλλα στοιχεία στα πεδία ενός αντικειμένου.
  • Δεν χρησιμοποιείτε αν δηλώσεις για δοκιμή για αποτυχία. Αντ 'αυτού, τα αντικείμενα εξαίρεσης ρίχνονται σε έναν χειριστή που είναι ξεχωριστός από τον κώδικα προγράμματος. Ως αποτέλεσμα, ο πηγαίος κώδικας είναι πιο ευανάγνωστος και λιγότερο πιθανό να είναι με λάθη.

Ρίξιμο και οι υποκατηγορίες του

Η Java παρέχει μια ιεραρχία τάξεων που αντιπροσωπεύουν διαφορετικά είδη εξαιρέσεων. Αυτές οι τάξεις έχουν τις ρίζες τους στο java.lang πακέτο Ρίξιμο τάξη, μαζί με το Εξαίρεση, RuntimeException, και Λάθος υποκατηγορίες.

Ρίξιμο είναι το απόλυτο superclass για εξαιρέσεις. Μόνο αντικείμενα δημιουργήθηκαν από Ρίξιμο και οι υποκατηγορίες του μπορούν να πεταχτούν (και στη συνέχεια να πιάσουν). Τέτοια αντικείμενα είναι γνωστά ως απορρίμματα.

ΕΝΑ Ρίξιμο το αντικείμενο σχετίζεται με ένα μήνυμα λεπτομέρειας που περιγράφει μια εξαίρεση. Διάφοροι κατασκευαστές, συμπεριλαμβανομένου του ζεύγους που περιγράφεται παρακάτω, παρέχονται για να δημιουργήσουν ένα Ρίξιμο αντικείμενο με ή χωρίς λεπτομερές μήνυμα:

  • Ρίξιμο () δημιουργεί ένα Ρίξιμο χωρίς λεπτομερές μήνυμα. Αυτός ο κατασκευαστής είναι κατάλληλος για καταστάσεις όπου δεν υπάρχει πλαίσιο. Για παράδειγμα, θέλετε μόνο να γνωρίζετε ότι μια στοίβα είναι κενή ή γεμάτη.
  • Throwable (μήνυμα συμβολοσειράς) δημιουργεί ένα Ρίξιμο με μήνυμα ως το λεπτομερές μήνυμα. Αυτό το μήνυμα μπορεί να αποσταλεί στον χρήστη ή / και να καταγραφεί.

Ρίξιμο παρέχει το String getMessage () μέθοδος για την επιστροφή του λεπτομερούς μηνύματος. Παρέχει επίσης πρόσθετες χρήσιμες μεθόδους, τις οποίες θα παρουσιάσω αργότερα.

Η τάξη εξαίρεσης

Ρίξιμο έχει δύο άμεσες υποκατηγορίες. Μία από αυτές τις υποκατηγορίες είναι Εξαίρεση, η οποία περιγράφει μια εξαίρεση που προκύπτει από εξωτερικό παράγοντα (όπως απόπειρα ανάγνωσης από ανύπαρκτο αρχείο). Εξαίρεση δηλώνει τους ίδιους κατασκευαστές (με ίδιες λίστες παραμέτρων) με Ρίξιμο, και κάθε κατασκευαστής επικαλείται το Ρίξιμο αντίστοιχος. Εξαίρεση κληρονομεί Ρίξιμοοι μέθοδοι? δεν δηλώνει καμία νέα μέθοδο.

Η Java παρέχει πολλές κατηγορίες εξαιρέσεων που απευθείας υποκατηγορίες Εξαίρεση. Ακολουθούν τρία παραδείγματα:

  • CloneNotSupportedException σηματοδοτεί μια προσπάθεια κλωνοποίησης ενός αντικειμένου του οποίου η κλάση δεν εφαρμόζει το Κλωνοποιήσιμο διεπαφή. Και οι δύο τύποι είναι στο java.lang πακέτο.
  • IOException σηματοδοτεί ότι έχει συμβεί κάποιο σφάλμα I / O. Αυτός ο τύπος βρίσκεται στο java.io πακέτο.
  • ParseException σηματοδοτεί ότι έχει προκύψει αστοχία κατά την ανάλυση κειμένου. Αυτός ο τύπος μπορεί να βρεθεί στο java.text πακέτο.

Παρατηρήστε ότι το καθένα Εξαίρεση Το όνομα της υποκατηγορίας τελειώνει με τη λέξη Εξαίρεση. Αυτή η σύμβαση διευκολύνει τον προσδιορισμό του σκοπού της τάξης.

Συνήθως θα κάνετε υποκατηγορία Εξαίρεση (ή μία από τις υποκατηγορίες του) με τις δικές σας κατηγορίες εξαιρέσεων (των οποίων τα ονόματα πρέπει να τελειώνουν με Εξαίρεση). Ακολουθούν μερικά παραδείγματα προσαρμοσμένων υποκατηγοριών:

δημόσια τάξη StackFullException επεκτείνει την εξαίρεση {} δημόσια τάξη EmptyDirectoryException επεκτείνει την εξαίρεση {private String directoryName; δημόσια EmptyDirectoryException (String message, String directoryName) {super (μήνυμα); this.directoryName = directoryName; } δημόσια συμβολοσειρά getDirectoryName () {return directoryName; }}

Το πρώτο παράδειγμα περιγράφει μια κλάση εξαίρεσης που δεν απαιτεί μήνυμα λεπτομέρειας. Είναι ο προεπιλεγμένος κατασκευαστής noggument Εξαίρεση(), που επικαλείται Ρίξιμο ().

Το δεύτερο παράδειγμα περιγράφει μια κατηγορία εξαίρεσης της οποίας ο κατασκευαστής απαιτεί ένα μήνυμα λεπτομέρειας και το όνομα του κενού καταλόγου. Ο κατασκευαστής επικαλείται Εξαίρεση (μήνυμα συμβολοσειράς), που επικαλείται Throwable (μήνυμα συμβολοσειράς).

Αντικείμενα από Εξαίρεση ή μία από τις υποκατηγορίες του (εκτός από το RuntimeException ή μία από τις υποκατηγορίες του) ελέγχονται εξαιρέσεις.

Η τάξη RuntimeException

Εξαίρεση υποκατηγοριοποιείται απευθείας από RuntimeException, η οποία περιγράφει μια εξαίρεση που πιθανότατα προκύπτει από κακώς γραμμένο κώδικα. RuntimeException δηλώνει τους ίδιους κατασκευαστές (με ίδιες λίστες παραμέτρων) με Εξαίρεση, και κάθε κατασκευαστής επικαλείται Εξαίρεση αντίστοιχος. RuntimeException κληρονομεί Ρίξιμοοι μέθοδοι. Δεν δηλώνει νέες μεθόδους.

Η Java παρέχει πολλές κατηγορίες εξαιρέσεων που απευθείας υποκατηγορίες RuntimeException. Τα ακόλουθα παραδείγματα είναι όλα τα μέλη του java.lang πακέτο:

  • ArithmeticException σηματοδοτεί μια παράνομη αριθμητική λειτουργία, όπως η απόπειρα διαίρεσης ενός ακέραιου με το 0.
  • IlegalArgumentException σηματοδοτεί ότι ένα παράνομο ή ακατάλληλο επιχείρημα έχει περάσει σε μια μέθοδο.
  • NullPointerException σηματοδοτεί μια προσπάθεια επίκλησης μιας μεθόδου ή πρόσβασης σε ένα πεδίο παρουσίας μέσω της μηδενικής αναφοράς.

Αντικείμενα από RuntimeException ή μία από τις υποκατηγορίες του είναι μη ελεγμένες εξαιρέσεις.

Η κλάση σφάλματος

ΡίξιμοΆλλη άμεση υποκατηγορία είναι Λάθος, το οποίο περιγράφει ένα σοβαρό (ακόμη και μη φυσιολογικό) πρόβλημα που δεν πρέπει να προσπαθήσει να αντιμετωπίσει μια λογική εφαρμογή - όπως εξάντληση μνήμης, υπερχείλιση της στοίβας του JVM ή προσπάθεια φόρτωσης μιας κλάσης που δεν μπορεί να βρεθεί. Σαν Εξαίρεση, Λάθος δηλώνει πανομοιότυπους κατασκευαστές με Ρίξιμο, κληρονομεί Ρίξιμοτις μεθόδους και δεν δηλώνει καμία από τις δικές της μεθόδους.

Μπορείτε να προσδιορίσετε Λάθος υποκατηγορίες από τη σύμβαση με την οποία τελειώνουν τα ονόματα των τάξεων τους Λάθος. Τα παραδείγματα περιλαμβάνουν Σφάλμα OutOfMemory, Σφάλμα Linkage, και Σφάλμα StackOverflow. Και οι τρεις τύποι ανήκουν στο java.lang πακέτο.

Ρίχνουν εξαιρέσεις

Η συνάρτηση C βιβλιοθήκης ειδοποιεί τον κωδικό κλήσης για μια εξαίρεση ρυθμίζοντας την καθολική Έρνο μεταβλητή σε κωδικό σφάλματος και επιστροφή κωδικού αστοχίας. Αντίθετα, μια μέθοδος Java ρίχνει ένα αντικείμενο. Το να γνωρίζετε πώς και πότε να κάνετε εξαιρέσεις αποτελεί ουσιαστική πτυχή του αποτελεσματικού προγραμματισμού Java. Η εξαίρεση περιλαμβάνει δύο βασικά βήματα:

  1. Χρησιμοποιήστε το βολή δήλωση για να ρίξει ένα αντικείμενο εξαίρεσης.
  2. Χρησιμοποιήστε το ρίχνει ρήτρα για την ενημέρωση του μεταγλωττιστή.

Οι επόμενες ενότητες θα επικεντρωθούν στη σύλληψη εξαιρέσεων και στον καθαρισμό μετά από αυτές, αλλά πρώτα ας μάθουμε περισσότερα σχετικά με τα απορρίμματα.

Η δήλωση ρίψης

Η Java παρέχει το βολή δήλωση για να ρίξει ένα αντικείμενο που περιγράφει μια εξαίρεση. Εδώ είναι η σύνταξη του βολή δήλωση:

βολή ρίξιμο;

Το αντικείμενο προσδιορίστηκε από το ρίξιμο είναι ένα παράδειγμα του Ρίξιμο ή οποιαδήποτε από τις υποκατηγορίες του. Ωστόσο, συνήθως ρίχνετε μόνο αντικείμενα που έχουν δημιουργηθεί από υποκατηγορίες του Εξαίρεση ή RuntimeException. Ακολουθούν μερικά παραδείγματα:

ρίξτε νέο FileNotFoundException ("αδυναμία εύρεσης αρχείου" + όνομα αρχείου); ρίξτε νέο IllegalArgumentException ("το όρισμα που πέρασε για να μετρηθεί είναι μικρότερο από το μηδέν");

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

Η ρήτρα ρίχνει

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

ρίχνει checkExceptionClassName (, checkExceptionClassName)*

ΕΝΑ ρίχνει η ρήτρα αποτελείται από λέξη-κλειδί ρίχνει ακολουθούμενη από μια λίστα διαχωρισμένη με κόμμα των ονομάτων τάξης των ελεγμένων εξαιρέσεων που απορρίπτονται από τη μέθοδο. Εδώ είναι ένα παράδειγμα:

public static void main (String [] args) ρίχνει ClassNotFoundException {if (args.length! = 1) {System.err.println ("use: java ... classfile"); ΕΠΙΣΤΡΟΦΗ; } Class.forName (args [0]); }

Αυτό το παράδειγμα επιχειρεί να φορτώσει ένα classfile που προσδιορίζεται από ένα όρισμα γραμμής εντολών. Αν Class.forName () δεν μπορεί να βρει το classfile, ρίχνει ένα java.lang.ClassNotFoundException αντικείμενο, το οποίο είναι μια ελεγμένη εξαίρεση.

Έλεγξε τη διαμάχη εξαίρεσης

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