Ohjelmointi

Java-vinkki 124: Seuraa vaiheitasi Java 1.4: ssä

En tiedä sinusta, mutta haluan todella tietää missä olen. Koska olen kaveri, olen ei koskaan kadonnut, mutta joskus en vain tiedä missä olen. Joissakin paikoissa, kuten ostoskeskuksissa, on kartat "Olet tässä" -merkinnöillä. Samoin Java antaa meidän nyt selvittää sijaintimme järjestelmän avulla. Tässä vinkissä näytän sinulle, kuinka nämä sijaintitiedot saadaan järjestelmästä johdonmukaisella ja luotettavalla tavalla.

Uskon vakaasti, että ajonaikaisen järjestelmän tulisi antaa riittävästi metatietoja itse järjestelmästä, jotta ohjelmat voivat tehdä parempia päätöksiä ja suorittaa tehtäviä. Java on pystynyt tutkimaan ja pohtimaan luokkia jonkin aikaa, mutta toistaiseksi sillä ei ole ollut yksinkertaista kykyä kartoittaa ajonaikaiskoodia takaisin sijaintiinsa lähdekooditiedostossa. Java 1.4 -versiota edeltävä ratkaisu oli jäsentää poikkeuksen pinon jäljitys manuaalisesti. Nyt meillä on Java 1.4: lla parempi ratkaisu.

Vedätkö pinon jäljen?

Java 1.4: ää edeltävä kiertotapa sijaintitietojen keräämisessä oli jäsentää poikkeukset manuaalisesti printStackTrace () ulostulo. Tässä on esimerkki yksinkertaisesta pinon jäljityksestä:

java.lang.Trrowable osoitteessa boo.hoo.StackTrace.bar (StackTrace.java:223) osoitteessa boo.hoo.StackTrace.foo (StackTrace.java:218) osoitteessa boo.hoo.StackTrace.main (StackTrace.java:54) 

Yllä olevan koodin vetäminen toisistaan ​​ei ole suuri jäsentämisongelma. Mutta entä tämä?

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

Uh. Mitä kaikki tämä outo goobley-guk todella tarkoittaa, ja miksi maan päällä minun pitäisi jäsentää se? On selvää, että järjestelmä seuraa jo kyseisiä sijaintitietoja, koska se pystyy rakentamaan nuo pinonjäljet. Joten miksi sijaintitiedot eivät ole saatavilla suoraan? No, Java 1.4: n kanssa se lopulta on.

Muista lisäksi, että JIT (just-in-time) -kääntäjien ja dynaamisten, optimoivien kääntäjien, kuten Sun Microsystemsin HotSpot, edessä tiedostoja ja rivinumeroita ei välttämättä ole. "Suorituskyvyn tai rintakuvan" tavoite voi varmasti olla häiritsevä.

Java 1.4 Heitettävissä pelastukseen!

Vuosien valitusten sietämisen jälkeen Sun Microsystems on vihdoin laajentanut java.lang.heitettävä luokan kanssa getStackTrace () menetelmä. getStackTrace () palauttaa taulukon PinoTraceElements, missä kukin PinoTraceElement object tarjoaa keinot poimia sijaintitiedot enemmän tai vähemmän suoraan.

Voit hankkia kartoitustiedot luomalla edelleen Heitettävä esimerkki koodisi kiinnostavasta kohdasta:

 // ... public static void main (String [] argumentoi) {Heitettävä ex = uusi Heitettävä (); // ... 

Tämä koodi sijoittaa kyseisen kohdan alkuun main ().

Tietysti on turhaa kerätä tietoja vain tekemättä jotain sen kanssa. Tätä vinkkiä varten käytämme kutakin PinoTraceElements poimia ja näyttää kaikki tiedot voimme.

Esimerkkiohjelma, StackTrace.java, näyttää, kuinka sijaintitiedot puretaan useilla esimerkeillä. Tarvitset J2SE (Java 2 Platform, Standard Edition) 1.4 SDK esimerkkiohjelman kokoamiseen ja suorittamiseen.

Kartoitustietojen purkamiseksi ja näyttämiseksi esimerkkikoodi käyttää auttajamenetelmää, displayStackTraceInformation (), seuraavalla peruskäyttötilanteella:

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

displayStackTraceInformation () koodi on melko yksinkertainen:

 julkinen staattinen looginen näyttöStackTraceInformation (heitettävä ex, looginen näyttöAll) {if (null == ex) {System.out.println ("Nollapinon jäljitysviite! Bailing ..."); return false; } System.out.println ("Pino printStackTrace (): n mukaan: \ n"); ex.printStackTrace (); System.out.println (""); StackTraceElement [] stackElements = ex.getStackTrace (); if (displayAll) {System.out.println ("pinon jäljityksen" + stackElements.length + "elementti" + ((stackElements.length == 1)? "": "s") + ": \ n" ); } else {System.out.println ("+ pinoelementit.length +" -elementtipinon jäljityksen ylin elementti: \ n "); } (int lcv = 0; lcv <pinoelementit.length; lcv ++) {System.out.println ("Tiedostonimi:" + pinoelementit [lcv] .getFileName ()); System.out.println ("Rivinumero:" + pinoelementit [lcv] .getLineNumber ()); Merkkijono className = pinoelementit [lcv] .getClassName (); String packageName = extractPackageName (luokanNimi); Merkkijono simpleClassName = extractSimpleClassName (luokanNimi); System.out.println ("Paketin nimi:" + ("" .equals (paketinNimi)? "[Oletuspaketti]": paketinNimi)); System.out.println ("Koko luokan nimi:" + luokan nimi); System.out.println ("Yksinkertainen luokan nimi:" + simpleClassName); System.out.println ("Luokan nimeämätön nimi:" + unmungeSimpleClassName (simpleClassName)); System.out.println ("Suora luokan nimi:" + extractDirectClassName (simpleClassName)); System.out.println ("Menetelmän nimi:" + pinoelementit [lcv] .getMethodName ()); System.out.println ("Natiivi menetelmä ?:" + pinoelementit [lcv] .isNativeMethod ()); System.out.println ("toString ():" + pinoelementit [lcv] .toString ()); System.out.println (""); jos (! displayAll) palaa tosi; } System.out.println (""); palaa tosi; } // DisplayStackTraceInformation (): n loppu. 

Pohjimmiltaan soitamme getStackTrace () sisäänpääsyssä Heitettäväja sitten silmukka yksilön läpi PinoTraceElements, poimien mahdollisimman paljon kartoitustietoja.

Huomaa, kuinka vähän matkaa displayKaikki parametri esittelee. displayKaikki antaa soittavan sivuston päättää, näytetäänkö kaikki PinoTraceElements tai vain pinon ylin elementti. Esimerkkiohjelma käyttää displayKaikki parametri rajoittaa tuotoksen kohtuulliseen määrään.

Suurin osa pinon jäljitystiedoista on suoraan hyödyllistä. Esimerkiksi StackTraceElement.getMethodName () palauttaa merkkijonon, joka sisältää menetelmän nimen, kun StackTraceElement.getFileName () palauttaa merkkijonon alkuperäisen lähdetiedostonimellä. Lue PinoTraceElement Javadoc täydellinen luettelo menetelmistä.

Luokkien nimet yllin kyllin!

Kuten luultavasti huomasit, displayStackTraceInformation () code käyttää useita muita auttajamenetelmiä erottamaan arvon, jonka palauttaa StackTraceElement.getClassName (). Näitä auttajamenetelmiä tarvitaan, koska StackTraceElement.getClassName () palauttaa luokan täysin hyväksytyn nimen ja PinoTraceElement ei ole muita menetelmiä täysin pätevän luokan nimen taustalla olevien osien toimittamiseksi. Opimme jokaisesta ylimääräisestä auttajamenetelmästä tutustumalla eri esimerkkien käyttötarkoituksiin displayStackTraceInformation ().

Oletus vs. nimetyt paketit

Kun otetaan huomioon luokan täydellinen nimi, extractPackageName () antaa sen paketin nimen, jossa luokka asuu:

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

Pohjimmiltaan, extractPackageName poimii kaiken edellisen viimeisen pisteen edeltä täysin luokitellussa luokassa. Tämä edeltävä tieto sattuu olemaan paketin nimi.

merkintä: Voit kommentoida / kommentoida paketin lausetta yläosassa StackTrace.java tutkia eroa esimerkkiohjelman suorittamisen nimettömässä oletuspaketissa sen ja sen välillä boo.hoo paketti. Esimerkiksi kommentoimatta ylimmän pinoelementin näyttö, johon soitetaan baari() alkaen foo () alkaen main () pitäisi näyttää tältä:

Tiedostonimi: StackTrace.java Linjanumero: 227 Paketin nimi: boo.hoo Koko luokan nimi: boo.hoo.StackTrace Yksinkertainen luokan nimi: StackTrace Unmunged-luokan nimi: StackTrace Suoran luokan nimi: StackTrace-menetelmän nimi: baari Native method ?: false toString ( ): boo.hoo.StackTrace.bar (StackTrace.java:227) 

Vaihtoehtoisesti, jos kommentoit pakettilauseketta, yllä olevan pinon elementin tulisi muistuttaa tätä:

Tiedostonimi: StackTrace.java Rivinumero: 227 Paketin nimi: [oletuspaketti] Koko luokan nimi: StackTrace Yksinkertainen luokan nimi: StackTrace Unmunged luokan nimi: StackTrace Suora luokan nimi: StackTrace Menetelmän nimi: bar Native method ?: false toString (): StackTrace .bar (StackTrace.java:227) 

Voivatko luokkien nimet koskaan olla yksinkertaisia?

Seuraava käyttämämme auttajamenetelmä on extractSimpleClassName (). Kuten näette, menetelmän tulokset eivät välttämättä ole yksinkertaisia, mutta haluan erottaa tämän yksinkertaistetun luokan nimen täysin hyväksytystä luokan nimestä.

Pohjimmiltaan, extractSimpleClassName () täydentää extractPackageName ():

 public static String extractSimpleClassName (String fullClassName) ("" .equals (fullClassName))) return ""; int lastDot = fullClassName.lastIndexOf ('.'); jos (0> lastDot) palauttaa fullClassName; palauta fullClassName.substring (++ lastDot); 

Toisin sanoen, extractSimpleClassName () palauttaa kaiken jälkeen viimeinen piste (.) täysin pätevästä luokan nimestä. Esimerkiksi samasta puhelusta baari() Yllä, näemme, että yksinkertainen luokan nimi on oikeudenmukainen PinoTrace, onko koodi osa oletuspakettia vai nimettyä pakettia.

Saamme mielenkiintoisempia tuloksia, kun kiinnitämme huomiomme sisäkkäisiin luokkiin. Luonut esimerkkiohjelmassa kaksi tasoa sisäkkäisiä, nimettyjä luokkia (Ensin sisäkkäin ja FirstNested.SecondNested) sekä ylimääräinen, tuntematon sisempi luokka (sisällä FirstNested.SecondNested).

Koko sisäkkäinen käyttö alkaa:

 public StackTrace (looginen na) {StackTrace.FirstNested sisäkkäin = new StackTrace.FirstNested (); } 

Huomaa, että looginen parametri (na) ei tarkoita mitään. Lisäsin sen vain, koska muut rakentajat on erotettava toisistaan.

Tässä ovat sisäkkäiset luokat:

 public class FirstNested {public FirstNested () {StackTrace.displayStackTraceInformation (uusi heitettävä ()); StackTrace.FirstNested.SecondNested yan = uusi StackTrace.FirstNested.SecondNested (); System.out.println ("Kaataminen tylypahkan sisältä ():"); yan.hogwash (); } public class SecondNested {public SecondNested () {StackTrace.displayStackTraceInformation (uusi heitettävä ()); } public void hogwash () {StackTrace.displayStackTraceInformation (uusi heitettävä ()); Whackable whacked = new Whackable () {public void whack () {StackTrace.displayStackTraceInformation (new Throwable ()); }}; // Anonyymin jäsenluokan loppu. lyönyt. lyö (); } // Tylypahkan loppu (). } // FirstNested.SecondNexted-jäsenluokan loppu. } // FirstNested-jäsenluokan loppu. 

Ylin pinoelementti SecondNestedrakentaja näyttää tältä:

Tiedostonimi: StackTrace.java Linjanumero: 267 Paketin nimi: boo.hoo Koko luokan nimi: boo.hoo.StackTrace $ FirstNested $ SecondNested Yksinkertainen luokan nimi: StackTrace $ FirstNested $ SecondNested Unmunged luokan nimi: StackTrace.FirstNested.SecondNested Suora luokan nimi: SecondNested-menetelmän nimi: Native method ?: false toString (): boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:267) 

Voit nähdä, että yksinkertainen luokan nimi ei ole aivan niin yksinkertainen tässä tapauksessa. Sisäkkäiset luokat erotetaan ylemmän tason sisäkkäisistä luokista ja ylimmän luokan luokista käyttämällä dollarin merkki-merkkiä ($). Joten teknisesti toisen sisäkkäisen luokan "yksinkertainen" nimi on StackTrace $ FirstNested $ SecondNested.

Olen toimittanut unmungeSimpleClassName () menetelmä korvata dollarimerkit täydellisyysjaksoilla.

Koska olen itsepäinen, halusin silti saada todella yksinkertaisen luokan nimen, joten loin extractDirectClassName ():

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