Programování

Výjimky v Javě, část 2: Pokročilé funkce a typy

JDK 1.0 představil rámec jazykových funkcí a typů knihoven pro práci s výjimky, což jsou odchylky od očekávaného chování programu. První polovina tohoto kurzu pokrývala základní možnosti zpracování výjimek v Javě. Tato druhá polovina představuje pokročilejší funkce poskytované JDK 1.0 a jeho nástupci: JDK 1.4, JDK 7 a JDK 9. Naučte se, jak předvídat a spravovat výjimky ve vašich programech Java pomocí pokročilých funkcí, jako jsou trasování zásobníku, příčiny a řetězení výjimek, zkuste -s-prostředky, multi-úlovek, závěrečné opětovné házení a kráčení zásobníku.

Všimněte si, že příklady kódu v tomto kurzu jsou kompatibilní s JDK 12.

stáhnout Získat kód Stáhněte si zdrojový kód například pro aplikace v tomto výukovém programu. Vytvořil Jeff Friesen pro JavaWorld.

Zpracování výjimek v JDK 1.0 a 1.4: Stohovací stopy

Každý JVM vlákno (cesta provedení) je spojena s zásobník který se vytvoří při vytvoření vlákna. Tato datová struktura je rozdělena na rámy, což jsou datové struktury spojené s voláním metod. Z tohoto důvodu se zásobník každého vlákna často označuje jako a zásobník volání metod.

Při každém vyvolání metody se vytvoří nový rámec. Každý snímek ukládá místní proměnné, proměnné parametrů (které obsahují argumenty předané metodě), informace pro návrat k volající metodě, prostor pro uložení návratové hodnoty, informace užitečné při odeslání výjimky atd.

A trasování zásobníku (také známý jako stack backtrace) je zpráva o aktivních rámcích zásobníku v určitém časovém okamžiku během provádění vlákna. Java Vrhací třída (v java.lang balíček) poskytuje metody pro tisk trasování zásobníku, vyplnění trasování zásobníku a přístup k prvkům trasování zásobníku.

Tisk trasování zásobníku

Když házet příkaz hodí házet, nejprve hledá vhodný chytit blok v prováděcí metodě. Pokud není nalezen, odvíjí se zásobník volání metod, který hledá nejbližší chytit blok, který zvládne výjimku. Pokud není nalezen, JVM končí vhodnou zprávou. Zvažte výpis 1.

Výpis 1. PrintStackTraceDemo.java (verze 1)

import java.io.IOException; public class PrintStackTraceDemo {public static void main (String [] args) hodí IOException {hodit novou IOException (); }}

Vymyšlený příklad seznamu 1 vytvoří a java.io.IOException objekt a vyhodí tento objekt z hlavní() metoda. Protože hlavní() nezpracovává tento házet, a protože hlavní() je metoda nejvyšší úrovně, JVM končí vhodnou zprávou. U této aplikace se zobrazí následující zpráva:

Výjimka ve vlákně "main" java.io.IOException na PrintStackTraceDemo.main (PrintStackTraceDemo.java:7)

JVM vydá tuto zprávu voláním Vrhacíje void printStackTrace () metoda, která vytiskne trasování zásobníku pro vyvolání Vrhací objekt ve standardním chybovém proudu. První řádek ukazuje výsledek vyvolání hodu toString () metoda. Následující řádek zobrazuje data dříve zaznamenaná uživatelem fillInStackTrace () (krátce diskutováno).

Další metody trasování zásobníku tisku

Vrhacíje přetížený void printStackTrace (PrintStream ps) a void printStackTrace (PrintWriter pw) metody vygenerují trasování zásobníku do zadaného streamu nebo zapisovače.

Trasování zásobníku odhalí zdrojový soubor a číslo řádku, kde byla vytvořena přehazovatelná položka. V tomto případě byl vytvořen na řádku 7 PrintStackTrace.java zdrojový soubor.

Můžete vyvolat printStackTrace () přímo, typicky z a chytit blok. Zvažte například druhou verzi PrintStackTraceDemo aplikace.

Výpis 2. PrintStackTraceDemo.java (verze 2)

import java.io.IOException; public class PrintStackTraceDemo {public static void main (String [] args) vyvolá IOException {try {a (); } catch (IOException ioe) {ioe.printStackTrace (); }} static void a () vyvolá IOException {b (); } static void b () vyvolá IOException {hodit novou IOException (); }}

Výpis 2 odhaluje a hlavní() metoda, která volá metodu A(), který volá metodu b (). Metoda b () hodí IOException namítat objekt JVM, který odvíjí zásobník volání metody, dokud nenajde hlavní()je chytit blok, který zvládne výjimku. Výjimka je zpracována vyvoláním printStackTrace () na házení. Tato metoda generuje následující výstup:

java.io.IOException at PrintStackTraceDemo.b (PrintStackTraceDemo.java:24) at PrintStackTraceDemo.a (PrintStackTraceDemo.java:19) at PrintStackTraceDemo.main (PrintStackTraceDemo.java:9)

printStackTrace () nevypíše název vlákna. Místo toho vyvolává toString () na hodu vrátit plně kvalifikovaný název třídy hodu (java.io.IOException), který je výstupem na prvním řádku. Potom odešle hierarchii volání metod: naposledy volaná metoda (b ()) je nahoře a hlavní() je dole.

Jaký řádek identifikuje trasování zásobníku?

Trasování zásobníku identifikuje řádek, kde je vytvořen házet. Neidentifikuje linku, kde je hozen házet (via házet), pokud není hozen hoden na stejný řádek, kde je vytvořen.

Vyplňování trasování zásobníku

Vrhací prohlašuje a Vrhací fillInStackTrace () metoda, která vyplní trasování zásobníku spuštění. Ve vyvolání Vrhací objekt, zaznamenává informace o aktuálním stavu rámců zásobníku aktuálního vlákna. Zvažte výpis 3.

Výpis 3. FillInStackTraceDemo.java (verze 1)

import java.io.IOException; public class FillInStackTraceDemo {public static void main (String [] args) hodí IOException {try {a (); } catch (IOException ioe) {ioe.printStackTrace (); System.out.println (); throw (IOException) ioe.fillInStackTrace (); }} static void a () vyvolá IOException {b (); } static void b () vyvolá IOException {hodit novou IOException (); }}

Hlavní rozdíl mezi seznamem 3 a seznamem 2 je chytit bloku throw (IOException) ioe.fillInStackTrace (); prohlášení. Toto prohlášení nahrazuje ioeStopa zásobníku, po které je hozená hra znovu vyhozena. Tento výstup byste měli sledovat:

java.io.IOException at FillInStackTraceDemo.b (FillInStackTraceDemo.java:26) at FillInStackTraceDemo.a (FillInStackTraceDemo.java:21) at FillInStackTraceDemo.main (FillInStackTraceDemo.java:9) FillInStackTraceDemo.main (FillInStackTraceDemo.java:15)

Místo opakování počátečního trasování zásobníku, které identifikuje umístění, kde IOException objekt byl vytvořen, druhá stopa zásobníku odhalí umístění ioe.fillInStackTrace ().

Vrhatelné konstruktory a fillInStackTrace ()

Každý z VrhacíKonstruktory vyvolávají fillInStackTrace (). Následující konstruktor (představený v JDK 7) však tuto metodu při předání nevyvolá Nepravdivé na writableStackTrace:

Throwable (řetězcová zpráva, Throwable příčina, boolean enableSuppression, boolean writableStackTrace)

fillInStackTrace () vyvolá nativní metodu, která prochází trasování zásobníku aktuálního vlákna metodou volání k vytvoření trasování zásobníku. Tato procházka je drahá a může ovlivnit výkon, pokud k ní dochází příliš často.

Pokud narazíte na situaci (možná zahrnující vložené zařízení), kde je kritický výkon, můžete zabránit vytvoření trasování zásobníku přepsáním fillInStackTrace (). Podívejte se na výpis 4.

Výpis 4. FillInStackTraceDemo.java (verze 2)

{public static void main (String [] args) hodí NoStackTraceException {try {a (); } catch (NoStackTraceException nste) {nste.printStackTrace (); }} static void a () vyvolá NoStackTraceException {b (); } static void b () hodí NoStackTraceException {hodit novou NoStackTraceException (); }} třída NoStackTraceException rozšiřuje výjimku {@Override public synchronized Throwable fillInStackTrace () {return this; }}

Výpis 4 zavádí NoStackTraceException. Tento zvyk přepíše třídu výjimek fillInStackTrace () vrátit tento - odkaz na vyvolání Vrhací. Tento program generuje následující výstup:

NoStackTraceException

Komentujte přepsání fillInStackTrace () metoda a budete sledovat následující výstup:

NoStackTraceException at FillInStackTraceDemo.b (FillInStackTraceDemo.java:22) at FillInStackTraceDemo.a (FillInStackTraceDemo.java:17) at FillInStackTraceDemo.main (FillInStackTraceDemo.java:7)

Přístup k prvkům trasování zásobníku

Občas budete muset přistupovat k prvkům trasování zásobníku, abyste mohli extrahovat podrobnosti potřebné pro protokolování, identifikaci zdroje úniku prostředků a další účely. The printStackTrace () a fillInStackTrace () metody tento úkol nepodporují, ale byla představena JDK 1.4 java.lang.StackTraceElement a jeho metody pro tento účel.

The java.lang.StackTraceElement třída popisuje prvek představující rámec zásobníku v trasování zásobníku. Jeho metody lze použít k vrácení plně kvalifikovaného názvu třídy obsahující bod spuštění představovaný tímto prvkem trasování zásobníku spolu s dalšími užitečnými informacemi. Zde jsou hlavní metody:

  • Řetězec getClassName () vrátí plně kvalifikovaný název třídy obsahující bod spuštění představovaný tímto prvkem trasování zásobníku.
  • Řetězec getFileName () vrátí název zdrojového souboru obsahujícího bod spuštění představovaný tímto prvkem trasování zásobníku.
  • int getLineNumber () vrací číslo řádku zdrojového řádku obsahujícího bod spuštění představovaný tímto prvkem trasování zásobníku.
  • Řetězec getMethodName () vrátí název metody obsahující bod spuštění představovaný tímto prvkem trasování zásobníku.
  • boolean isNativeMethod () se vrací skutečný když metoda obsahující bod spuštění představovaný tímto prvkem trasování zásobníku je nativní metoda.

JDK 1.4 také představil StackTraceElement [] getStackTrace () metoda k java.lang.Thread a Vrhací třídy. Tato metoda vrací pole prvků trasování zásobníku představujících výpis zásobníku vyvolávajícího vlákna a poskytuje programový přístup k informacím trasování zásobníku vytištěným printStackTrace ().

Výpis 5 ukazuje StackTraceElement a getStackTrace ().

Výpis 5. StackTraceElementDemo.java (verze 1)

import java.io.IOException; public class StackTraceElementDemo {public static void main (String [] args) vyvolá IOException {try {a (); } catch (IOException ioe) {StackTraceElement [] stackTrace = ioe.getStackTrace (); for (int i = 0; i <stackTrace.length; i ++) {System.err.println ("Výjimka vyvolána z" + stackTrace [i] .getMethodName () + "ve třídě" + stackTrace [i] .getClassName () + "on line" + stackTrace [i] .getLineNumber () + "souboru" + stackTrace [i] .getFileName ()); System.err.println (); }}} static void a () vyvolá IOException {b (); } static void b () vyvolá IOException {hodit novou IOException (); }}

Při spuštění této aplikace budete sledovat následující výstup:

Výjimka vyvolána z b ve třídě StackTraceElementDemo na řádku 33 souboru StackTraceElementDemo.java Výjimka vyvolána z ve třídě StackTraceElementDemo na řádku 28 souboru StackTraceElementDemo.java Výjimka vyvolána z hlavní třídy ve třídě StackTraceElementDemo na řádku 9 souboru StackTraceE

Nakonec JDK 1.4 představil setStackTrace () metoda k Vrhací. Tato metoda je navržena pro použití v rámcích vzdáleného volání procedur (RPC) a dalších pokročilých systémech, což klientovi umožňuje přepsat výchozí trasování zásobníku, které je generováno fillInStackTrace () když je zkonstruován házet.

Dříve jsem ukázal, jak přepsat fillInStackTrace () aby se zabránilo vytváření trasování zásobníku. Místo toho můžete nainstalovat nové trasování zásobníku pomocí StackTraceElement a setStackTrace (). Vytvořte pole StackTraceElement objekty inicializované pomocí následujícího konstruktoru a předat toto pole setStackTrace ():

StackTraceElement (String declaringClass, String methodName, String fileName, int lineNumber)

Výpis 6 ukazuje StackTraceElement a setStackTrace ().

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