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

Εξαιρέσεις στην Java, Μέρος 2: Προηγμένες δυνατότητες και τύποι

Το JDK 1.0 εισήγαγε ένα πλαίσιο γλωσσικών χαρακτηριστικών και τύπων βιβλιοθηκών για αντιμετώπιση εξαιρέσεις, οι οποίες είναι αποκλίσεις από την αναμενόμενη συμπεριφορά του προγράμματος. Το πρώτο μισό αυτού του σεμιναρίου κάλυψε τις βασικές δυνατότητες χειρισμού εξαιρέσεων της Java. Αυτό το δεύτερο ημίχρονο εισάγει πιο προηγμένες δυνατότητες που παρέχονται από το JDK 1.0 και τους διαδόχους του: JDK 1.4, JDK 7 και JDK 9. Μάθετε πώς να προβλέπετε και να διαχειρίζεστε εξαιρέσεις στα προγράμματα Java χρησιμοποιώντας προηγμένες δυνατότητες όπως ίχνη στοίβας, αιτίες και αλυσίδες εξαίρεσης, δοκιμάστε -με πόρους, πολλαπλά πιάτα, τελικό re-throw και stack stack.

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

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

Χειρισμός εξαίρεσης στα JDK 1.0 και 1.4: Ίχνη στοίβας

Κάθε JVM Νήμα (μια διαδρομή εκτέλεσης) σχετίζεται με ένα σωρός που δημιουργείται όταν δημιουργείται το νήμα. Αυτή η δομή δεδομένων χωρίζεται σε κουφώματα, οι οποίες είναι δομές δεδομένων που σχετίζονται με κλήσεις μεθόδου. Για το λόγο αυτό, κάθε στοίβα του νήματος αναφέρεται συχνά ως μέθοδος-κλήσης στοίβα.

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

ΕΝΑ ίχνος στοίβας (επίσης γνωστό ως backtrace στοίβας) είναι μια αναφορά των ενεργών πλαισίων στοίβας σε μια συγκεκριμένη χρονική στιγμή κατά την εκτέλεση ενός νήματος. Java Ρίξιμο τάξη (στο java.lang πακέτο) παρέχει μεθόδους για να εκτυπώσετε ένα ίχνος στοίβας, να συμπληρώσετε ένα ίχνος στοίβας και να αποκτήσετε πρόσβαση στα στοιχεία ενός ίχνους στοίβας.

Εκτύπωση ίχνους στοίβας

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

Λίστα 1. PrintStackTraceDemo.java (έκδοση 1)

εισαγωγή java.io.IOException; δημόσια τάξη PrintStackTraceDemo {public static void main (String [] args) ρίχνει το IOException {ρίξτε νέο IOException (); }}

Το κατασκεύασμα του καταλόγου 1 δημιουργεί ένα java.io.IOException αντικείμενο και πετάει αυτό το αντικείμενο από το κύριος() μέθοδος. Επειδή κύριος() δεν χειρίζεται αυτό το ρίξιμο, και επειδή κύριος() είναι η μέθοδος ανώτατου επιπέδου, η JVM τερματίζει με ένα κατάλληλο μήνυμα. Για αυτήν την εφαρμογή, θα δείτε το ακόλουθο μήνυμα:

Εξαίρεση στο νήμα "main" java.io.IOException στο PrintStackTraceDemo.main (PrintStackTraceDemo.java:7)

Το JVM εξάγει αυτό το μήνυμα καλώντας Ρίξιμο'μικρό void printStackTrace () μέθοδος, η οποία εκτυπώνει ένα ίχνος στοίβας για την επίκληση Ρίξιμο αντικείμενο στην τυπική ροή σφαλμάτων. Η πρώτη γραμμή δείχνει το αποτέλεσμα της επίκλησης των απορριμμάτων toString () μέθοδος. Η επόμενη γραμμή εμφανίζει δεδομένα που είχαν ήδη καταγραφεί από fillInStackTrace () (συζητήθηκε σύντομα).

Πρόσθετες μέθοδοι παρακολούθησης στοίβας εκτύπωσης

Ρίξιμοείναι υπερφορτωμένο void printStackTrace (PrintStream ps) και void printStackTrace (PrintWriter pw) Οι μέθοδοι εξάγουν το ίχνος στοίβας στην καθορισμένη ροή ή συγγραφέα.

Το ίχνος στοίβας αποκαλύπτει το αρχείο προέλευσης και τον αριθμό γραμμής όπου δημιουργήθηκε το ρίξιμο. Σε αυτήν την περίπτωση, δημιουργήθηκε στη Γραμμή 7 του PrintStackTrace.java αρχείο προέλευσης.

Μπορείτε να επικαλεσθείτε printStackTrace () άμεσα, συνήθως από ένα σύλληψη ΟΙΚΟΔΟΜΙΚΟ ΤΕΤΡΑΓΩΝΟ. Για παράδειγμα, εξετάστε μια δεύτερη έκδοση του PrintStackTraceDemo εφαρμογή.

Λίστα 2. PrintStackTraceDemo.java (έκδοση 2)

εισαγωγή java.io.IOException; δημόσια τάξη PrintStackTraceDemo {public static void main (String [] args) ρίχνει το IOException {try {a (); } catch (IOException πρόσβαση) {αρχ.printStackTrace (); }} στατικό κενό a () ρίχνει το IOException {b (); } στατικό κενό b () ρίχνει IOException {ρίξτε νέο IOException (); }}

Η λίστα 2 αποκαλύπτει ένα κύριος() μέθοδος που καλεί μέθοδο ένα(), η οποία καλεί τη μέθοδο σι(). Μέθοδος σι() ρίχνει ένα IOException αντικείμενο στο JVM, το οποίο ξετυλίγει τη στοίβα κλήσης μεθόδου έως ότου εντοπίσει κύριος()'μικρό σύλληψη μπλοκ, το οποίο μπορεί να χειριστεί την εξαίρεση. Η εξαίρεση αντιμετωπίζεται με επίκληση printStackTrace () στο ρίξιμο. Αυτή η μέθοδος παράγει την ακόλουθη έξοδο:

java.io.IOException στο PrintStackTraceDemo.b (PrintStackTraceDemo.java:24) στο PrintStackTraceDemo.a (PrintStackTraceDemo.java:19) στο PrintStackTraceDemo.main (PrintStackTraceDemo.java:9)

printStackTrace () δεν εξάγει το όνομα του νήματος. Αντ 'αυτού, επικαλείται toString () στο ρίξιμο για να επιστρέψετε το πλήρως αναγνωρισμένο όνομα κλάσης του ρίξιμου (java.io.IOException), που εξάγεται στην πρώτη γραμμή. Στη συνέχεια εξάγει την ιεραρχία μεθόδου-κλήσης: την πιο πρόσφατα καλούμενη μέθοδο (σι()) είναι στην κορυφή και κύριος() είναι στο κάτω μέρος.

Ποια γραμμή προσδιορίζει το ίχνος στοίβας;

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

Συμπλήρωση ίχνους στοίβας

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

Λίστα 3. FillInStackTraceDemo.java (έκδοση 1)

εισαγωγή java.io.IOException; δημόσια τάξη FillInStackTraceDemo {public static void main (String [] args) ρίχνει το IOException {try {a (); } catch (IOException πρόσβαση) {αρχ.printStackTrace (); System.out.println (); ρίξτε (IOException) πρόσβαση.fillInStackTrace (); }} στατικό κενό a () ρίχνει το IOException {b (); } στατικό κενό b () ρίχνει IOException {ρίξτε νέο IOException (); }}

Η κύρια διαφορά μεταξύ της καταχώρισης 3 και της καταχώρισης 2 είναι η σύλληψη μπλοκ ρίξτε (IOException) πρόσβαση.fillInStackTrace (); δήλωση. Αυτή η δήλωση αντικαθιστά Ο.Ε.Το ίχνος στοίβας, μετά το οποίο το ρίξιμο ρίχνεται ξανά. Θα πρέπει να παρατηρήσετε αυτήν την έξοδο:

java.io.IOException στο FillInStackTraceDemo.b (FillInStackTraceDemo.java:26) στο FillInStackTraceDemo.a (FillInStackTraceDemo.java:21) στο FillInStackTraceDemo.main (FillInStackTraceDemo.xava. FillInStackTraceDemo.main (FillInStackTraceDemo.java:15)

Αντί να επαναλαμβάνεται το αρχικό ίχνος στοίβας, το οποίο προσδιορίζει τη θέση όπου το IOException δημιουργήθηκε αντικείμενο, το δεύτερο ίχνος στοίβας αποκαλύπτει τη θέση του طالب.fillInStackTrace ().

Κατασκευαστές με δυνατότητα ρίψης και fillInStackTrace ()

Καθένα από Ρίξιμοεπικαλείται τους κατασκευαστές fillInStackTrace (). Ωστόσο, ο ακόλουθος κατασκευαστής (που εισήχθη στο JDK 7) δεν θα επικαλεστεί αυτήν τη μέθοδο όταν περάσετε ψευδής προς την writableStackTrace:

Throwable (Μήνυμα συμβολοσειράς, Throwable αιτία, boolean activSuppression, boolean writableStackTrace)

fillInStackTrace () επικαλείται μια εγγενή μέθοδο που ακολουθεί τη στοίβα μεθόδου-κλήσης του τρέχοντος νήματος για να δημιουργήσει το ίχνος στοίβας. Αυτή η διαδρομή είναι δαπανηρή και μπορεί να επηρεάσει την απόδοση, εάν συμβαίνει πολύ συχνά.

Εάν αντιμετωπίσετε μια κατάσταση (ίσως να περιλαμβάνει ενσωματωμένη συσκευή) όπου η απόδοση είναι κρίσιμη, μπορείτε να αποτρέψετε την κατασκευή ίχνους στοίβας με παράκαμψη fillInStackTrace (). Δείτε την καταχώριση 4.

Λίστα 4. FillInStackTraceDemo.java (έκδοση 2)

{public static void main (String [] args) ρίχνει το NoStackTraceException {try {a (); } catch (NoStackTraceException nste) {nste.printStackTrace (); }} στατικό κενό a () ρίχνει το NoStackTraceException {b (); } στατικό κενό b () ρίχνει NoStackTraceException {ρίξτε νέο NoStackTraceException (); }} Η κλάση NoStackTraceException επεκτείνει την Εξαίρεση {@Override public synchronized Throwable fillInStackTrace () {επιστροφή αυτού; }}

Η λίστα 4 εισάγει NoStackTraceException. Αυτή η προσαρμοσμένη εξαίρεση κλάσης εξαιρέσεων fillInStackTrace () το να γυρίζεις Αυτό - αναφορά στην επίκληση Ρίξιμο. Αυτό το πρόγραμμα παράγει την ακόλουθη έξοδο:

NoStackTraceException

Σχολιάστε την παράκαμψη fillInStackTrace () μέθοδο και θα παρατηρήσετε την ακόλουθη έξοδο:

NoStackTraceException στο FillInStackTraceDemo.b (FillInStackTraceDemo.java:22) στο FillInStackTraceDemo.a (FillInStackTraceDemo.java:17) στο FillInStackTraceDemo.main (FillInStackTraceDemo.java:7)

Πρόσβαση στα στοιχεία ενός ίχνους στοίβας

Κατά καιρούς θα χρειαστεί να αποκτήσετε πρόσβαση στα στοιχεία ενός ίχνους στοίβας για να εξαγάγετε τις λεπτομέρειες που απαιτούνται για την καταγραφή, τον προσδιορισμό της προέλευσης μιας διαρροής πόρων και άλλους σκοπούς. ο printStackTrace () και fillInStackTrace () Οι μέθοδοι δεν υποστηρίζουν αυτήν την εργασία, αλλά το JDK 1.4 εισήχθη java.lang.StackTraceElement και τις μεθόδους του για το σκοπό αυτό.

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

  • String getClassName () επιστρέφει το πλήρως αναγνωρισμένο όνομα της κλάσης που περιέχει το σημείο εκτέλεσης που αντιπροσωπεύεται από αυτό το στοιχείο παρακολούθησης στοίβας.
  • String getFileName () επιστρέφει το όνομα του αρχείου προέλευσης που περιέχει το σημείο εκτέλεσης που αντιπροσωπεύεται από αυτό το στοιχείο παρακολούθησης στοίβας.
  • int getLineNumber () επιστρέφει τον αριθμό γραμμής της γραμμής προέλευσης που περιέχει το σημείο εκτέλεσης που αντιπροσωπεύεται από αυτό το στοιχείο παρακολούθησης στοίβας.
  • String getMethodName () επιστρέφει το όνομα της μεθόδου που περιέχει το σημείο εκτέλεσης που αντιπροσωπεύεται από αυτό το στοιχείο παρακολούθησης στοίβας.
  • boolean isNativeMethod () επιστρέφει αληθής όταν η μέθοδος που περιέχει το σημείο εκτέλεσης που αντιπροσωπεύεται από αυτό το στοιχείο παρακολούθησης στοίβας είναι μια εγγενής μέθοδος.

Το JDK 1.4 παρουσίασε επίσης το StackTraceElement [] getStackTrace () μέθοδος στο java.lang.Tread και Ρίξιμο τάξεις. Αυτή η μέθοδος επιστρέφει αντίστοιχα μια σειρά στοιχείων ανίχνευσης στοίβας που αντιπροσωπεύουν την απόρριψη στοίβας του νήματος επίκλησης και παρέχει πρόσβαση μέσω προγραμματισμού στις πληροφορίες ιχνών στοίβας που εκτυπώνονται από printStackTrace ().

Η λίστα 5 δείχνει StackTraceElement και getStackTrace ().

Λίστα 5. StackTraceElementDemo.java (έκδοση 1)

εισαγωγή java.io.IOException; δημόσια τάξη StackTraceElementDemo {public static void main (String [] args) ρίχνει το IOException {try {a (); } catch (IOException) {StackTraceElement [] stackTrace = θε.getStackTrace (); για (int i = 0; i <stackTrace.length; i ++) {System.err.println ("Exception throw from" + stackTrace [i] .getMethodName () + "in class" + stackTrace [i] .getClassName () + "on line" + stackTrace [i] .getLineNumber () + "του αρχείου" + stackTrace [i] .getFileName ()); System.err.println (); }}} στατικό κενό a () ρίχνει το IOException {b (); } στατικό κενό b () ρίχνει IOException {ρίξτε νέο IOException (); }}

Όταν εκτελείτε αυτήν την εφαρμογή, θα παρατηρήσετε την ακόλουθη έξοδο:

Εξαίρεση που ρίχτηκε από το b στην κλάση StackTraceElementDemo στη γραμμή 33 του αρχείου StackTraceElementDemo.java Εξαίρεση που ρίχτηκε από ένα στην κατηγορία StackTraceElementDemo στη γραμμή 28 του αρχείου StackTraceElementDemo.java Εξαίρεση από την κύρια στην κατηγορία StackTraceElementDemo στη γραμμή 9 του αρχείου StackTrace

Τέλος, το JDK 1.4 παρουσίασε το setStackTrace () μέθοδος για Ρίξιμο. Αυτή η μέθοδος έχει σχεδιαστεί για χρήση μέσω πλαισίων απομακρυσμένης διαδικασίας κλήσης (RPC) και άλλων προηγμένων συστημάτων, επιτρέποντας στον πελάτη να παρακάμψει το προεπιλεγμένο ίχνος στοίβας που δημιουργείται από fillInStackTrace () όταν κατασκευάζεται ένα αναλώσιμο.

Προηγουμένως έδειξα πώς να παρακάμψω fillInStackTrace () για να αποφευχθεί η δημιουργία ίχνους στοίβας. Αντ 'αυτού, θα μπορούσατε να εγκαταστήσετε ένα νέο ίχνος στοίβας χρησιμοποιώντας StackTraceElement και setStackTrace (). Δημιουργήστε μια σειρά από StackTraceElement αντικείμενα που έχουν αρχικοποιηθεί μέσω του ακόλουθου κατασκευαστή και μεταβιβάζονται σε αυτόν τον πίνακα setStackTrace ():

StackTraceElement (String δήλωσηςClass, String methodName, String fileName, int lineNumber)

Η λίστα 6 δείχνει StackTraceElement και setStackTrace ().