Programování

Tip pro Javu 124: Sledujte své kroky v prostředí Java 1.4

Nevím jak vy, ale já opravdu ráda vím, kde jsem. Být chlapem, to jsem nikdy ztracen, ale někdy prostě nevím, kde jsem. Některá místa, například nákupní střediska, mají mapy s indikátory „Jste tady“. Podobně nám Java nyní umožňuje zjistit naši polohu pomocí systému. V tomto tipu vám ukážu, jak konzistentně a spolehlivě extrahovat tyto informace o poloze ze systému.

Pevně ​​věřím, že běhový systém by měl poskytovat dostatek metadat o samotném systému, aby programy mohly lépe rozhodovat a plnit úkoly. Java byla po určitou dobu schopna introspektovat a přemýšlet o třídách, ale doposud postrádala jednoduchou schopnost mapovat běhový kód zpět na svou pozici v souboru zdrojového kódu. Před-Java 1.4 řešení bylo ručně analyzovat trasování zásobníku výjimky. Nyní s Java 1.4 máme lepší řešení.

Rozdělit stopu zásobníku?

Alternativním řešením před shromažďováním informací o poloze 1.4 bylo ručně analyzovat výjimky printStackTrace () výstup. Zde je příklad jednoduchého trasování zásobníku:

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

Roztažení výše uvedeného kódu není velkým problémem při analýze. Ale co tohle?

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

Fuj. Co všechno ten podivný goobley-guk ve skutečnosti znamená a proč bych to proboha musel analyzovat? Je zřejmé, že systém již sleduje tyto informace o poloze, protože je schopen tyto stopy zásobníku vytvořit. Proč tedy nejsou tyto informace o poloze k dispozici přímo? S Java 1.4 to konečně je.

Kromě toho mějte na paměti, že tváří v tvář kompilátorům JIT (just-in-time) a dynamickým, optimalizujícím kompilátorům, jako je HotSpot společnosti Sun Microsystems, nemusí informace o číslech souborů a řádků existovat. Cíl „výkonu nebo poprsí“ může být jistě obtěžující.

Java 1.4 hoditelný na záchranu!

Po tolerování let stížností společnost Sun Microsystems konečně prodloužila java.lang. Hoditelné třída s getStackTrace () metoda. getStackTrace () vrací pole StackTraceElements, kde každý StackTraceElement objekt poskytuje prostředky k víceméně přímé extrakci informací o poloze.

Chcete-li získat tyto informace o mapování, stále vytvoříte a Vrhací instance v místě zájmu ve vašem kódu:

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

Tento kód umístí tento bod na začátek hlavní().

Samozřejmě je zbytečné tyto informace jen shromažďovat, aniž byste s nimi něco dělali. Pro tento tip použijeme každou základní metodu StackTraceElements extrahovat a zobrazit všechny informace, které můžeme.

Ukázkový program, StackTrace.java, ukazuje, jak extrahovat informace o poloze pomocí několika příkladů. Ke kompilaci a spuštění ukázkového programu budete potřebovat sadu SDK J2SE (Java 2 Platform, Standard Edition) 1.4 SDK.

K extrakci a zobrazení informací o mapování používá ukázkový kód pomocnou metodu, displayStackTraceInformation (), s následujícím základním idiomem použití:

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

The displayStackTraceInformation () kód je docela přímočarý:

 public static boolean displayStackTraceInformation (Throwable ex, boolean displayAll) {if (null == ex) {System.out.println ("Null stack trace reference! Bailing ..."); návrat false; } System.out.println ("Zásobník podle printStackTrace (): \ n"); např. printStackTrace (); System.out.println (""); StackTraceElement [] stackElements = ex.getStackTrace (); if (displayAll) {System.out.println ("The" + stackElements.length + "element" + ((stackElements.length == 1)? "": "s") + "trasování zásobníku: \ n" ); } else {System.out.println ("Horní prvek trasování zásobníku prvku" + stackElements.length + ": \ n"); } for (int lcv = 0; lcv <stackElements.length; lcv ++) {System.out.println ("Filename:" + stackElements [lcv] .getFileName ()); System.out.println ("Číslo řádku:" + stackElements [lcv] .getLineNumber ()); Řetězec className = stackElements [lcv] .getClassName (); Řetězec packageName = extractPackageName (className); Řetězec simpleClassName = extractSimpleClassName (className); System.out.println ("Název balíčku:" + ("" .equals (název_balíku)? "[Výchozí balíček]": název_balíku)); System.out.println ("Celý název třídy:" + název třídy); System.out.println ("Jednoduchý název třídy:" + simpleClassName); System.out.println ("Unmunged název třídy:" + unmungeSimpleClassName (simpleClassName)); System.out.println ("Přímý název třídy:" + extractDirectClassName (simpleClassName)); System.out.println ("Název metody:" + stackElements [lcv] .getMethodName ()); System.out.println ("Nativní metoda ?:" + stackElements [lcv] .isNativeMethod ()); System.out.println ("toString ():" + stackElements [lcv] .toString ()); System.out.println (""); if (! displayAll) return true; } System.out.println (""); návrat true; } // Konec displayStackTraceInformation (). 

V zásadě voláme getStackTrace () na předán Vrhacía poté procházejte jednotlivcem StackTraceElements, extrahování co největšího počtu mapovacích informací.

Všimněte si trochu krutosti zobrazit vše parametr zavádí. zobrazit vše umožňuje volajícímu webu rozhodnout, zda zobrazit nebo nezobrazit všechny StackTraceElements nebo jen nejvyšší prvek zásobníku. Ukázkový program používá zobrazit vše parametr omezit výstup na rozumnou částku.

Většina informací o trasování zásobníku je přímo užitečná. Například StackTraceElement.getMethodName () vrátí řetězec, který obsahuje název metody, while StackTraceElement.getFileName () vrací řetězec s původním zdrojovým názvem souboru. Číst StackTraceElement Javadoc pro úplný seznam metod.

Názvy tříd hodně!

Jak jste si pravděpodobně všimli, displayStackTraceInformation () kód používá několik dalších pomocných metod k oddělení hodnoty vrácené StackTraceElement.getClassName (). Tyto pomocné metody jsou potřebné, protože StackTraceElement.getClassName () vrátí plně kvalifikovaný název třídy a StackTraceElement nemá žádné jiné metody k poskytnutí podkladových částí plně kvalifikovaného názvu třídy. Dozvíme se o každé další pomocné metodě procvičováním různých příkladů použití displayStackTraceInformation ().

Výchozí vs. pojmenované balíčky

Vzhledem k plně kvalifikovanému názvu třídy extractPackageName () dává název balíčku, ve kterém je třída umístěna:

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

V podstatě, extractPackageName extrahuje vše před poslední tečkou v plně kvalifikovaném názvu třídy. Předcházející informace je název balíčku.

Poznámka: Prohlášení o balíčku můžete komentovat / odkomentovat v horní části stránky StackTrace.java prozkoumat rozdíl mezi spuštěním ukázkového programu ve výchozím nepojmenovaném balíčku a spuštěním v programu boo.hoo balík. Když je například nekomentováno, zobrazení prvku nejvyššího zásobníku pro volání bar() z foo () z hlavní() by měl vypadat takto:

Název souboru: StackTrace.java Číslo řádku: 227 Název balíčku: boo.hoo Celý název třídy: boo.hoo.StackTrace Jednoduchý název třídy: StackTrace Název nezaměněné třídy: StackTrace Přímý název třídy: StackTrace Název metody: bar Nativní metoda ?: false toString ( ): boo.hoo.StackTrace.bar (StackTrace.java:227) 

Alternativně, pokud okomentujete příkaz balíčku, měl by výše uvedený prvek zásobníku vypadat takto:

Název souboru: StackTrace.java Číslo řádku: 227 Název balíčku: [výchozí balíček] Celý název třídy: StackTrace Jednoduchý název třídy: StackTrace Název nezměněné třídy: StackTrace Přímý název třídy: StackTrace Název metody: bar Nativní metoda ?: false toString (): StackTrace .bar (StackTrace.java:227) 

Mohou být názvy tříd někdy jednoduché?

Další pomocná metoda, kterou používáme, je extractSimpleClassName (). Jak uvidíte, výsledky této metody nemusí být nutně jednoduché, ale chci jasně odlišit tento zjednodušený název třídy od plně kvalifikovaného názvu třídy.

V podstatě, extractSimpleClassName () doplňuje extractPackageName ():

 public static String extractSimpleClassName (String fullClassName) ("" .equals (fullClassName))) return ""; int lastDot = fullClassName.lastIndexOf ('.'); if (0> lastDot) return fullClassName; návrat fullClassName.substring (++ lastDot); 

Jinými slovy, extractSimpleClassName () vrátí vše po poslední tečka (.) z plně kvalifikovaného názvu třídy. Například ze stejného volání na bar() výše vidíme, že jednoduchý název třídy je spravedlivý StackTrace, ať už je či není kód součástí výchozího balíčku nebo pojmenovaného balíčku.

Zajímavější výsledky získáme, když obrátíme naši pozornost k vnořeným třídám. V ukázkovém programu jsem vytvořil dvě úrovně vnořených pojmenovaných tříd (FirstNested a FirstNested.SecondNested) spolu s další anonymní vnitřní třídou (uvnitř FirstNested.SecondNested).

Celé vnořené použití začíná:

 public StackTrace (boolean na) {StackTrace.FirstNested vnořený = nový StackTrace.FirstNested (); } 

Všimněte si, že booleovský parametr (na) nic neznamená. Právě jsem to přidal, protože je třeba rozlišovat ostatní konstruktory.

Zde jsou vnořené třídy:

 veřejná třída FirstNested {public FirstNested () {StackTrace.displayStackTraceInformation (new Throwable ()); StackTrace.FirstNested.SecondNested yan = nový StackTrace.FirstNested.SecondNested (); System.out.println ("Dumping from inside hogwash ():"); yan.hogwash (); } veřejná třída 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 ()); }}; // Konec anonymní členské třídy. whacked.whack (); } // Konec hogwash (). } // Konec členské třídy FirstNested.SecondNexted. } // Konec třídy člena FirstNested. 

Nejvyšší prvek zásobníku pro Druhý vnořenýKonstruktor vypadá takto:

Název souboru: StackTrace.java Číslo řádku: 267 Název balíčku: boo.hoo Celý název třídy: boo.hoo.StackTrace $ FirstNested $ SecondNested Jednoduchý název třídy: StackTrace $ FirstNested $ SecondNested Název nemístné třídy: StackTrace.FirstNested.SecondNested Přímý název třídy: Název metody SecondNested: Nativní metoda ?: false toString (): boo.hoo.StackTrace $ FirstNested $ SecondNested. (StackTrace.java:267) 

Vidíte, že jednoduchý název třídy není v tomto případě tak jednoduchý. Vnořené třídy se odlišují od vnořených tříd vyšší úrovně a od třídy nejvyšší úrovně pomocí znaku znaku dolaru ($). Technicky tedy „jednoduchý“ název druhé vnořené třídy je StackTrace $ FirstNested $ SecondNested.

Poskytl jsem unmungeSimpleClassName () metoda nahrazení dolarových znaků periodami pro úplnost.

Jelikož jsem tvrdohlavý, stále jsem chtěl získat skutečně jednoduchý název třídy, tak jsem vytvořil extractDirectClassName ():

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