Ohjelmointi

Poikkeukset Javassa, osa 2: Lisäominaisuudet ja -tyypit

JDK 1.0 esitteli kehyksen kieliominaisuuksista ja kirjastotyypeistä käsiteltäväksi poikkeuksia, jotka poikkeavat odotetusta ohjelmakäyttäytymisestä. Tämän opetusohjelman alkupuoliskolla käsiteltiin Javan poikkeusten käsittelyn perusominaisuudet. Tämä toinen puolisko esittelee JDK 1.0: n ja sen seuraajien edistyneemmät ominaisuudet: JDK 1.4, JDK 7 ja JDK 9. Opettele ennakoimaan ja hallitsemaan poikkeuksia Java-ohjelmissasi käyttämällä lisäominaisuuksia, kuten pinojälkiä, syitä ja poikkeusketjua, kokeile -resursseilla, multi-catch, viimeinen heitto ja pino kävely.

Huomaa, että tämän opetusohjelman koodiesimerkit ovat yhteensopivia JDK 12: n kanssa.

lataa Hanki koodi Lataa lähdekoodi esimerkiksi sovelluksiin tässä opetusohjelmassa. Luonut Jeff Friesen JavaWorldille.

Poikkeusten käsittely malleissa JDK 1.0 ja 1.4: Pinon jäljet

Jokainen JVM lanka (toteutuspolku) liittyy a pino joka luodaan, kun ketju luodaan. Tämä tietorakenne on jaettu kehykset, jotka ovat menetelmäkutsuihin liittyviä tietorakenteita. Tästä syystä kutakin langan pinoa kutsutaan usein nimellä method-call -pino.

Uusi kehys luodaan aina, kun menetelmää kutsutaan. Kukin kehys tallentaa paikalliset muuttujat, parametrimuuttujat (joissa on menetelmälle välitetyt argumentit), tiedot soittomenetelmään palaamiseksi, paluuarvon tallennuspaikat, tiedot, joista on hyötyä poikkeuksen lähettämisessä, ja niin edelleen.

A pinon jäljitys (tunnetaan myös nimellä pino takaisinjälki) on raportti aktiivisista pinokehyksistä tiettynä ajankohtana ketjun suorituksen aikana. Java Heitettävä luokka (luokassa java.lang package) tarjoaa menetelmiä pinon jäljityksen tulostamiseksi, pinon jäljen täyttämiseksi ja pinon jäljityksen elementtien käyttämiseksi.

Pino-jäljen tulostaminen

Kun heittää lause heittää heitettävän, se etsii ensin sopivaa saada kiinni esto toteutusmenetelmässä. Jos sitä ei löydy, se avaa metodi-kutsupinon etsimällä lähintä saada kiinni lohko, joka pystyy käsittelemään poikkeuksen. Jos sitä ei löydy, JVM päättyy sopivalla viestillä. Harkitse listaa 1.

Listaus 1. PrintStackTraceDemo.java (versio 1)

tuo java.io.IOException; public class PrintStackTraceDemo {public static void main (String [] args) heittää IOException {heittää uuden IOException (); }}

Luettelossa 1 olevan keksitty esimerkki luo a java.io.IOException esine ja heittää tämän kohteen ulos main () menetelmä. Koska main () ei käsittele tätä heitettävää, ja koska main () on ylätason menetelmä, JVM päättyy sopivalla viestillä. Tälle sovellukselle näet seuraavan viestin:

Poikkeus säikeessä "main" java.io.IOException at PrintStackTraceDemo.main (PrintStackTraceDemo.java:7)

JVM lähettää tämän viestin soittamalla Heitettäväon void printStackTrace () menetelmä, joka tulostaa pinon jäljen kutsumiselle Heitettävä objekti normaalissa virtavirrassa. Ensimmäinen rivi näyttää heitettävien kutsumisen tuloksen toString () menetelmä. Seuraava rivi näyttää aiemmin tallentamat tiedot fillInStackTrace () (keskustellaan pian).

Muita tulostuspinon jäljitysmenetelmiä

Heitettäväon ylikuormitettu void printStackTrace (PrintStream ps) ja void printStackTrace (PrintWriter pw) metodit tuottavat pinon jäljityksen määritettyyn streamiin tai kirjoittajaan.

Pino jäljittää paljastaa lähdetiedoston ja rivinumeron, jossa heitettävä luotiin. Tässä tapauksessa se luotiin linjan 7 riville PrintStackTrace.java lähdetiedosto.

Voit vedota printStackTrace () suoraan, tyypillisesti a saada kiinni lohko. Harkitse esimerkiksi toista versiota PrintStackTraceDemo sovellus.

Listaus 2. PrintStackTraceDemo.java (versio 2)

tuo java.io.IOException; public class PrintStackTraceDemo {public static void main (String [] args) heittää IOException {try {a (); } catch (IOException ioe) {ioe.printStackTrace (); }} staattinen void a () heittää IOException {b (); } staattinen void b () heittää IOException {heittää uuden IOException (); }}

Listaus 2 paljastaa a main () menetelmä, joka kutsuu menetelmää a (), joka kutsuu menetelmää b (). Menetelmä b () heittää an IOException objekti JVM: lle, joka kelaa menetelmä-kutsupinon, kunnes se löytää main ()on saada kiinni lohko, joka voi käsitellä poikkeusta. Poikkeus hoidetaan vetoamalla printStackTrace () heitettävällä. Tämä menetelmä tuottaa seuraavan tuotoksen:

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

printStackTrace () ei lähetä ketjun nimeä. Sen sijaan se vetoaa toString () heitettävällä palauttamaan heitettävän luokan pätevyys (java.io.IOException), joka tulostetaan ensimmäisellä rivillä. Sitten se tuottaa metodi-kutsuhierarkian: viimeksi kutsuttu menetelmä (b ()) on yläosassa ja main () on alareunassa.

Mitä viivaa pinon jäljitys tunnistaa?

Pinon jäljitys tunnistaa linjan, johon heitettävyys luodaan. Se ei tunnista linjaa, johon heitettävä heitetään (via heittää), ellei heitettävää heitetä samalla linjalla, missä se on luotu.

Pinon jäljen täyttäminen

Heitettävä julistaa a Heitettävä täyttöInStackTrace () menetelmä, joka täyttää suorituspinon jäljityksen. Vetoamalla Heitettävä objekti, se tallentaa tietoja nykyisen säikeen pinokehysten nykyisestä tilasta. Harkitse listaa 3.

Listaus 3. FillInStackTraceDemo.java (versio 1)

tuo java.io.IOException; public class FillInStackTraceDemo {public static void main (String [] args) heittää IOException {try {a (); } catch (IOException ioe) {ioe.printStackTrace (); System.out.println (); heittää (IOException) ioe.fillInStackTrace (); }} staattinen void a () heittää IOExceptionin {b (); } staattinen void b () heittää IOException {heittää uuden IOException (); }}

Tärkein ero luetteloiden 3 ja 2 välillä on saada kiinni lohkon heittää (IOException) ioe.fillInStackTrace (); lausunto. Tämä lause korvaa ioepinon jäljitys, jonka jälkeen heitettävä heitetään uudelleen. Ota huomioon tämä tulos:

java.io.IOException osoitteessa FillInStackTraceDemo.b (FillInStackTraceDemo.java:26) osoitteessa FillInStackTraceDemo.a (FillInStackTraceDemo.java:21) FillInStackTraceDemo.main (FillInStackTrace FillInStackTraceDemo.main (FillInStackTraceDemo.java:15)

Sen sijaan, että toistettaisiin alkuperäinen pino jäljitys, joka tunnistaa sijainnin, jossa IOException objekti luotiin, toinen pinon jäljitys paljastaa kohteen ioe.fillInStackTrace ().

Heitettävät rakentajat ja fillInStackTrace ()

Jokainen Heitettävärakentajat vetoavat fillInStackTrace (). Seuraava konstruktori (esitelty JDK 7: ssä) ei kuitenkaan käytä tätä menetelmää, kun ohitat väärä että writableStackTrace:

Heitettävä (merkkijono-viesti, heitettävä syy, looginen käytössäSuppression, looginen kirjoitettavaStackTrace)

fillInStackTrace () kutsuu natiivimenetelmän, joka kulkee nykyisen säikeen metodikutsupinossa alaspäin pinon jäljityksen rakentamiseksi. Tämä kävely on kallista ja voi vaikuttaa suorituskykyyn, jos se tapahtuu liian usein.

Jos törmäät tilanteeseen (johon mahdollisesti liittyy upotettu laite), jossa suorituskyky on kriittinen, voit estää pinon jäljityksen rakentamisen ohittamalla fillInStackTrace (). Tutustu luetteloon 4.

Listaus 4. FillInStackTraceDemo.java (versio 2)

{public static void main (String [] args) heittää NoStackTraceException {try {a (); } catch (NoStackTraceException nste) {nste.printStackTrace (); }} staattinen void a () heittää NoStackTraceException {b (); } staattinen void b () heittää NoStackTraceException {heittää uuden NoStackTraceException (); }} luokka NoStackTraceException laajentaa poikkeusta {@Override public synchronized Throwable fillInStackTrace () {return this; }}

Listaus 4 esittelee NoStackTraceException. Tämä mukautettu tarkistettu poikkeusluokka ohittaa fillInStackTrace () palata Tämä - viittaus vetoamiseen Heitettävä. Tämä ohjelma tuottaa seuraavan tuotoksen:

NoStackTraceException

Kommentoi tärkeintä fillInStackTrace () menetelmä ja huomaat seuraavan tuloksen:

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

Pääsy pinon jäljityselementteihin

Joskus sinun on käytettävä pinon jäljityselementtejä, jotta voit poimia tarvittavat tiedot kirjaamiseen, resurssivuodon lähteen tunnistamiseen ja muihin tarkoituksiin. printStackTrace () ja fillInStackTrace () menetelmät eivät tue tätä tehtävää, mutta JDK 1.4 esiteltiin java.lang.StackTraceElement ja sen menetelmät tähän tarkoitukseen.

java.lang.StackTraceElement luokka kuvaa elementtiä, joka edustaa pinon kehystä pinon jäljityksessä. Sen menetelmiä voidaan käyttää palauttamaan täysin pätevä nimi luokalle, joka sisältää suorituspisteen, jota tämä pinon hivenaine edustaa, sekä muita hyödyllisiä tietoja. Tässä ovat päämenetelmät:

  • Merkkijono getClassName () palauttaa luokan täsmällisen nimen, joka sisältää suorituspisteen, jota tämä pinon jäljityselementti edustaa.
  • Merkkijono getFileName () palauttaa lähdetiedoston nimen, joka sisältää tämän pinon jäljityselementin esittämän suorituspisteen.
  • int getLineNumber () palauttaa lähdeviivan rivinumeron, joka sisältää suorituspisteen, jota tämä pinon jäljityselementti edustaa.
  • Merkkijono getMethodName () palauttaa menetelmän nimen, joka sisältää suorituspisteen, jota tämä pino jäljittää.
  • looginen isNativeMethod () palaa totta kun menetelmä, joka sisältää tämän pinon hivenaineen edustaman suorituspisteen, on natiivi menetelmä.

JDK 1.4 esitteli myös StackTraceElement [] getStackTrace () menetelmä java.lang.Thread ja Heitettävä luokat. Tämä menetelmä palauttaa vastaavasti joukon pinon jäljityselementtejä, jotka edustavat kutsuvan säikeen pino-dumpia, ja tarjoaa ohjelmallisen pääsyn pinon jäljitystietoihin, jotka printStackTrace ().

Listaus 5 osoittaa PinoTraceElement ja getStackTrace ().

Listaus 5. StackTraceElementDemo.java (versio 1)

tuo java.io.IOException; public class StackTraceElementDemo {public static void main (String [] args) heittää IOException {try {a (); } catch (IOException ioe) {StackTraceElement [] stackTrace = ioe.getStackTrace (); for (int i = 0; i <stackTrace.length; i ++) {System.err.println ("Poikkeus heitetty" + stackTrace [i] .getMethodName () + "luokassa" + stackTrace [i] .getClassName () + "rivillä" + stackTrace [i] .getLineNumber () + "tiedoston" + stackTrace [i] .getFileName ()); System.err.println (); }}} staattinen void a () heittää IOExceptionin {b (); } staattinen void b () heittää IOException {heittää uuden IOException (); }}

Kun suoritat tämän sovelluksen, huomaat seuraavan tuloksen:

Poikkeus heitetään luokan B StackTraceElementDemo-tiedostosta StackTraceElementDemo.java luokan 33 StackTraceElementDemo-luokan luokan StackTraceElementDemo.java sivulta 28. StackTraceElementDemo.java Poikkeus luokan StackTraceElementDemo riviltä StackTraceElementDemo tiedoston StackTraceElementDemo riviltä 9. Tiedon StackTraceElementDemo rivillä 9.

Lopuksi JDK 1.4 esitteli setStackTrace () menetelmä Heitettävä. Tämä menetelmä on suunniteltu käytettäväksi RPC-kehyksissä ja muissa kehittyneissä järjestelmissä, jolloin asiakas voi ohittaa fillInStackTrace () kun heittokyky on rakennettu.

Olin aiemmin osoittanut, kuinka ohittaa fillInStackTrace () estämään pinon jäljityksen muodostumisen. Sen sijaan voit asentaa uuden pinon jäljityksen käyttämällä PinoTraceElement ja setStackTrace (). Luo joukko PinoTraceElement objektit alustetaan seuraavan konstruktorin kautta ja välitetään tämä taulukko setStackTrace ():

StackTraceElement (Merkkijonon deklarointiluokka, String methodName, String-tiedoston nimi, int lineNumber)

Listaus 6 osoittaa PinoTraceElement ja setStackTrace ().