Programmering

Java Tips 124: Spor trinnene dine i Java 1.4

Jeg vet ikke om deg, men jeg liker veldig godt å vite hvor jeg er. Å være en fyr, det er jeg aldri tapt, men noen ganger vet jeg bare ikke hvor jeg er. Noen steder, for eksempel kjøpesentre, har kart med "You Are Here" -indikatorer. På samme måte lar Java oss nå finne ut plasseringen vår med systemets hjelp. I dette tipset vil jeg vise deg hvordan du trekker ut denne plasseringsinformasjonen fra systemet på en konsekvent og pålitelig måte.

Jeg er overbevist om at kjøretidssystemet skal gi nok metadata om selve systemet slik at programmene kan ta bedre beslutninger og fullføre oppgaver. Java har vært i stand til å introdusere og reflektere over klasser i noen tid, men til nå har det manglet den enkle muligheten til å kartlegge kjøretidskoden tilbake til sin posisjon i kildekodefilen. Pre-Java 1.4-løsningen var å manuelt analysere et unntaks stack-spor. Nå, med Java 1.4, har vi en bedre løsning.

Trekke fra hverandre et stabelspor?

Den midlertidige løsningen for Java 1.4 for å samle stedsinformasjon var å manuelt analysere et unntak printStackTrace () produksjon. Her er et eksempel på en enkel stakksporing:

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

Å trekke koden ovenfor er ikke et stort analyseproblem. Men hva med dette?

java.lang.Trowable at boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:267) at boo.hoo.StackTrace $ FirstNested. (StackTrace.java:256) at boo.hoo.StackTrace. (StackTrace.java : 246) på boo.hoo.StackTrace.main (StackTrace.java:70) 

Ugh. Hva betyr egentlig alt det rare goobley-guk, og hvorfor i all verden skal jeg analysere det? Åpenbart sporer systemet allerede denne plasseringsinformasjonen, siden det er i stand til å bygge disse stabelsporene. Så hvorfor er ikke denne informasjonen tilgjengelig direkte? Vel, med Java 1.4 er det endelig det.

I tillegg må du huske at i møte med JIT (just-in-time) kompilatorer og dynamiske, kan det hende at optimalisering av kompilatorer som Sun Microsystems 'HotSpot, ikke finnes fil- og linjenummerinformasjon. Målet med "performance or bust" kan absolutt være plagsomt.

Java 1.4 kan kastes til unnsetning!

Etter å ha tolerert mange års klager, har Sun Microsystems endelig utvidet java.lang. kastbar klasse med getStackTrace () metode. getStackTrace () returnerer en rekke StackTraceElements, hvor hver StackTraceElement objektet gir midler til mer eller mindre direkte å trekke ut lokaliseringsinformasjon.

For å tilegne deg kartinformasjonen, oppretter du fortsatt en Kastbar forekomst på det interessante stedet i koden din:

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

Denne koden plasserer det som peker i begynnelsen av hoved().

Selvfølgelig er det ubrukelig å bare samle den informasjonen uten å gjøre noe med den. For dette tipset vil vi bruke hver underliggende metode for StackTraceElements for å hente ut og vise all informasjon vi kan.

Eksempelprogrammet, StackTrace.java, viser hvordan du trekker ut lokaliseringsinformasjonen med flere eksempler. Du trenger J2SE (Java 2 Platform, Standard Edition) 1.4 SDK for å kompilere og kjøre eksempelprogrammet.

For å trekke ut og vise kartinformasjonen bruker eksempelkoden en hjelpemetode, displayStackTraceInformation (), med følgende grunnleggende bruksform:

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

De displayStackTraceInformation () koden er ganske grei:

 public static boolean displayStackTraceInformation (Throwable ex, boolean displayAll) {if (null == ex) {System.out.println ("Null stack trace reference! Bailing ..."); returner falsk; } System.out.println ("Stakken i henhold til printStackTrace (): \ n"); ex.printStackTrace (); System.out.println (""); StackTraceElement [] stackElements = ex.getStackTrace (); if (displayAll) {System.out.println ("" + stackElements.length + "element" + ((stackElements.length == 1)? "": "s") + "av stack-sporingen: \ n" ); } annet {System.out.println ("Det øverste elementet i et" + stackElements.length + "element stack-spor: \ n"); } for (int lcv = 0; lcv <stackElements.length; lcv ++) {System.out.println ("Filnavn:" + stackElements [lcv] .getFileName ()); System.out.println ("Linjenummer:" + stackElements [lcv] .getLineNumber ()); Streng className = stackElements [lcv] .getClassName (); Strengpakkenavn = extractPackageName (className); Streng simpleClassName = extractSimpleClassName (className); System.out.println ("Pakkens navn:" + ("" .equals (packageName)? "[Standardpakke]": packageName)); System.out.println ("Fullt klassenavn:" + className); System.out.println ("Enkelt klassenavn:" + simpleClassName); System.out.println ("Unmunged class name:" + unmungeSimpleClassName (simpleClassName)); System.out.println ("Direkte klassenavn:" + extractDirectClassName (simpleClassName)); System.out.println ("Metodenavn:" + stackElements [lcv] .getMethodName ()); System.out.println ("Innfødt metode ?:" + stackElements [lcv] .isNativeMethod ()); System.out.println ("toString ():" + stackElements [lcv] .toString ()); System.out.println (""); hvis (! displayAll) returnerer true; } System.out.println (""); returner sant; } // Slutt på displayStackTraceInformation (). 

I utgangspunktet ringer vi getStackTrace () på innleveringen Kastbar, og deretter gå gjennom individet StackTraceElements, utvinne så mye kartinformasjon som mulig.

Legg merke til biten av cruft displayAll parameter introduserer. displayAll lar anropssiden bestemme om alle skal vises StackTraceElements eller bare stakkens øverste element. Eksempelprogrammet bruker displayAll parameter for å begrense utgangen til et rimelig beløp.

Det meste av stabelsporinformasjonen er direkte nyttig. For eksempel StackTraceElement.getMethodName () returnerer en streng som inneholder metodenavnet, mens StackTraceElement.getFileName () returnerer en streng med det opprinnelige kildens filnavn. Les StackTraceElement Javadoc for den komplette listen over metoder.

Klassenavn i massevis!

Som du sikkert la merke til, displayStackTraceInformation () koden bruker flere tilleggsmetoder for å trekke fra hverandre verdien som returneres av StackTraceElement.getClassName (). Disse hjelpemetodene er nødvendige fordi StackTraceElement.getClassName () returnerer klassens fullt kvalifiserte navn, og StackTraceElement har ingen andre metoder for å gi de underliggende delene av det fullt kvalifiserte klassenavnet. Vi lærer om hver ekstra hjelpermetode ved å jobbe gjennom de forskjellige eksemplene på bruk av displayStackTraceInformation ().

Standard vs. navngitte pakker

Gitt det fullt kvalifiserte klassenavnet, extractPackageName () gir navnet på pakken der klassen bor:

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

I utgangspunktet, extractPackageName trekker ut alt som gikk foran den siste prikken i det fullt kvalifiserte klassenavnet. Den foregående informasjonen er tilfeldigvis navnet på pakken.

Merk: Du kan kommentere / oppheve kommentaren til pakkeuttalelsen øverst på StackTrace.java for å utforske forskjellen mellom å kjøre eksempelprogrammet i standardpakken uten navn mot å kjøre den i boo.hoo pakke. For eksempel, når det ikke er kommentert, vises skjermbildet til det øverste bunkeelementet for samtalen til bar () fra foo () fra hoved() skal se slik ut:

Filnavn: StackTrace.java Linjenummer: 227 Pakkens navn: boo.hoo Fullt klassenavn: boo.hoo.StackTrace Enkelt klassenavn: StackTrace Unmunged klassenavn: StackTrace Direkte klassenavn: StackTrace Metodenavn: bar Innfødt metode ?: falsk toString ( ): boo.hoo.StackTrace.bar (StackTrace.java:227) 

Alternativt, hvis du kommenterer pakkeerklæringen, bør ovennevnte stakkelement ligne på dette:

Filnavn: StackTrace.java Linjenummer: 227 Pakkens navn: [standardpakke] Fullt klassenavn: StackTrace Enkelt klassenavn: StackTrace Unmunged klassenavn: StackTrace Direkte klassenavn: StackTrace Metodenavn: bar Innfødt metode ?: falsk toString (): StackTrace .bar (StackTrace.java:227) 

Kan klassenavn noen gang være enkle?

Den neste hjelpermetoden vi bruker er extractSimpleClassName (). Som du ser er resultatene av denne metoden ikke nødvendigvis enkle, men jeg vil tydelig skille dette forenklede klassenavnet fra det fullt kvalifiserte klassenavnet.

I utgangspunktet, extractSimpleClassName () komplement extractPackageName ():

 offentlig statisk String extractSimpleClassName (String fullClassName) ("" .equals (fullClassName))) return ""; int lastDot = fullClassName.lastIndexOf ('.'); hvis (0> lastDot) returnerer fullClassName; returner fullClassName.substring (++ lastDot); 

Med andre ord, extractSimpleClassName () returnerer alt etter den siste prikken (.) fra det fullt kvalifiserte klassenavnet. For eksempel fra samme samtale til bar () over ser vi at det enkle klassenavnet er rettferdig StackTrace, uansett om koden er en del av standardpakken eller en navngitt pakke.

Vi får mer interessante resultater når vi retter oppmerksomheten mot nestede klasser. I eksempelprogrammet opprettet jeg to nivåer av nestede, navngitte klasser (FirstNested og FirstNested.SecondNested) sammen med en ekstra, anonym indre klasse (inni FirstNested.SecondNested).

Hele nestede bruken starter med:

 offentlig StackTrace (boolsk na) {StackTrace.FirstNested nestet = ny StackTrace.FirstNested (); } 

Merk at den boolske parameteren (na) betyr ingenting. Jeg har nettopp lagt til det siden andre konstruktører må skilles.

Her er de nestede klassene:

 offentlig klasse FirstNested {public FirstNested () {StackTrace.displayStackTraceInformation (new Throwable ()); StackTrace.FirstNested.SecondNested yan = ny StackTrace.FirstNested.SecondNested (); System.out.println ("Dumping from inside hogwash ():"); yan.hogwash (); } offentlig klasse 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 ()); }}; // Avslutning på anonym medlemsklasse. whacked.whack (); } // Slutt på hogwash (). } // Slutten på FirstNested.SecondNexted medlemsklasse. } // Slutten på FirstNested-medlemsklassen. 

Det øverste bunkeelementet for SecondNestedkonstruktøren ser slik ut:

Filnavn: StackTrace.java Linjenummer: 267 Pakkens navn: boo.hoo Fullt klassenavn: boo.hoo.StackTrace $ FirstNested $ SecondNested Enkelt klassenavn: StackTrace $ FirstNested $ SecondNested Unmunged class name: StackTrace.FirstNested.SecondNested Direct class name: SecondNested Method name: Native method ?: false toString (): boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:267) 

Du kan se at det enkle klassenavnet ikke er så enkelt i dette tilfellet. De nestede klassene skilles fra de nestede klassene på høyere nivå og fra den øverste klassen ved å bruke dollartegnet ($). Så teknisk sett er det "enkle" navnet på den nest nestede klassen StackTrace $ FirstNested $ SecondNested.

Jeg har gitt unmungeSimpleClassName () metode for å erstatte dollartegnene med perioder for fullstendighet.

Siden jeg er sta, ønsket jeg fortsatt å få det virkelig enkle klassenavnet, så jeg opprettet extractDirectClassName ():

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