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

Συμβουλή Java 124: Εντοπίστε τα βήματά σας στην Java 1.4

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

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

Διαχωρισμός ίχνους στοίβας;

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

java.lang.Throwable στο boo.hoo.StackTrace.bar (StackTrace.java:2323) στο boo.hoo.StackTrace.foo (StackTrace.java:2318) στο boo.hoo.StackTrace.main (StackTrace.java:54) 

Το διαχωρισμό του παραπάνω κώδικα δεν αποτελεί σημαντικό πρόβλημα ανάλυσης. Τι γίνεται όμως με αυτό;

java.lang.Throwable στο boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:2367) στο boo.hoo.StackTrace $ FirstNested. (StackTrace.java: 235) στο boo.hoo.StackTrace. : 246) στο boo.hoo.StackTrace.main (StackTrace.java:70) 

Ωχ. Τι σημαίνει πραγματικά αυτό το παράξενο goobley-guk και γιατί στη γη πρέπει να το αναλύσω; Προφανώς, το σύστημα παρακολουθεί ήδη αυτές τις πληροφορίες τοποθεσίας, καθώς είναι σε θέση να δημιουργήσει αυτά τα ίχνη στοίβας. Γιατί λοιπόν αυτές οι πληροφορίες τοποθεσίας δεν είναι διαθέσιμες απευθείας; Λοιπόν, με το Java 1.4, είναι τελικά.

Επιπλέον, λάβετε υπόψη ότι έναντι των JIT (just-in-time) μεταγλωττιστών και δυναμικών, βελτιστοποιημένων μεταγλωττιστών όπως το HotSpot της Sun Microsystems, ενδέχεται να μην υπάρχουν πληροφορίες αριθμού αρχείων και γραμμών. Ο στόχος της "απόδοσης ή της αποτυχίας" μπορεί σίγουρα να είναι ενοχλητικός.

Java 1.4 Δυνατότητα διάσωσης!

Αφού ανέχεται χρόνια καταγγελιών, η Sun Microsystems επέκτεινε επιτέλους το java.lang.Throwable τάξη με το getStackTrace () μέθοδος. getStackTrace () επιστρέφει μια σειρά από StackTraceElements, όπου το καθένα StackTraceElement Το αντικείμενο παρέχει τα μέσα για λίγο ή πολύ άμεση εξαγωγή πληροφοριών τοποθεσίας.

Για να αποκτήσετε αυτές τις πληροφορίες χαρτογράφησης, εξακολουθείτε να δημιουργείτε ένα Ρίξιμο παρουσία στο σημείο ενδιαφέροντος για τον κωδικό σας:

 // ... public static void main (String [] args) {Throwable ex = new Throwable (); // ... 

Αυτός ο κωδικός τοποθετεί το σημείο στην αρχή του κύριος().

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

Το παράδειγμα προγράμματος, StackTrace.java, δείχνει πώς μπορείτε να εξαγάγετε τις πληροφορίες τοποθεσίας με διάφορα παραδείγματα. Θα χρειαστείτε το J2SE (Java 2 Platform, Standard Edition) 1.4 SDK για να μεταγλωττίσετε και να εκτελέσετε το παράδειγμα προγράμματος.

Για να εξαγάγετε και να εμφανίσετε τις πληροφορίες χαρτογράφησης, ο κωδικός παραδείγματος χρησιμοποιεί μια βοηθητική μέθοδο, displayStackTraceInformation (), με το ακόλουθο βασικό ιδίωμα χρήσης:

 // ... public void crashAndBurnout () {// ... displayStackTraceInformation (νέο Throwable ()); // ...} // ... 

ο displayStackTraceInformation () ο κωδικός είναι αρκετά απλός:

 δημόσια στατική boolean displayStackTraceInformation (Throwable ex, boolean displayAll) {if (null == ex) {System.out.println ("Null stack trace αναφορά! Bailing ..."); επιστροφή ψευδής? } System.out.println ("Η στοίβα σύμφωνα με το printStackTrace (): \ n"); ex.printStackTrace (); System.out.println (""); StackTraceElement [] stackElements = ex.getStackTrace (); if (displayAll) {System.out.println ("The" + stackElements.length + "element" + ((stackElements.length == 1)? "": "s") + "του ίχνους στοίβας: \ n" ); } άλλο {System.out.println ("Το κορυφαίο στοιχείο ενός ίχνους στοίβας στοιχείου" + stackElements.length + ": \ n"); } για (int lcv = 0; lcv <stackElements.length; lcv ++) {System.out.println ("Όνομα αρχείου:" + stackElements [lcv] .getFileName ()); System.out.println ("Αριθμός γραμμής:" + stackElements [lcv] .getLineNumber ()); String className = stackElements [lcv] .getClassName (); String packageName = extractPackageName (className); String simpleClassName = extractSimpleClassName (className); System.out.println ("Όνομα πακέτου:" + ("" .equals (packageName)? "[Προεπιλεγμένο πακέτο]": packageName)); System.out.println ("Όνομα πλήρους τάξης:" + className); System.out.println ("Απλό όνομα κλάσης:" + simpleClassName); System.out.println ("Unmunged class name:" + unmungeSimpleClassName (simpleClassName)); System.out.println ("Όνομα άμεσης κλάσης:" + extractDirectClassName (simpleClassName)); System.out.println ("Όνομα μεθόδου:" + stackElements [lcv] .getMethodName ()); System.out.println ("Native method ?:" + stackElements [lcv] .isNativeMethod ()); System.out.println ("toString ():" + stackElements [lcv] .toString ()); System.out.println (""); αν (! displayAll) επιστρέψει true; } System.out.println (""); επιστροφή αληθινή? } // Τέλος του displayStackTraceInformation (). 

Βασικά, καλούμε getStackTrace () σχετικά με το πέρασμα Ρίξιμοκαι, στη συνέχεια, περάστε από το άτομο StackTraceElements, εξάγοντας όσο το δυνατόν περισσότερες πληροφορίες χαρτογράφησης.

Σημειώστε το κομμάτι του cruft το οθόνηΌλα η παράμετρος εισάγει. οθόνηΌλα επιτρέπει στον ιστότοπο κλήσης να αποφασίσει εάν θα εμφανίσει όλα ή όχι StackTraceElements ή απλώς το κορυφαίο στοιχείο της στοίβας. Το παράδειγμα προγράμματος χρησιμοποιεί το οθόνηΌλα παράμετρος για τον περιορισμό της παραγωγής σε λογικό ποσό.

Οι περισσότερες από τις πληροφορίες ανίχνευσης στοίβας είναι άμεσα χρήσιμες. Για παράδειγμα, το StackTraceElement.getMethodName () επιστρέφει μια συμβολοσειρά που περιέχει το όνομα της μεθόδου, ενώ StackTraceElement.getFileName () επιστρέφει μια συμβολοσειρά με το αρχικό όνομα αρχείου προέλευσης. Διαβάστε το StackTraceElement Javadoc για τον πλήρη κατάλογο μεθόδων.

Τα ονόματα των τάξεων αφθονούν!

Όπως πιθανώς παρατηρήσατε, το displayStackTraceInformation () Ο κώδικας χρησιμοποιεί πολλές πρόσθετες μεθόδους βοηθού για να ξεχωρίσει την τιμή που επέστρεψε StackTraceElement.getClassName (). Αυτές οι βοηθητικές μέθοδοι χρειάζονται επειδή StackTraceElement.getClassName () επιστρέφει το πλήρως αναγνωρισμένο όνομα της τάξης και StackTraceElement δεν έχει άλλες μεθόδους για να παρέχει τα υποκείμενα μέρη του πλήρως αναγνωρισμένου ονόματος τάξης. Θα μάθουμε για κάθε πρόσθετη μέθοδο βοηθού μέσω των διάφορων παραδειγμάτων χρήσεων του displayStackTraceInformation ().

Προεπιλεγμένα εναντίον πακέτων με όνομα

Δεδομένου του πλήρως αναγνωρισμένου ονόματος τάξης, extractPackageName () δίνει το όνομα του πακέτου στο οποίο βρίσκεται η τάξη:

 δημόσιο στατικό String extractPackageName (String fullClassName) ("" .equals (fullClassName))) return ""; int lastDot = fullClassName.lastIndexOf ('.'); if (0> = lastDot) επιστροφή ""; επιστροφή fullClassName.substring (0, lastDot); 

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

Σημείωση: Μπορείτε να σχολιάσετε / αποσυνδέσετε τη δήλωση πακέτου στην κορυφή του StackTrace.java για να εξερευνήσετε τη διαφορά μεταξύ της εκτέλεσης του παραδείγματος προγράμματος στο προεπιλεγμένο, χωρίς όνομα πακέτο έναντι της εκτέλεσης του στο boo.hoo πακέτο. Για παράδειγμα, όταν δεν είναι ασυμβίβαστη, η εμφάνιση του ανώτατου στοιχείου στοίβας για την κλήση προς μπαρ() από foo () από κύριος() πρέπει να μοιάζει με αυτό:

Όνομα αρχείου: StackTrace.java Αριθμός γραμμής: 227 Όνομα πακέτου: boo.hoo Πλήρες όνομα κατηγορίας: boo.hoo.StackTrace Απλό όνομα κλάσης: StackTrace Unmunged όνομα κλάσης: StackTrace Direct όνομα κλάσης: StackTrace Όνομα μεθόδου: bar Εγγενής μέθοδος: false toString ( ): boo.hoo.StackTrace.bar (StackTrace.java:2327) 

Εναλλακτικά, εάν σχολιάσετε τη δήλωση πακέτου, τότε το παραπάνω στοιχείο στοίβας πρέπει να μοιάζει με αυτό:

Όνομα αρχείου: StackTrace.java Αριθμός γραμμής: 227 Όνομα πακέτου: [προεπιλεγμένο πακέτο] Πλήρες όνομα κατηγορίας: StackTrace Απλό όνομα κλάσης: StackTrace Όνομα κλάσης Unmunged: Όνομα κλάσης StackTrace Direct: StackTrace Όνομα μεθόδου: bar Εγγενής μέθοδος: false toString (): StackTrace .bar (StackTrace.java: 2227) 

Μπορούν τα ονόματα τάξεων να είναι απλά;

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

Βασικα, απόσπασμαSimpleClassName () συμπληρώνει extractPackageName ():

 δημόσιο στατικό String extractSimpleClassName (String fullClassName) ("" .equals (fullClassName))) return ""; int lastDot = fullClassName.lastIndexOf ('.'); εάν (0> lastDot) επιστρέψτε το πλήρεςClassName; επιστροφή fullClassName.substring (++ lastDot); 

Με άλλα λόγια, απόσπασμαSimpleClassName () επιστρέφει τα πάντα μετά η τελευταία κουκκίδα (.) από το πλήρως αναγνωρισμένο όνομα τάξης. Για παράδειγμα, από την ίδια κλήση προς μπαρ() παραπάνω, βλέπουμε ότι το απλό όνομα τάξης είναι δίκαιο StackTrace, ανεξάρτητα από το αν ο κωδικός αποτελεί μέρος του προεπιλεγμένου πακέτου ή ενός πακέτου με όνομα.

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

Ολόκληρη η ένθετη χρήση ξεκινά με:

 δημόσια StackTrace (boolean na) {StackTrace.FirstNested nested = νέο StackTrace.FirstNested (); } 

Σημειώστε ότι η παράμετρος boolean (να) δεν σημαίνει τίποτα. Μόλις το πρόσθεσα, καθώς άλλοι κατασκευαστές πρέπει να διακριθούν.

Εδώ είναι οι ένθετες τάξεις:

 δημόσια τάξη FirstNested {public FirstNested () {StackTrace.displayStackTraceInformation (νέο Throwable ()); StackTrace.FirstNested.SecondNested yan = νέο StackTrace.FirstNested.SecondNested (); System.out.println ("Απόρριψη από το εσωτερικό hogwash ():"); yan.hogwash (); } δημόσια τάξη SecondNested {public SecondNested () {StackTrace.displayStackTraceInformation (νέο Throwable ()); } public void hogwash () {StackTrace.displayStackTraceInformation (νέο Throwable ()); Whackable whacked = νέο Whackable () {public void whack () {StackTrace.displayStackTraceInformation (νέο Throwable ()); }} // Τέλος της ανώνυμης τάξης μέλους. whacked.whack (); } // Τέλος του hogwash (). } // Τέλος της τάξης μέλους FirstNested.SecondNexted. } // Τέλος της τάξης μέλους της FirstNested. 

Το κορυφαίο στοιχείο στοίβας για ΔεύτεροςΟ κατασκευαστής μοιάζει με αυτό:

Όνομα αρχείου: StackTrace.java Αριθμός γραμμής: 267 Όνομα πακέτου: boo.hoo Πλήρες όνομα τάξης: boo.hoo.StackTrace $ FirstNested $ SecondNested Απλό όνομα κλάσης: StackTrace $ FirstNested $ SecondNested Unmunged όνομα κλάσης: StackTrace.FirstNested.SecondNested Direct όνομα κλάσης: Όνομα μεθόδου SecondNested: Εγγενής μέθοδος: false toString (): boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:2367) 

Μπορείτε να δείτε ότι το απλό όνομα τάξης δεν είναι τόσο απλό σε αυτήν την περίπτωση. Οι ένθετες τάξεις διακρίνονται από τις ένθετες τάξεις υψηλότερου επιπέδου και από την τάξη ανώτερου επιπέδου χρησιμοποιώντας τον χαρακτήρα του δολαρίου ($). Έτσι, τεχνικά, το "απλό" όνομα της δεύτερης τάξης είναι StackTrace $ FirstNested $ SecondNested.

Έχω παράσχει το unmungeSimpleClassName () μέθοδος για την αντικατάσταση των σημείων δολαρίου με περιόδους πληρότητας.

Δεδομένου ότι είμαι πεισματάρης, ήθελα ακόμα να αποκτήσω το πραγματικά απλό όνομα τάξης, έτσι δημιούργησα extractDirectClassName ():

$config[zx-auto] not found$config[zx-overlay] not found