Programmēšana

Java 124. padoms: izsekojiet savas darbības Java 1.4

Es nezinu par tevi, bet man ļoti patīk zināt, kur esmu. Būdams puisis, esmu nekad pazaudēju, bet dažreiz es vienkārši nezinu, kur esmu. Dažās vietās, piemēram, tirdzniecības centros, ir kartes ar indikatoriem "Tu esi šeit". Līdzīgi Java tagad ļauj mums ar sistēmas palīdzību noskaidrot mūsu atrašanās vietu. Šajā padomā es parādīšu, kā konsekventi un uzticami iegūt šo atrašanās vietas informāciju no sistēmas.

Es esmu cieši pārliecināts, ka izpildlaika sistēmai vajadzētu nodrošināt pietiekami daudz metadatu par pašu sistēmu, lai programmas varētu pieņemt labākus lēmumus un izpildīt uzdevumus. Java jau kādu laiku ir spējusi izpētīt un pārdomāt klases, taču līdz šim tai trūka vienkāršas iespējas izpildlaika kodu kartēt atpakaļ savā pozīcijā avota koda failā. Pirms Java 1.4 risinājums bija manuāli parsēt izņēmuma kaudzes izsekošanu. Tagad, izmantojot Java 1.4, mums ir labāks risinājums.

Izvelciet kaudzes pēdas?

Iepriekšējs Java 1.4 risinājums, lai apkopotu informāciju par atrašanās vietu, bija manuāli parsēt izņēmumus printStackTrace () izeja. Šeit ir vienkāršas kaudzes izsekošanas piemērs:

java.lang.Trowable vietnē boo.hoo.StackTrace.bar (StackTrace.java:223) vietnē boo.hoo.StackTrace.foo (StackTrace.java:218) vietnē boo.hoo.StackTrace.main (StackTrace.java:54) 

Iepriekš minētā koda izvilkšana nav liela parsēšanas problēma. Bet kā ar šo?

java.lang.Trowable vietnē boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:267) vietnē boo.hoo.StackTrace $ FirstNested. (StackTrace.java:256) vietnē boo.hoo.StackTrace. (StackTrace.java : 246) vietnē boo.hoo.StackTrace.main (StackTrace.java:70) 

Ugh. Ko tas viss dīvainais goobley-guk patiesībā nozīmē, un kāpēc man uz zemes man tas būtu jāanalizē? Acīmredzot sistēma jau izseko šo atrašanās vietas informāciju, jo tā spēj izveidot šīs kaudzes pēdas. Kāpēc šī atrašanās vietas informācija nav pieejama tieši? Nu, ar Java 1.4 tas beidzot ir.

Turklāt ņemiet vērā, ka, ņemot vērā JIT (tieši laikā) sastādītājus un dinamiskus, optimizējošus kompilatorus, piemēram, Sun Microsystems HotSpot, informācija par failiem un līniju numuriem var nebūt. Mērķis "sniegums vai krūtis" noteikti var būt apgrūtinošs.

Java 1.4 Metams glābšanai!

Pēc daudzu gadu sūdzību panesšanas Sun Microsystems beidzot ir paplašinājis java.lang.Metams klase ar getStackTrace () metodi. getStackTrace () atgriež masīvu StackTraceElements, kur katrs StackTraceElement objekts nodrošina līdzekļus, lai vairāk vai mazāk tieši iegūtu informāciju par atrašanās vietu.

Lai iegūtu kartēšanas informāciju, jūs joprojām izveidojat Metams jūsu koda interesējošajā vietā:

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

Šis kods pozicionē šo punktu sākumā galvenais ().

Protams, ir bezjēdzīgi vienkārši apkopot šo informāciju, neko ar to nedarot. Šim padomam mēs izmantosim katru pamata metodi StackTraceElements, lai iegūtu un parādītu visu iespējamo informāciju.

Programmas piemērs, StackTrace.java, parādīts, kā iegūt informāciju par atrašanās vietu, izmantojot vairākus piemērus. Programmas parauga sastādīšanai un palaišanai būs nepieciešams J2SE (Java 2 Platform, Standard Edition) 1.4 SDK.

Lai iegūtu un parādītu kartēšanas informāciju, koda piemērā tiek izmantota palīga metode, displayStackTraceInformation (), ar šādu pamata lietošanas idiomu:

 // ... public void crashAndBurnout () {// ... displayStackTraceInformation (new Throwable ()); // ...} // ... 

The displayStackTraceInformation () kods ir diezgan vienkāršs:

 public static boolean displayStackTraceInformation (Throwable ex, boolean displayAll) {if (null == ex) {System.out.println ("Nulles kaudzes izsekošanas atsauce! Bailing ..."); atgriezties nepatiesa; } System.out.println ("Steks saskaņā ar printStackTrace (): \ n"); ex.printStackTrace (); System.out.println (""); StackTraceElement [] stackElements = ex.getStackTrace (); if (displayAll) {System.out.println ("" + stackElements.length + "elements" + ((stackElements.length == 1)? "": "s") + "no kaudzes izsekošanas: \ n" ); } else {System.out.println ("" + stackElements.length + "elementa kaudzes pēdas augšējais elements: \ n"); } par (int lcv = 0; lcv <stackElements.length; lcv ++) {System.out.println ("Faila nosaukums:" + stackElements [lcv] .getFileName ()); System.out.println ("Līnijas numurs:" + stackElements [lcv] .getLineNumber ()); String className = stackElements [lcv] .getClassName (); String packageName = extractPackageName (klases nosaukums); String simpleClassName = extractSimpleClassName (klases nosaukums); System.out.println ("Paketes nosaukums:" + ("" .equals (packageName)? "[Noklusējuma pakete]": packageName)); System.out.println ("Pilns klases nosaukums:" + klases nosaukums); System.out.println ("Vienkāršais klases nosaukums:" + simpleClassName); System.out.println ("Neieslēgtais klases nosaukums:" + unmungeSimpleClassName (simpleClassName)); System.out.println ("Tiešais klases nosaukums:" + extractDirectClassName (simpleClassName)); System.out.println ("Metodes nosaukums:" + stackElements [lcv] .getMethodName ()); System.out.println ("Vietējā metode ?:" + stackElements [lcv] .isNativeMethod ()); System.out.println ("toString ():" + stackElements [lcv] .toString ()); System.out.println (""); ja (! displayAll) atgriežas patiess; } System.out.println (""); atgriezties taisnība; } // displayStackTraceInformation () beigas. 

Būtībā mēs piezvanām getStackTrace () uz ieejas Metams, un pēc tam cilpa caur personu StackTraceElements, iegūstot pēc iespējas vairāk kartēšanas informācijas.

Ievērojiet, cik maz ir krampis displayAll parametrs ievieš. displayAll ļauj zvanītāja vietnei izlemt, vai rādīt visus StackTraceElements vai tikai kaudzes augšējais elements. Programmas piemērs izmanto displayAll parametru, lai izlaidi ierobežotu līdz saprātīgai summai.

Lielākā daļa informācijas par kaudzes izsekošanu ir tieši noderīga. Piemēram, StackTraceElement.getMethodName () atgriež virkni, kurā ir metodes nosaukums, kamēr StackTraceElement.getFileName () atgriež virkni ar sākotnējā avota faila nosaukumu. Lasīt StackTraceElement Javadoc par pilnu metožu sarakstu.

Nodarbību nosaukumi ir ļoti daudz!

Kā jūs droši vien pamanījāt, displayStackTraceInformation () Kods izmanto vairākas papildu palīga metodes, lai atdalītu vērtību, kuru atgrieza StackTraceElement.getClassName (). Šīs palīgu metodes ir vajadzīgas, jo StackTraceElement.getClassName () atgriež klases pilnībā kvalificēto vārdu un StackTraceElement nav citu metožu, lai nodrošinātu pilnībā kvalificēta klases nosaukuma pamatā esošās daļas. Mēs uzzināsim par katru papildu palīga metodi, izskatot dažādus pielietojuma piemērus displayStackTraceInformation ().

Noklusētie un nosauktie pakotnes

Ņemot vērā pilnībā kvalificēto klases nosaukumu, extractPackageName () norāda pakas, kurā dzīvo klase, nosaukumu:

 public static String extractPackageName (String fullClassName) ("" .equals (fullClassName))) return ""; int lastDot = fullClassName.lastIndexOf ('.'); if (0> = lastDot) return ""; atgriezt fullClassName.substring (0, lastDot); 

Būtībā extractPackageName izvelk visu, kas atrodas pirms pēdējā punkta pilnībā kvalificētajā klases nosaukumā. Iepriekšējā informācija vienkārši ir paketes nosaukums.

Piezīme: Jūs varat komentēt / atcelt komentāru pakotnes paziņojumā augšpusē StackTrace.java lai izpētītu atšķirību starp paraugprogrammas palaišanu noklusējuma paketē bez nosaukuma un tās palaišanu boo.hoo iepakojums. Piemēram, ja to nekomentē, tiek parādīts augšējais kaudzes elements zvanam uz bārs() no foo () no galvenais () vajadzētu izskatīties šādi:

Faila nosaukums: StackTrace.java Rindas numurs: 227 Paketes nosaukums: boo.hoo Pilnas klases nosaukums: boo.hoo.StackTrace Vienkāršais klases nosaukums: StackTrace Neatrunātā klases nosaukums: StackTrace Direct klases nosaukums: StackTrace Metodes nosaukums: josla Native method ?: false toString ( ): boo.hoo.StackTrace.bar (StackTrace.java:227) 

Alternatīvi, ja komentējat pakotnes paziņojumu, iepriekš minētajam kaudzes elementam vajadzētu būt līdzīgam šim:

Faila nosaukums: StackTrace.java Rindas numurs: 227 Pakotnes nosaukums: [noklusējuma pakotne] Pilna klases nosaukums: StackTrace Vienkāršs klases nosaukums: StackTrace Unmunged klases nosaukums: StackTrace Tiešais klases nosaukums: StackTrace Metodes nosaukums: bar Vietējā metode ?: false toString (): StackTrace .bar (StackTrace.java:227) 

Vai klases nosaukumi kādreiz var būt vienkārši?

Nākamā palīga metode, kuru mēs izmantojam, ir extractSimpleClassName (). Kā redzēsiet, šīs metodes rezultāti ne vienmēr ir vienkārši, bet es vēlos skaidri nošķirt šo vienkāršoto klases nosaukumu no pilnībā kvalificētā klases nosaukuma.

Būtībā extractSimpleClassName () papildina extractPackageName ():

 public static String extractSimpleClassName (String fullClassName) ("" .equals (fullClassName))) return ""; int lastDot = fullClassName.lastIndexOf ('.'); if (0> lastDot) atgriež fullClassName; atgriezt fullClassName.substring (++ lastDot); 

Citiem vārdiem sakot, extractSimpleClassName () atgriež visu pēc pēdējais punkts (.) no pilnībā kvalificēta klases nosaukuma. Piemēram, no tā paša zvana uz bārs() iepriekš redzam, ka vienkāršais klases nosaukums ir taisnīgs StackTrace, neatkarīgi no tā, vai kods ir vai nav noklusējuma pakotnes daļa vai nosaukta pakete.

Mēs iegūstam interesantākus rezultātus, pievēršot uzmanību ligzdotajām klasēm. Programmas piemērā es izveidoju divus ligzdotu līmeņu nosaukumus (FirstNested un FirstNested.OtrāNested) kopā ar papildu anonīmu iekšējo klasi (iekšpusē FirstNested.OtrāNested).

Viss ligzdotais lietojums sākas ar:

 public StackTrace (boolean na) {StackTrace.FirstNested nested = new StackTrace.FirstNested (); } 

Ņemiet vērā, ka Būla parametrs (na) neko nenozīmē. Es tikko pievienoju, jo ir jānošķir citi konstruktori.

Šeit ir ligzdotās klases:

 public class FirstNested {public FirstNested () {StackTrace.displayStackTraceInformation (new Throwable ()); StackTrace.FirstNested.SecondNested yan = jauns StackTrace.FirstNested.SecondNested (); System.out.println ("Izgāzt no latvāņa iekšpuses ():"); yan.hogwash (); } public class SecondNested {public SecondNested () {StackTrace.displayStackTraceInformation (new Throwable ()); } public void hogwash () {StackTrace.displayStackTraceInformation (new Throwable ()); Whackable whacked = new Whackable () {public void whack () {StackTrace.displayStackTraceInformation (new Throwable ()); }}; // Anonīmo dalībnieku klases beigas. dauzīts. dauzīt (); } // latvāņa beigas (). } // FirstNested.SecondNexted dalībnieku klases beigas. } // FirstNested dalībnieku klases beigas. 

Augšējais kaudzes elements SecondNestedkonstruktors izskatās šādi:

Faila nosaukums: StackTrace.java Rindas numurs: 267 Paketes nosaukums: boo.hoo Pilnas klases nosaukums: boo.hoo.StackTrace $ FirstNested $ SecondNested Vienkāršais klases nosaukums: StackTrace $ FirstNested $ SecondNested Nemainītā klases nosaukums: StackTrace.FirstNested.SecondNested Tiešais klases nosaukums: SecondNested metodes nosaukums: Native method ?: false toString (): boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:267) 

Var redzēt, ka vienkāršais klases nosaukums šajā gadījumā nav tik vienkāršs. Ligzdotās klases nošķir no augstākā līmeņa ligzdotajām klasēm un no augstākā līmeņa klases, izmantojot dolāra zīmes rakstzīmi ($). Tātad tehniski otrās ligzdotās klases "vienkāršais" nosaukums ir StackTrace $ FirstNested $ SecondNested.

Es esmu sniedzis unmungeSimpleClassName () metode, kā dolāra zīmes aizstāt ar pilnības periodiem.

Tā kā esmu spītīga, es tomēr vēlējos iegūt patiesi vienkāršu klases nosaukumu, tāpēc izveidoju extractDirectClassName ():

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