Programmering

Undtagelser i Java, del 2: Avancerede funktioner og typer

JDK 1.0 introducerede en ramme med sprogfunktioner og bibliotektyper til håndtering undtagelser, som er afvigelser fra forventet programadfærd. Den første halvdel af denne vejledning dækkede Java's grundlæggende undtagelseshåndteringsfunktioner. Denne anden halvdel introducerer mere avancerede funktioner, der leveres af JDK 1.0 og dens efterfølgere: JDK 1.4, JDK 7 og JDK 9. Lær hvordan du kan forudse og styre undtagelser i dine Java-programmer ved hjælp af avancerede funktioner såsom stakkspor, årsager og undtagelseskæder, prøv -med ressourcer, multi-fangst, sidste genkast og stakgang.

Bemærk, at kodeeksempler i denne vejledning er kompatible med JDK 12.

download Hent koden Download kildekoden for eksempel applikationer i denne vejledning. Oprettet af Jeff Friesen til JavaWorld.

Undtagelseshåndtering i JDK 1.0 og 1.4: Stakspor

Hver JVM tråd (en eksekveringssti) er forbundet med en stak der oprettes, når tråden oprettes. Denne datastruktur er opdelt i rammer, som er datastrukturer forbundet med metodeopkald. Af denne grund omtales hver tråds stak ofte som en metode-opkald stak.

En ny ramme oprettes hver gang en metode kaldes. Hver ramme gemmer lokale variabler, parametervariabler (som indeholder argumenter, der sendes til metoden), information til returnering til opkaldsmetoden, plads til lagring af en returværdi, information, der er nyttig til afsendelse af en undtagelse osv.

EN stak spor (også kendt som en stack backtrace) er en rapport om de aktive stabelrammer på et bestemt tidspunkt under udførelsen af ​​en tråd. Java'er Kan kastes klasse (i java.lang pakke) indeholder metoder til at udskrive et stakkspor, udfylde et stackspor og få adgang til et stacksporings elementer.

Udskrivning af et stakspor

Når kaste udsagn kaster et kastbart, det ser først efter en passende fangst blok i udførelsesmetoden. Hvis den ikke findes, afvikler den metoden-opkaldstakken på udkig efter den nærmeste fangst blok, der kan håndtere undtagelsen. Hvis den ikke findes, afsluttes JVM med en passende besked. Overvej at liste 1.

Liste 1. PrintStackTraceDemo.java (version 1)

importere java.io.IOException; public class PrintStackTraceDemo {public static void main (String [] args) kaster IOException {throw new IOException (); }}

Fortegnelse 1's konstruerede eksempel skaber en java.io.IOUndtagelse objekt og smider dette objekt ud af hoved () metode. Fordi hoved () håndterer ikke dette kastbare, og fordi hoved () er metoden på øverste niveau, afsluttes JVM med en passende besked. For denne applikation ser du følgende meddelelse:

Undtagelse i tråden "main" java.io.IOUndtagelse på PrintStackTraceDemo.main (PrintStackTraceDemo.java:7)

JVM sender denne besked ved at ringe Kan kastes's ugyldig printStackTrace () metode, der udskriver et stakspor til påkaldelsen Kan kastes objekt på standardfejlstrømmen. Den første linje viser resultatet af påkaldelse af kastbare toString () metode. Den næste linje viser data, der tidligere er registreret af fillInStackTrace () (diskuteret kort).

Yderligere sporingsmetoder til udskrivningsstak

Kan kasteser overbelastet ugyldig printStackTrace (PrintStream ps) og ugyldig printStackTrace (PrintWriter pw) metoder output stacksporingen til den specificerede stream eller forfatter.

Stacksporingen afslører kildefilen og linjenummeret, hvor kastet blev oprettet. I dette tilfælde blev det oprettet på linje 7 i PrintStackTrace.java kildefil.

Du kan påberåbe dig printStackTrace () direkte, typisk fra en fangst blok. Overvej f.eks. En anden version af PrintStackTraceDemo Ansøgning.

Liste 2. PrintStackTraceDemo.java (version 2)

importere java.io.IOException; offentlig klasse PrintStackTraceDemo {offentlig statisk ugyldigt hoved (String [] args) kaster IOException {prøv {a (); } fange (IOException ioe) {ioe.printStackTrace (); }} statisk tomrum a () kaster IOException {b (); } statisk tomrum b () kaster IOException {kast nyt IOException (); }}

Liste 2 afslører en hoved () metode, der kalder metode en(), der kalder metode b (). Metode b () kaster en IOUndtagelse modsætter sig JVM'en, som afvikler metodekaldstakken, indtil den finder hoved ()'s fangst blok, som kan håndtere undtagelsen. Undtagelsen håndteres ved at påberåbe sig printStackTrace () på den kastbare. Denne metode genererer følgende output:

java.io.IOException på PrintStackTraceDemo.b (PrintStackTraceDemo.java:24) på ​​PrintStackTraceDemo.a (PrintStackTraceDemo.java:19) på PrintStackTraceDemo.main (PrintStackTraceDemo.java:9)

printStackTrace () udsender ikke trådens navn. I stedet påberåber det sig toString () på kastbart for at returnere kastbart fuldt kvalificeret klassenavn (java.io.IOUndtagelse), som sendes ud på første linje. Derefter udsender metoden opkaldshierarkiet: den senest kaldte metode (b ()) er øverst og hoved () er i bunden.

Hvilken linje identificerer stakksporingen?

Staksporingen identificerer den linje, hvor en kastbar oprettes. Det identificerer ikke den linje, hvor kastbar kastes (via kaste), medmindre den kastbare kastes på den samme linje, hvor den er oprettet.

Udfyldning af et stakspor

Kan kastes erklærer en Kastbar fillInStackTrace () metode, der udfylder sporet af udførelsesstakken. I påkaldelsen Kan kastes objekt registrerer den information om den aktuelle tilstand for den aktuelle tråds stabelrammer. Overvej liste 3.

Liste 3. FillInStackTraceDemo.java (version 1)

importere java.io.IOException; offentlig klasse FillInStackTraceDemo {offentlig statisk ugyldig hoved (String [] args) kaster IOException {prøv {a (); } fange (IOException ioe) {ioe.printStackTrace (); System.out.println (); kast (IOException) ioe.fillInStackTrace (); }} statisk tomrum a () kaster IOException {b (); } statisk tomrum b () kaster IOException {kast nyt IOException (); }}

Den største forskel mellem Listing 3 og Listing 2 er fangst blokere kast (IOException) ioe.fillInStackTrace (); udmelding. Denne erklæring erstatter ioestakspor, hvorefter kastet kastes igen. Du skal overholde dette output:

java.io.IOException på FillInStackTraceDemo.b (FillInStackTraceDemo.java:26) på FillInStackTraceDemo.a (FillInStackTraceDemo.java:21) ved FillInStackTraceDemo.main (FillInStackTraceDemo.java:9) Excision java:9 FillInStackTraceDemo.main (FillInStackTraceDemo.java:15)

I stedet for at gentage det oprindelige stakspor, som identificerer det sted, hvor IOUndtagelse objekt blev oprettet, afslører den anden staksporing placeringen af ioe.fillInStackTrace ().

Kastbare konstruktører og fillInStackTrace ()

Hver af Kan kastes's konstruktører påberåber sig fillInStackTrace (). Følgende konstruktør (introduceret i JDK 7) påberåber sig imidlertid ikke denne metode, når du passerer falsk til writableStackTrace:

Kan kastes (streng besked, kastbar årsag, boolsk mulig undertrykkelse, boolsk skrivbar stakspor)

fillInStackTrace () påberåber en indfødt metode, der går ned i den aktuelle tråds metode-opkaldsstak for at oprette staksporet. Denne gåtur er dyr og kan påvirke ydeevnen, hvis den forekommer for ofte.

Hvis du løber ind i en situation (måske involverer en indlejret enhed), hvor ydeevne er kritisk, kan du forhindre, at stack-sporet bygges ved at tilsidesætte fillInStackTrace (). Tjek Listing 4.

Liste 4. FillInStackTraceDemo.java (version 2)

{public static void main (String [] args) kaster NoStackTraceException {prøv {a (); } fange (NoStackTraceException nste) {nste.printStackTrace (); }} statisk ugyldighed a () kaster NoStackTraceException {b (); } statisk tomrum b () kaster NoStackTraceException {kast nyt NoStackTraceException (); }} klasse NoStackTraceException udvider Undtagelse {@ Override offentlig synkroniseret Throwable fillInStackTrace () {returner dette; }}

Listing 4 introducerer NoStackTraceException. Denne brugerdefinerede kontrollerede undtagelsesklasse tilsidesætter fillInStackTrace () at vende tilbage det her - en henvisning til påberåbelse Kan kastes. Dette program genererer følgende output:

NoStackTraceException

Kommenter den overordnede fillInStackTrace () metode, og du vil observere følgende output:

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

Adgang til en stakksporelementer

Til tider er du nødt til at få adgang til et stakksporelement for at udtrække de nødvendige oplysninger til logning, identificere kilden til en ressourcelækage og andre formål. Det printStackTrace () og fillInStackTrace () metoder understøtter ikke denne opgave, men JDK 1.4 blev introduceret java.lang.StackTraceElement og dens metoder til dette formål.

Det java.lang.StackTraceElement klasse beskriver et element, der repræsenterer en stakramme i et stakspor. Dens metoder kan bruges til at returnere det fuldt kvalificerede navn på klassen, der indeholder det eksekveringspunkt, der er repræsenteret af dette stack-sporelement sammen med andre nyttige oplysninger. Her er de vigtigste metoder:

  • String getClassName () returnerer det fuldt kvalificerede navn på klassen, der indeholder det udførelsespunkt, der er repræsenteret af dette stack-sporelement.
  • String getFileName () returnerer navnet på kildefilen, der indeholder det udførelsespunkt, der er repræsenteret af dette stak-sporelement.
  • int getLineNumber () returnerer linjenummeret på kildelinjen, der indeholder eksekveringspunktet repræsenteret af dette stak-sporelement.
  • String getMethodName () returnerer navnet på metoden, der indeholder det eksekveringspunkt, der er repræsenteret af dette stack-sporelement.
  • boolsk isNativeMethod () vender tilbage rigtigt når metoden, der indeholder udførelsespunktet, der er repræsenteret af dette stakksporelement, er en indfødt metode.

JDK 1.4 introducerede også StackTraceElement [] getStackTrace () metode til java.lang.Tråd og Kan kastes klasser. Denne metode returnerer henholdsvis en matrix af stakssporelementer, der repræsenterer den påkaldende tråds stakdump, og giver programmatisk adgang til stakssporingsinformationen, der udskrives af printStackTrace ().

Liste 5 viser StackTraceElement og getStackTrace ().

Liste 5. StackTraceElementDemo.java (version 1)

importere java.io.IOException; offentlig klasse StackTraceElementDemo {offentlig statisk ugyldig hoved (String [] args) kaster IOException {prøv {a (); } fange (IOException ioe) {StackTraceElement [] stackTrace = ioe.getStackTrace (); for (int i = 0; i <stackTrace.length; i ++) {System.err.println ("Undtagelse kastet fra" + stackTrace [i] .getMethodName () + "i klasse" + stackTrace [i] .getClassName () + "on line" + stackTrace [i] .getLineNumber () + "for fil" + stackTrace [i] .getFileName ()); System.err.println (); }}} statisk tomrum a () kaster IOException {b (); } statisk tomrum b () kaster IOException {kast nyt IOException (); }}

Når du kører denne applikation, observerer du følgende output:

Undtagelse fra b i klasse StackTraceElementDemo på linje 33 i fil StackTraceElementDemo.java Undtagelse kastet fra en i klasse StackTraceElementDemo på linje 28 i fil StackTraceElementDemo.java Undtagelse kastet fra main i klasse StackTraceElementDemo på linje 9 i fil StackTraceElementDemo.java

Endelig introducerede JDK 1.4 setStackTrace () metode til Kan kastes. Denne metode er designet til brug af RPC-rammer (Remote Procedure Call) og andre avancerede systemer, hvilket gør det muligt for klienten at tilsidesætte standardstacksporingen, der genereres af fillInStackTrace () når en kastbar er konstrueret.

Jeg har tidligere vist, hvordan man tilsidesætter fillInStackTrace () for at forhindre, at der bygges et stakspor I stedet kunne du installere en ny stakksporing ved hjælp af StackTraceElement og setStackTrace (). Opret en matrix af StackTraceElement objekter initialiseret via følgende konstruktør, og videregive dette array til setStackTrace ():

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

Liste 6 viser StackTraceElement og setStackTrace ().