Ohjelmointi

Java-poikkeukset, osa 1: Perustiedot poikkeusten käsittelystä

Java-poikkeukset ovat kirjastotyyppejä ja kieliominaisuuksia, joita käytetään edustamaan ja käsittelemään ohjelmavirheitä. Jos olet halunnut ymmärtää, miten epäonnistuminen esitetään lähdekoodissa, olet tullut oikeaan paikkaan. Java-poikkeusten yleiskatsauksen lisäksi pääsen alkuun Java-kieliominaisuuksilla esineiden heittämiseen, epäonnistuneen koodin kokeilemiseen, heitettyjen esineiden sieppaamiseen ja Java-koodisi puhdistamiseen, kun poikkeus on heitetty.

Tämän opetusohjelman ensimmäisellä puoliskolla opit kielen perusominaisuuksista ja kirjastotyypeistä, jotka ovat olleet käytössä Java 1.0: n jälkeen. Toisella puoliskolla huomaat edistyneemmät Java-versiot.

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.

Mitä Java-poikkeukset ovat?

Epäonnistuminen tapahtuu, kun odottamaton toiminta keskeyttää Java-ohjelman normaalin toiminnan. Tämä ero tunnetaan nimellä poikkeus. Esimerkiksi ohjelma yrittää avata tiedoston lukemaan sen sisällön, mutta tiedostoa ei ole olemassa. Java luokittelee poikkeukset muutamaan tyyppiin, joten tarkastellaan kaikkia.

Tarkistetut poikkeukset

Java luokittelee ulkoisista tekijöistä (kuten puuttuvasta tiedostosta) johtuvat poikkeukset tarkistetut poikkeukset. Java-kääntäjä tarkistaa, että tällaiset poikkeukset ovat joko käsitelty (korjattu), jos ne esiintyvät tai dokumentoitu käsiteltäviksi muualla.

Poikkeuksen käsittelijät

An poikkeuksen käsittelijä on koodisarja, joka käsittelee poikkeuksen. Se kysyy asiayhteyttä - mikä tarkoittaa, että se lukee arvot, jotka on tallennettu muuttujista, jotka olivat soveltamisalaan poikkeuksen tapahtumahetkellä - käyttää sitten oppimaansa palauttamaan Java-ohjelman normaaliin käyttäytymiseen. Esimerkiksi poikkeusten käsittelijä saattaa lukea tallennetun tiedostonimen ja kehottaa käyttäjää korvaamaan puuttuvan tiedoston.

Suorituksen (tarkistamattomat) poikkeukset

Oletetaan, että ohjelma yrittää jakaa kokonaisluvun kokonaisluvulla 0. Tämä mahdottomuus kuvaa toisenlaista poikkeusta, nimittäin a ajonaikainen poikkeus. Toisin kuin tarkistetut poikkeukset, ajonaikaiset poikkeukset johtuvat tyypillisesti huonosti kirjoitetusta lähdekoodista, joten ohjelmoijan on korjattava ne. Koska kääntäjä ei tarkista, että ajonaikaisia ​​poikkeuksia käsitellään tai dokumentoidaan käsiteltäviksi muualla, voit ajatella ajonaikaista poikkeusta tarkastamaton poikkeus.

Tietoja ajonaikaisista poikkeuksista

Voit muokata ohjelmaa suorittamaan ajonaikaisen poikkeuksen, mutta lähdekoodi on parempi korjata. Suorituksenaikaiset poikkeukset syntyvät usein virheellisten argumenttien välittämisestä kirjaston menetelmille; buginen puhelukoodi tulisi korjata.

Virheet

Jotkut poikkeukset ovat erittäin vakavia, koska ne vaarantavat ohjelman kyvyn jatkaa suoritusta. Esimerkiksi ohjelma yrittää jakaa muistia JVM: stä, mutta vapaata muistia ei ole tarpeeksi pyynnön tyydyttämiseksi. Toinen vakava tilanne tapahtuu, kun ohjelma yrittää ladata luokkatiedoston a: n kautta Class.forName () menetelmä kutsu, mutta luokkatiedosto on vioittunut. Tällainen poikkeus tunnetaan nimellä virhe. Älä koskaan yritä käsitellä virheitä itse, koska JVM ei välttämättä pysty palautumaan siitä.

Lähdekoodin poikkeukset

Poikkeus voidaan esittää lähdekoodissa virhekoodi tai kuten esine. Esittelen molemmat ja näytän, miksi esineet ovat parempia.

Virhekoodit kohteisiin nähden

Ohjelmointikielet, kuten C, käyttävät kokonaislukua virhekoodit edustamaan epäonnistumista ja epäonnistumisen syitä - eli poikkeuksia. Tässä on muutama esimerkki:

if (chdir ("C: \ temp")) printf ("Ei voida vaihtaa temp-hakemistoon:% d \ n", errno); FILE * fp = fopen ("C: \ temp \ foo"); if (fp == NULL) printf ("Foo-tiedostoa ei voi avata:% d \ n", virhe);

C: t chdir () (muuta hakemistoa) -funktio palauttaa kokonaisluvun: 0 onnistumiselle tai -1 epäonnistumiselle. Samoin C: t fopen () (tiedosto auki) -toiminto palauttaa nollull-arvon osoitin (kokonaislukuosoite) a TIEDOSTO onnistumisen rakenne tai nolla (0) osoitin (edustaa vakio TYHJÄ) epäonnistumisesta. Kummassakin tapauksessa vian aiheuttaneen poikkeuksen tunnistamiseksi sinun on luettava yleinen errno muuttujan kokonaislukupohjainen virhekoodi.

Virhekoodit aiheuttavat joitain ongelmia:

  • Kokonaisluvut ovat merkityksettömiä; he eivät kuvaa niiden edustamia poikkeuksia. Esimerkiksi mitä 6 tarkoittaa?
  • Kontekstin liittäminen virhekoodiin on hankalaa. Voit esimerkiksi antaa sen tiedoston nimen, jota ei voitu avata, mutta mihin aiot tallentaa tiedoston nimen?
  • Kokonaisluvut ovat mielivaltaisia, mikä voi aiheuttaa sekaannusta lähdekoodia luettaessa. Esimerkiksi määrittelemällä if (! chdir ("C: \ temp")) (! tarkoittaa EI) sijasta if (chdir ("C: \ temp")) epäonnistumisen testaaminen on selkeämpää. 0 valittiin kuitenkin osoittamaan menestystä, ja niin if (chdir ("C: \ temp")) on määriteltävä vikatesti.
  • Virhekoodeja on liian helppo jättää huomiotta, mikä voi johtaa vikakoodiin. Ohjelmoija voisi esimerkiksi määrittää chdir ("C: \ temp"); ja sivuuttaa jos (fp == NULL) tarkistaa. Lisäksi ohjelmoijan ei tarvitse tutkia errno. Jos ei testata vikoja, ohjelma käyttäytyy virheellisesti, kun jompikumpi toiminto palauttaa vikaindikaattorin.

Näiden ongelmien ratkaisemiseksi Java omaksui uuden lähestymistavan poikkeusten käsittelyyn. Javassa yhdistämme poikkeuksia kuvaavat kohteet mekanismiin, joka perustuu näiden esineiden heittämiseen ja kiinniottamiseen. Tässä on joitain etuja käyttämällä objekteja vs. virhekoodia poikkeusten merkitsemiseen:

  • Kohde voidaan luoda luokasta, jolla on merkityksellinen nimi. Esimerkiksi, FileNotFoundException ( java.io paketti) on merkityksellisempi kuin 6.
  • Objektit voivat tallentaa kontekstin eri kenttiin. Voit esimerkiksi tallentaa objektin kenttiin viestin, tiedoston, jota ei voitu avata, viimeisimmän sijainnin, jossa jäsentäminen epäonnistui, ja / tai muita kohteita.
  • Et käytä jos lausunnot epäonnistumisen testaamiseksi. Sen sijaan poikkeusobjektit heitetään käsittelijälle, joka on erillinen ohjelmakoodista. Tämän seurauksena lähdekoodia on helpompi lukea ja vähemmän todennäköisesti buginen.

Heitettävä ja sen alaluokat

Java tarjoaa hierarkian luokista, jotka edustavat erilaisia ​​poikkeuksia. Nämä luokat juurtuvat java.lang paketti Heitettävä luokka yhdessä sen kanssa Poikkeus, Ajonaikainen poikkeusja Virhe alaluokat.

Heitettävä on perimmäinen superluokka poikkeusten osalta. Vain kohteet, jotka on luotu Heitettävä ja sen alaluokat voidaan heittää (ja myöhemmin kiinni). Tällaiset esineet tunnetaan nimellä heitettäviä.

A Heitettävä esine liittyy a yksityiskohtainen viesti joka kuvaa poikkeusta. Useita konstruktoreita, mukaan lukien alla kuvattu pari, tarjotaan a: n luomiseksi Heitettävä esine yksityiskohdan kanssa tai ilman:

  • Heitettävä () luo Heitettävä ilman yksityiskohtia. Tämä konstruktori soveltuu tilanteisiin, joissa ei ole asiayhteyttä. Esimerkiksi haluat vain tietää, että pino on tyhjä tai täynnä.
  • Heitettävä (merkkijono) luo Heitettävä kanssa viesti yksityiskohtaisena viestinä. Tämä viesti voidaan lähettää käyttäjälle ja / tai kirjautua sisään.

Heitettävä tarjoaa Merkkijono getMessage () tapa palauttaa yksityiskohtainen viesti. Se tarjoaa myös hyödyllisiä menetelmiä, jotka esitän myöhemmin.

Poikkeusluokka

Heitettävä on kaksi suoraa alaluokkaa. Yksi näistä alaluokista on Poikkeus, joka kuvaa ulkoisesta tekijästä johtuvaa poikkeusta (kuten yritystä lukea olemattomasta tiedostosta). Poikkeus ilmoittaa samat konstruktorit (identtisillä parametriluetteloilla) kuin Heitettävä, ja kukin rakentaja käyttää sitä Heitettävä vastine. Poikkeus perii Heitettävämenetelmät; se ei julista uusia menetelmiä.

Java tarjoaa monia poikkeusluokkia, jotka suoraan aliluokkaavat Poikkeus. Tässä on kolme esimerkkiä:

  • CloneNotSupportedException merkitsee yritystä kloonata objekti, jonka luokka ei toteuta Kloonattava käyttöliittymä. Molemmat tyypit ovat java.lang paketti.
  • IOException ilmoittaa, että jonkinlainen I / O-vika on tapahtunut. Tämä tyyppi sijaitsee java.io paketti.
  • ParseException ilmaisee, että tekstin jäsentämisessä on tapahtunut virhe. Tämä tyyppi löytyy java.text paketti.

Huomaa, että jokainen Poikkeus alaluokan nimi päättyy sanaan Poikkeus. Tämän käytännön avulla on helppo tunnistaa luokan tarkoitus.

Tyypillisesti alaluokka Poikkeus (tai yksi sen alaluokista) omilla poikkeusluokillasi (joiden nimien tulisi päättyä Poikkeus). Tässä on muutama mukautettu alaluokan esimerkki:

public class StackFullException laajentaa poikkeusta {} public class EmptyDirectoryException laajentaa poikkeusta {private String directoryName; public EmptyDirectoryException (String-viesti, String-hakemistonimi) {super (viesti); this.hakemistonimi = hakemistonimi; } public String getDirectoryName () {return hakemistonimi; }}

Ensimmäinen esimerkki kuvaa poikkeusluokkaa, joka ei vaadi yksityiskohtaista viestiä. Oletusarvoinen noargument-rakentaja kutsuu Poikkeus (), joka vetoaa Heitettävä ().

Toinen esimerkki kuvaa poikkeusluokkaa, jonka konstruktori vaatii yksityiskohtaisen sanoman ja tyhjän hakemiston nimen. Rakentaja vetoaa Poikkeus (merkkijono), joka vetoaa Heitettävä (merkkijono).

Kohteet, jotka on saatu heti Poikkeus tai jokin sen alaluokista (paitsi Ajonaikainen poikkeus tai yksi sen alaluokista) tarkistetaan poikkeuksina.

RuntimeException-luokka

Poikkeus on suoraan aliluokiteltu Ajonaikainen poikkeus, joka kuvaa poikkeusta, joka todennäköisesti johtuu huonosti kirjoitetusta koodista. Ajonaikainen poikkeus ilmoittaa samat konstruktorit (identtisillä parametriluetteloilla) kuin Poikkeus, ja kukin rakentaja käyttää sitä Poikkeus vastine. Ajonaikainen poikkeus perii Heitettävämenetelmät. Se ei julista uusia menetelmiä.

Java tarjoaa monia poikkeusluokkia, jotka suoraan aliluokkaavat Ajonaikainen poikkeus. Seuraavat esimerkit ovat kaikki ryhmän jäseniä java.lang paketti:

  • Aritmeettinen poikkeus merkitsee laitonta laskutoimitusta, kuten yrittää jakaa kokonaisluku 0: lla.
  • IllegalArgumentException signaali siitä, että laiton tai sopimaton argumentti on välitetty menetelmälle.
  • NullPointerException merkitsee yritystä kutsua menetelmä tai käyttää ilmentymäkenttää nollaviitteen avulla.

Objektit, jotka on saatu heti Ajonaikainen poikkeus tai yksi sen alaluokista on tarkistamattomat poikkeukset.

Virheluokka

Heitettävätoinen suora alaluokka on Virhe, joka kuvaa vakavaa (jopa epänormaalia) ongelmaa, jota järkevän sovelluksen ei pitäisi yrittää käsitellä - kuten muistin loppuminen, JVM: n pinon täyttö tai yritys yrittää ladata luokkaa, jota ei löydy. Kuten Poikkeus, Virhe julistaa identtiset rakentajat Heitettävä, perii Heitettävä: n menetelmiä, eikä ilmoita mitään omia menetelmiä.

Voit tunnistaa Virhe alaluokat sopimuksesta, jolla heidän luokkanimensä päättyvät Virhe. Esimerkkejä ovat OutOfMemoryError, LinkageErrorja StackOverflowError. Kaikki kolme tyyppiä kuuluvat ryhmään java.lang paketti.

Heittää poikkeuksia

C-kirjastofunktio ilmoittaa poikkeuskutsukoodista asettamalla yleisen errno muuttuja virhekoodiksi ja palauttaa vikakoodin. Java-menetelmä sitä vastoin heittää objektin. Tieto siitä, miten ja milloin heittää poikkeuksia, on olennainen osa tehokasta Java-ohjelmointia. Poikkeuksen heittäminen sisältää kaksi perusvaihetta:

  1. Käytä heittää lauseke heittää poikkeusobjekti.
  2. Käytä heittää lauseke kääntäjän ilmoittamiseksi.

Myöhemmissä osioissa keskitytään poikkeusten saamiseen ja siivoamiseen niiden jälkeen, mutta ensin opitaan lisää heitettävistä.

Heittolauseke

Java tarjoaa heittää lauseke heittää esine, joka kuvaa poikkeusta. Tässä on syntaksi heittää lausunto:

heittää heitettävä;

Kohde, jonka tunnistaa heitettävä on esimerkki Heitettävä tai jokin sen alaluokista. Heität kuitenkin yleensä vain esineitä, jotka on instantoitu alaluokista Poikkeus tai Ajonaikainen poikkeus. Tässä on muutama esimerkki:

heittää uusi FileNotFoundException ("tiedostoa ei löydy" + tiedostonimi); heittää uusi IllegalArgumentException ("laskemaan syötetty argumentti on alle nolla");

Heitettävä heitetään nykyisestä menetelmästä JVM: ään, joka tarkistaa tämän menetelmän sopivan käsittelijän suhteen. Jos sitä ei löydy, JVM purkaa menetelmäpuhelun pinon etsimällä lähintä kutsumenetelmää, joka pystyy käsittelemään heitettävän kuvaaman poikkeuksen. Jos se löytää tämän menetelmän, se välittää heitettävän menetelmän käsittelijälle, jonka koodi suoritetaan poikkeuksen käsittelemiseksi. Jos poikkeuksen käsittelemiseksi ei löydy menetelmää, JVM päättyy sopivalla viestillä.

Heittolauseke

Sinun on ilmoitettava kääntäjälle, kun heität valitun poikkeuksen menetelmästä. Tee tämä liittämällä a heittää lauseke menetelmän otsikkoon. Tällä lausekkeella on seuraava syntakse:

heittää checkExceptionClassName (, checkExceptionClassName)*

A heittää lauseke koostuu avainsanasta heittää jota seuraa pilkulla erotettu luettelo menetelmän ulkopuolelle jätetyistä tarkistetuista poikkeuksista. Tässä on esimerkki:

public static void main (String [] args) heittää ClassNotFoundException {if (args.pituus! = 1) {System.err.println ("käyttö: java ... classfile"); palata; } Class.forName (argumentit [0]); }

Tämä esimerkki yrittää ladata komentotiedon argumentilla tunnistetun luokkatiedoston. Jos Class.forName () ei löydä luokkatiedostoa, se heittää a java.lang.ClassNotFoundException objekti, joka on tarkistettu poikkeus.

Tarkistettu poikkeuskiista

heittää lauseke ja tarkistetut poikkeukset ovat kiistanalaisia. Monet kehittäjät vihaavat sitä, että heitä pakotetaan määrittelemään heittää tai käsittele tarkistettuja poikkeuksia. Lisätietoja tästä löytyy Ovatko tarkistetut poikkeukset hyviä vai huonoja? blogipostaus.

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