Poikkeusten ja tapahtumien käsittely

Samankaltaiset tiedostot
815338A Ohjelmointikielten periaatteet

14. Poikkeukset 14.1

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

14. Poikkeukset 14.1

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Poikkeukset ja tietovirrat: Virhetilanteiden ja syötevirtojen käsittely

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

Olio-ohjelmointi Virhetilanteiden käsittely

Olio-ohjelmointi Poikkeusten käsittelystä. 1. Johdanto

Poikkeustenkäsittely

Ohjelmoinnin perusteet Y Python

15. Ohjelmoinnin tekniikkaa 15.1

Olio-ohjelmointi Suunnittelumallit Adapter ja Composite. 1. Adapter

JAVA-OHJELMOINTI 3 op A274615

5. HelloWorld-ohjelma 5.1

Mitä poikkeuskäsittely tarkoittaa?

15. Ohjelmoinnin tekniikkaa 15.1

8. Näppäimistöltä lukeminen 8.1

1. Omat operaatiot 1.1

8. Näppäimistöltä lukeminen 8.1

Tietojen syöttäminen ohjelmalle. Tietojen syöttäminen ohjelmalle Scanner-luokan avulla

4. Luokan testaus ja käyttö olion kautta 4.1

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Java-API, rajapinnat, poikkeukset, UML,...

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

Rinnakkaisohjelmointi kurssi. Opintopiiri työskentelyn raportti

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Sisältö. 2. Taulukot. Yleistä. Yleistä

Sisältö. 22. Taulukot. Yleistä. Yleistä

Ohjelmoinnin jatkokurssi, kurssikoe

12. Näppäimistöltä lukeminen 12.1

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Ohjelmointi 2 / 2010 Välikoe / 26.3

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. VII Suunnittelumallit Adapter ja Composite

5. HelloWorld-ohjelma 5.1

Ohjelman virheet ja poikkeusten käsittely

Olio-ohjelmointi Käyttöliittymä

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Sisällys. JAVA-OHJELMOINTI Osa 7: Abstrakti luokka ja rajapinta. Abstraktin luokan idea. Abstrakti luokka ja metodi. Esimerkki

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

ITKP102 Ohjelmointi 1 (6 op)

Java-kielen perusteet

Ohjelmointi 2 / 2008 Välikoe / Pöytätestaa seuraava ohjelma.

Sisältö. Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 6.2

7. Oliot ja viitteet 7.1

812341A Olio-ohjelmointi, IX Olioiden välisistä yhteyksistä

11/20: Konepelti auki

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

Olio-ohjelmointi Javalla

Taulukot. Jukka Harju, Jukka Juslin

Java-kielen perusteita

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmoinnin perusteet Y Python

Tapahtumapohjainen ohjelmointi. Juha Järvensivu 2007

Metodien tekeminen Javalla

5/20: Algoritmirakenteita III

Ohjelmoinnin perusteet Y Python

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Sisältö. Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 6.2

Ohjelmoinnin perusteet Y Python

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 16.3

Tehtävä 1. Tehtävä 2. Arvosteluperusteet Koherentti selitys Koherentti esimerkki

GRAAFISEN KÄYTTÖLIITTYMÄN OHJELMOINTI JAVA SWING

7. Näytölle tulostaminen 7.1

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

9. Periytyminen Javassa 9.1

Javan perusteita. Janne Käki

RINNAKKAINEN OHJELMOINTI A,

Rajapinta (interface)

// Tulostetaan double-tyyppiseen muuttujaan "hinta" tallennettu // kertalipun hinta ja vaihdetaan riviä. System.out.printf("%.1f euros.

ITKP102 Ohjelmointi 1 (6 op)

Java ja grafiikka. Ville Sundberg

Graafisen käyttöliittymän ohjelmointi

ITKP102 Ohjelmointi 1 (6 op)

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Osio 4: Graafinen käyttöliittymä

Osoitin ja viittaus C++:ssa

Sisältö Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 26.2

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 5: Python

Sisällys. JAVA-OHJELMOINTI Osa 6: Periytyminen ja näkyvyys. Luokkahierarkia. Periytyminen (inheritance)

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3

1.3Lohkorakenne muodostetaan käyttämällä a) puolipistettä b) aaltosulkeita c) BEGIN ja END lausekkeita d) sisennystä

Projekti 1 Säikeet ja kriittisen vaiheen kontrollointi javalla

JAVA on ohjelmointikieli, mikä on kieliopiltaan hyvin samankaltainen, jopa identtinen mm. C++

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Toisessa viikkoharjoituksessa on tavoitteena tutustua JUnit:lla testaukseen Eclipse-ympäristössä.

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Ohjelmoinnin perusteet, kurssikoe

9. Periytyminen Javassa 9.1

Ohjelmoinnin perusteet, syksy 2006

Rinnakkaisohjelmointi, Syksy 2006

TIE PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli

Luento 6. T Ohjelmoinnin jatkokurssi T1 & T Ohjelmoinnin jatkokurssi L1. Luennoitsija: Otto Seppälä

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Transkriptio:

Poikkeusten ja tapahtumien käsittely Tässä osassa tarkastellaan poikkeusten ja tapahtumien käsittelyä. Aluksi esitellään poikkeusten käsittelyä yleisesti sekä tarkastellaan siihen liittyviä käsitteitä Sebestan (ks. [Seb], kappale 14.1) pohjalta. Tämän jälkeen perehdytään hieman poikkeusten käsittelyn toteutukseen esimerkkikielissä Java ja C++. Lopuksi perehdytään pintapuolisesti Javan tapahtumien käsittelyyn. 1. Poikkeusten käsittelyn käsitteitä ja suunnittelukriteerejä Useimmat tietokonejärjestelmät havaitsevat erilaiset ohjelman ajon aikana syntyneet virhetilanteet (esimerkiksi laskentaoperaatioissa tapahtuvat nollalla jakamiset jne.). Varhaisemmissa ohjelmointikielissä tällaisiin virhetilanteisiin ei reagoitu millään tavoin, vaan virhe aiheutti suorituksen keskeytymisen ja kontrollin siirtymisen käyttöjärjestelmälle eli ohjelman kaatumisen. Yleensä käyttäjä näkee tällöin käyttöjärjestelmän generoiman virheilmoituksen, joka voi olla varsin hankalasti tulkittava. Ensimmäisissä ohjelmointikielissä ei ollut menetelmiä käsitellä ajon aikaisia virhetilanteita lukuun ottamatta syöttö- ja tulostustoimenpiteitä. Varsinainen poikkeustenkäsittelymekanismi toteutettiin ensimmäiseksi PL/I -kielessä jo 1960 - luvulla ([Seb], kappale 2.8.3); poikkeusten käsittely on kuitenkin yleistynyt vasta 1980 - luvulla ja nykykieliin on yleensä sisällytetty jonkinlainen yleinen poikkeustilanteiden käsittelymekanismi. Tässä poikkeuksilla (exceptions) tarkoitetaan sekä laitteistotasolla havaittavia että ohjelmatasolla havaittavia epänormaaleja tapahtumia. Ensin mainittuja ovat esimerkiksi oheislaitteiden lukuvirheet ja jälkimmäisiä taulukon rajojen ylitykset. Poikkeus ei välttämättä ole virheellinen toiminto, ainoastaan jokin epätavallinen tapahtuma, joka voidaan havaita joko laite- tai ohjelmatasolla. Poikkeusten käsittely (exception handling) tarkoittaa toimenpiteitä, jotka poikkeusten havaitseminen aiheuttaa. Nykyisiin kieliin on toteutettu erilaisia poikkeustilanteiden käsittelymenetelmiä, jotka mahdollistavat poikkeustilanteiden paikantamisen (detecting) ja niistä toipumisen (recovering), kun näihin tilanteisiin on varauduttu

ohjelmointikoodissa. Poikkeuksen käsittelyn suorittaa koodissa yleensä oma syntaktinen yksikkö jota kutsutaan poikkeuksien käsittelijäksi (exception handler). Kun poikkeukseen liittyvä epänormaali tapahtuma sattuu, sanotaan että poikkeus virittyy (raise). Sellaisessakin kielessä, jossa ei ole sisäänrakennettua poikkeuksenkäsittelymekanismia, voidaan toteuttaa käyttäjän määrittelemien ohjelmatason poikkeusten käsittely. Aliohjelmakutsuissa voidaan esimerkiksi käyttää ylimääräistä tilaparametria, jonka arvo annetaan aliohjelmassa ja on jokin virhekoodi epänormaalissa tilanteessa. Välittömästi palattaessa aliohjelmasta kutsuja testaa parametrin arvon ja käynnistää poikkeuksen käsittelyn virhekoodin mukaan. Funktioiden tapauksessa paluuarvo voi myös sisältää virhekoodin (kuten C-kielen kirjastofunktioissa on tapana). Sellaisissa kielissä, joissa aliohjelmia voidaan välittää aliohjelmaparametreina, voidaan myös itse poikkeusten käsittelijä antaa parametrina kutsuttavalle aliohjelmalle. On kuitenkin monia syitä, miksi kieleen kannattaisi rakentaa poikkeusten käsittelymekanismi jo olennaiseksi osaksi kieltä. Usein ohjelmissa virheen käsittely muodostaa suuren osan koodista. Tämä muodostuu koodin luettavuuden ja ylläpidon kannalta ongelmaksi, mikäli jokainen virhetilanne joudutaan havaitsemaan ja käsittelemään koodissa sen tapahtumispaikassa. Erillisen poikkeustenkäsittelymekanismin avulla koodi muodostuu selkeämmäksi. Edelleen poikkeuksen eteneminen (exception propagation) on ohjelman normaalista suorituspolusta erillinen suorituspolku. Tämä mahdollistaa sen, että poikkeuksen käsittely suoritetaan omassa käsittely-yksikössään, joka voi olla yleishyödyllinen ja siten edistää koodin uudelleen käytettävyyttä. Kieleen sisällytetty poikkeustenkäsittely myös tekee ohjelmoijalle helpommaksi ottaa koodissa huomioon sellaiset poikkeustilanteet, jotka muuten jäisivät vaille käsittelyä.

Kun kieleen (tai poikkeusten käsittelyä sisältämättömällä kielellä kirjoitettavaan ohjelmaan) suunnitellaan poikkeustenkäsittelymekanismia, joudutaan kiinnittämään huomio ainakin seuraaviin seikkoihin: 1. Miten (käyttäjän generoimat) poikkeustenkäsittelijät määritellään koodissa ja miten niiden viiteympäristö määräytyy? Käsittelijät voivat yleensä olla joko koodilohkoja tai itsenäisiä yksikköjä. Edellisessä tapauksessa poikkeuksen käsittely voidaan tehdä tapahtumapaikalla, ja jälkimmäisessä tapauksessa poikkeus pitää lähettää eteenpäin käsiteltäväksi. 2. Miten tapahtunut poikkeus sidotaan poikkeuksen käsittelijään? Joissakin olosuhteissa sama poikkeus pitäisi pystyä käsittelemään eri tilanteissa eri tavoin, jolloin tarvitaan mekanismi valita käsittelijä. Toisaalta saattaa käydä niin, että poikkeustilanteen kohtaavassa yksikössä ei olekaan sopivaa käsittelijää poikkeukselle; on ratkaistava, miten tässä tapauksessa poikkeus etenee ohjelmassa. 3. Miten ohjelman suoritus jatkuu poikkeuksen käsittelyn jälkeen? Ohjelma voidaan yksinkertaisesti päättää aina poikkeustilanteessa. Tämä ei kuitenkaan ole kaikissa tapauksissa paras ratkaisu, esimerkiksi poikkeus voi aiheutua virheellisestä syötteestä, joka voidaan antaa uudestaan. Tällaisissa tapauksissa ohjelmaa voidaan jatkaa, mahdollisesti poikkeustilanteen aiheuttaman lauseen jälkeisestä lauseesta tai kokonaan toisesta ohjelmayksiköstä. 4. Voiko käyttäjä määritellä omia poikkeuksia ja jos, niin mikä on niiden muoto? 5. Onko kielessä lainkaan standardipoikkeuksia? Jos on, niin voiko käyttäjä virittää kielen omia poikkeuksia? 6. Miten laitteistotason virhetilanteita käsitellään? Nämä voidaan rajata kokonaan poikkeusten käsittelyn ulkopuolelle (ts. niitä ei pidetä poikkeuksina vaan virheinä) ja pitää poikkeuksina ainoastaan ohjelmatason epänormaaleja tilanteita. 7. Voidaanko poikkeusten havaitsemista rajoittaa, joko kokonaan tai väliaikaisesti? Esimerkiksi taulukon rajojen tarkistamisesta luopuminen voi tehostaa koodia.

2. Poikkeusten käsittely esimerkkikielissä Tässä kappaleessa tarkastellaan hieman C++:n ja Javan poikkeusten käsittelyn toteutusta. 2.1. C++ C++ -kielessä (ks. [Strou], luku 14 tai [Seb] kappale 14.3) poikkeuksen käsittelijä määritellään 'try-catch' - rakenteen catch -lohkossa: try <Code that is expected to raise an exception> catch (exception_type_descpriction) <exception handler body>... catch (exception_type_descpriction) <exception handler body> Yllä try-lohkossa on koodi, joka voi aiheuttaa poikkeuksen ja sitä seuraavissa catch - lohkoissa kussakin voidaan käsitellä parametrilla määritelty poikkeus. Mikäli poikkeus sattuu, sitä verrataan järjestyksessä catch -lohkojen parametrityyppeihin ja ensimmäinen sopiva suoritetaan. Mikäli catch -lauseen parametrina käytetään kolmea pistettä (...) [ellipsis ], se käsittelee minkä tahansa poikkeuksen. Käytettäessä useita catch -lohkoja tällainen lohko on siis syytä kirjoittaa viimeiseksi. C++ -kielessä catch - lohko voi sisältää mitä tahansa laillista C++ -koodia. C++ -kielessä poikkeus voi virittyä ainoastaan eksplisiittisesti käyttämällä throw - lausetta: throw [lauseke] Tällöin sanotaan, että poikkeus heitetään. Ohjelmoijalle voi usein tulla poikkeuksia oman koodin ulkopuolelta; tällöin ne heitetään yleensä jostakin kirjastofunktiosta. Esimerkiksi tiedostojen käsittelyssä voi syntyä poikkeuksia. Pelkkä throw voi esiintyä

ainoastaan käsittelijässä, jolloin se heittää saamansa poikkeuksen edelleen. C++:ssa voidaan poikkeuksena heittää minkä tahansa tyypin tietoa. Poikkeus liitetään käsittelijään seuraavalla periaatteella: Kun heitetty poikkeus on tyyppiä E ja catch - lohkon parametri tyyppiä X, niin lohko käsittelee poikkeuksen jos 1. X on samaa tyyppiä kuin E tai 2. X on luokan E yksikäsitteinen julkinen kantaluokka 3. X ja E ovat osoitintyyppejä, joiden tarkoitetyypeille pätee 1 tai 2 4. X ja E ovat referenssityyppejä, joiden tarkoitetyypeille pätee 1 tai 2 ([Strou] 14.3.) Kun poikkeus virittyy try -lohkossa, tämän suoritus keskeytyy välittömästi ja poikkeukselle etsitään sopivaa käsittelijää järjestyksessä try -lohkoon liittyvistä catch - lohkoista. Mikäli paikallisesti ei löydy sopivaa käsittelijää, poikkeus etenee kutsuvalle yksikölle jne., kunnes sopiva käsittelijä löytyy tai ohjelma kaatuu. C++ -kielen poikkeustenkäsittely on varsin yksinkertainen, kielessä ei ole standardipoikkeuksia; tosin C++:n standardikirjastossa on määritelty mittava määrä erilaisia poikkeuksia. Minkä tahansa tietotyypin alkiota voidaan käyttää poikkeuksena. Poikkeuksien virittymistä ei voi rajoittaa, mutta funktiot voivat määritellä poikkeustyypit, jotka funktio voi virittää.

Tämä tapahtuu antamalla lista poikkeustyypeistä funktion otsikossa, esimerkiksi class A ; void heitto(int argh) throw(a,int) A mya; int x = 100; if (argh < 0) throw myc; else throw x; Mikäli funktio heittää poikkeuksen joka ei esiinny sen poikkeuslistassa, aiheutuu ajonaikainen virhe. Jos siis funktion otsikossa muutettaisiin tyyppi int floatiksi ja funktiota kutsuttaisiin ohjelmassa positiivisella parametrilla, ohjelma kaatuisi, vaikka kutsuja olisikin varautunut float -tyyppiseen poikkeukseen. Huomaa, että try- ja catch -lohkot muodostavat omat näkyvyysalueensa, joten try - lohkon paikallisia muuttujia ei voi käsitellä catch -lohkossa. Lisäksi kaikki try -lohkon paikalliset pinodynaamiset muuttujat tuhotaan poikkeuksen sattuessa. 2.2. Java Javan poikkeuksien käsittelymekanismi pohjautuu C++ -kielen try-catch-lohkorakenteen syntaksiin, mutta Javan toteutus on puhtaasti oliopohjainen ja noudattaa monelta osin erilaista semantiikkaa (ks. esimerkiksi [Arn], luku 7). Javassa ohjelman suoritusaikaiset virhetilanteet jakautuvat kahteen osaan: Virheisiin (Error) ja poikkeuksiin (Exception). Virheet ovat yleensä vakavia häiriöitä, joiden kannattaa antaa kaataa ohjelma. Poikkeukset ovat sen sijaan yleensä käsiteltävissä muutenkin - käsittelemättöminä nekin kaatavat ohjelman. Kaikki Javan poikkeukset ovat olioita. Virheet ovat Error-luokan (ja sen aliluokkien) olioita ja poikkeukset

Exception -luokan (ja sen aliluokkien) olioita. Sekä Error että Exception -luokka periytyy luokasta Throwable. Javan poikkeukset jakautuvat tarkistettaviin (checked) ja ei-tarkistettaviin (unchecked). Tarkistettavat poikkeukset on aina käsiteltävä tai heitettävä eteenpäin; eteenpäin heittämisen voi tehdä throws-komennolla metodin otsikossa. Error-luokan poikkeukset ovat ei-tarkistettavia, samoin Exception-luokasta periytyvät RuntimeException-poikkeukset. Kaikki muut poikkeukset ovat tarkistettavia poikkeuksia, esimerkiksi luku- ja kirjoitustapahtumiin liittyvä IOException. Näin ollen metodissa, joka tekee mainittuja operaatioita, on joko käsiteltävä IOException tai heitettävä se eteenpäin. Poikkeus voidaan ottaa kiinni ohjelmassa try -catch -finally -rakenteen catch -lohkossa. Sen syntaksi on seuraava: try lauseet catch(poikkeusluokka1 var1) poikkeuslauseet1 [catch(poikkeusluokka2 var2) poikkeuslauseet2 catch... finally lopetuslauseet ] Tässä yritetään suorittaa lauseet ja mikäli poikkeuksia ei ilmene, jatketaan ohjelman suoritusta normaalisti. Jos sen sijaan poikkeusluokka1 -tyyppinen poikkeus sattuu, suoritetaan siitä lähtien poikkeuslauseet1 ja kontrolli siirtyy catch -lauseiden jälkeiseen

koodiin. Jos taas sattuu poikkeusluokka2 -tyyppinen poikkeus, suoritetaan poikkeuslauseet2 ja kontrolli siirtyy catch -lauseiden jälkeiseen koodiin jne. Mikäli haluaa jonkin operaation suoritettavaksi riippumatta siitä, minkälainen virhetilanne sattuu, voi try - catch -lauseeseen lisätä finally-osan. Tämä koodi suoritetaan joka tapauksessa; myös siinä tapauksessa, että poikkeusta ei satu! Käsittelijä voi myös heittää uuden poikkeuksen, jonka ei tarvitse olla samaa tyyppiä kuin saatu poikkeus. Kun poikkeus heitetään, eikä koodissa ole siihen sopivaa catch -lausetta, ohjelman kontrolli siirtyy välittömästi poikkeuksen aiheuttanutta metodia kutsuvalle metodille, jossa puolestaan tarkastetaan onko sopivaa catch -lausetta jne. Prosessi jatkuu, kunnes poikkeus käsitellään tai kontrolli siirtyy main -metodin ulkopuolelle, jolloin ohjelma kaatuu ja konsolille tulostuu virheilmoitus, joka kertoo, mikä poikkeus sattui ja missä. Esimerkiksi ohjelma public class NollaJako public static void main(string[] args) int luku1=1000,luku2=0,jako; jako = luku1/luku2; System.out.println(jako); Tulostaa ennen päättymistään: Exception in thread "main" java.lang.arithmeticexception: / by zero at NollaJako.main(NollaJako.java:6) Ensimmäisellä rivillä ilmoitetaan poikkeuksen nimi (java.lang.arithmeticexception) ja sen syy (/ by zero) sekä toisella rivillä (sekä tarvittaessa siitä eteenpäin) poikkeuksen tapahtumapaikka (NollaJako.java:6). Tapahtumapaikka saadaan kutsupinosta (call stack trace) ja se voidaan aina kysyä poikkeusoliolta. Jos käsittelee ohjelmassaan syvältä tulleita poikkeuksia, voi olla hyödyllistä käyttää Exception -luokan metodia

printstacktrace(), joka tulostaa poikkeuksen kulkeman polun. Lisäksi metodilla getmessage() saa näkyviin poikkeuksen virheilmoituksen. Javassa on huomattava määrä määriteltyjä poikkeuksia. Esimerkiksi taulukon rajojen yli kirjoittaminen antaa ArrayIndexOutOfBoundsExceptionin, nollalla jakaminen ArithmeticExceptionin ja olioon viittaaminen ennen sen luomista NullPointerExceptionin. Lisäksi ohjelmoija voi itse määritellä uusia poikkeuksia tarpeen mukaan. Esimerkki 1. Seuraavassa ohjelmassa otetaan kiinni kaksi poikkeusta: ArrayIndexOutOfBoundsException sattuu, jos käytetään ensimmäistä versiota taulukosta nelja. Jos taas käytetään toista versiota, jaetaan nollalla, ennen kuin taulukon ylikirjoitus tapahtuu ja saadaan ArithmeticException.

public class KaxPoikkeus public static void main(string[] args) int i,jako; int[] nelja = 1,2,3,4; //int[] nelja = 1,2,0,4; int[] kolme = new int[3]; try for(i=0;i<3;i++) kolme[i] = nelja[i]; jako = kolme[i]/nelja[i]; catch(arithmeticexception ae) System.out.println("Jaoinpa nollalla!"); catch(arrayindexoutofboundsexception aibe) System.out.println("Ylitinpä taulukon!"); finally System.out.println("Meniköhän kaikki hyvin?"); System.out.println("Suoritus loppuu!");

Yleensä Javan omat poikkeukset ja niiden käsittely suovat riittävän kontrollin ohjelmoijalle. Joskus kuitenkin tarvitaan itse määriteltyjä poikkeuksia; niitä voidaan kirjoittaa tekemällä oma luokka, joka perii Exception-luokan seuraavasti: class MyException extends Exception public MyException( ) public MyException( String msg) super (msg); Poikkeusten käyttäminen antaa myös olio-ohjelmointiin uusia mahdollisuuksia. Koska luokan muodostimella ei ole paluuarvoa, ei olion luomisen voida palauttaa yhteydessä virhearvoa, jos luominen ei onnistu. Poikkeus voidaan silti heittää ja näin välittää oliota luovalle ohjelmalle tieto siitä ettei olion tekeminen onnistunutkaan. Lisäksi poikkeuksen virheilmoitus kertoo epäonnistumisen syyn. Kannattaa myös panna merkille, että oliota ei lainkaan luoda, jos muodostimessa heitetään poikkeus. Esimerkki 2. Oletetaan, että tarvitaan luokkaa, joka sisältää ainoana kenttänään parittoman kokonaisluvun. Tällöin voidaan määritellä oma poikkeus ja heittää se muodostimessa, jos yritetään syöttää parillista lukua. // Oma poikkeusluokka class ParillisPoikkeus extends Exception public ParillisPoikkeus() public ParillisPoikkeus(String msg) super(msg);

// Oma luokka, jossa pariton kokonaisluku muuttujan arvona class Pariton public int oddluku; public Pariton(int odd) throws ParillisPoikkeus if( (odd % 2) == 0 ) throw new ParillisPoikkeus("Luku "+odd +" ei ole pariton."); else oddluku = odd; // Poikkeusta testaava pääohjelma public class ParitonTesti public static void main(string[] args) Pariton p=null; try p = new Pariton(2); catch(parillispoikkeus pp) System.out.println(pp.getMessage() );

Mikäli tarkistettava poikkeus halutaan jättää käsittelemättä metodissa, sen lähettäminen kutsujalle edellyttää metodin otsikossa poikkeuksen esittelyn throwslauseella. Esimerkiksi int some_method( ) throws MyException, IOException <function body> Javan throws-lauseen semantiikka poikkeaa C++ -kielen vastaavasta throw-lauseesta. Javassa metodi, jossa ei esitellä throws-lauseella poikkeuksia, ei voi välittää mitään tarkistettavaa poikkeusta. Javassa ei ole oletuskäsittelijöitä poikkeuksille eikä poikkeuksien syntymistä voida rajoittaa. 3. Tapahtumien käsittely Tapahtumien käsittely muistuttaa suuresti poikkeusten käsittelyä. Myös tapahtumalla on käsittelijä, jota kutsutaan tapahtuman sattuessa. Toisin kuin poikkeuksia, tapahtumia virittyy ulkoisten toimintojen, kuten graafiseen käyttöliittymään kohdistumien toimenpiteiden, seurauksena. Tapahtumaohjatussa ohjelmoinnissa joitakin osia ohjelmassa suoritetaan aikoina, joita ei voida ennalta tietää, koska tapahtumien käsittelijät aktivoituvat joidenkin ulkoisten ärsykkeiden johdosta. Tapahtuma (event) on ilmoitus, että jotain erityistä on sattunut, kuten jonkin graafisen elementin valinta hiirellä. Tapahtuman käsittelijä (event handler) on koodin osa, joka suoritetaan vastaavan tapahtuman ilmetessä. Tapahtuman käsittelijöiden avulla ohjelma saadaan vastaamaan käyttäjän toimenpiteisiin. Tarkastellaan esimerkinomaisesti Javan tapahtumankäsittelyä. Javassa tapahtumat liitetään tapahtumien käsittelijöihin niin sanottujen tapahtumien kuuntelijoiden (event listeners) avulla. Tapahtuman kuuntelija rekisteröidään kuuntelemaan jotain määrättyä tapahtumaa. Rekisteröinti tapahtuu kutsumalla tarkkailtavan olion sopivaa metodia. Kuuntelijan on toteutettava metodi, joka käsittelee tapahtuman. Mikäli luokkaa aiotaan käyttää tapahtuman käsittelijänä, sen on toteutettava tapahtumia

vastaava rajapinta. Seuraava esimerkki havainnollistaa asiaa. Esimerkissä on Javaohjelma, joka avaa ikkunan. Ohjelma voidaan lopettaa ikkuna sulkemalla. Lisäksi ikkunassa on painonappi, jonka painallus avaa dialogi-ikkunan. import javax.swing.*; import java.awt.*; import java.awt.event.*; class TapahtumaIkkuna extends JFrame implements WindowListener, ActionListener JButton omanappi; public TapahtumaIkkuna() setlayout(new GridLayout(3,1)); this.settitle("tapahtumia tarkkaileva ikkuna."); this.setsize(400,200); // Lisätään Button -olio omanappi = new JButton("Painonappi"); add(omanappi); public void laitaesiin() // Laitetaan ikkuna näkyviin this.setvisible(true); // Lisätään ikkunaan kuuntelijaksi se itse! this.addwindowlistener(this); // Lisätään napille kuuntelijaksi ikkunaolio omanappi.addactionlistener(this); public static void main(string[] args) // Luodaan uusi TapahtumaIkkuna-olio final TapahtumaIkkuna akkuna = new TapahtumaIkkuna(); ); // Näytetään ikkuna omassa säikeessään javax.swing.swingutilities.invokelater(new Runnable() public void run() akkuna.laitaesiin();

/* Nämä metodit on rajapinnan takia implementoitava, mutta niiden rungot voidaan jättää tyhjiksi */ public void windowopened(windowevent e) public void windowclosed(windowevent e) public void windowactivated(windowevent e) public void windowdeactivated(windowevent e) public void windowiconified(windowevent e) public void windowdeiconified(windowevent e) // Vain tähän metodiin tarvitaan koodia! public void windowclosing(windowevent e) System.exit(0); // ActionListener -rajapinnan metodi public void actionperformed(actionevent e) // Tapahtuman lähdeolio Object olio = e.getsource(); // Tutkitaan miltä napilta tapahtuma lähti // ja näytetään dialogi if(olio == omanappi) JOptionPane.showMessageDialog(this,"Painoit nappia!"); Ohjelmassa on merkitty lihavoidulla fontilla tapahtumien käsittelyn kannalta olennaiset osat. Ohjelman ikkunan luokka on TapahtumaIkkuna. Tapahtumien käsittelijät implementoidaan sen metodeina, joten sen on toteutettava rajapinnat WindowListener ja ActionListener. class TapahtumaIkkuna extends JFrame implements WindowListener, ActionListener Rajapinta WindowListener sisältää ikkunatapahtumiin liittyvien metodien rajapinnan; ActionListener sisältää ainoastaan yhden, painonapin painallukseen liittyvän metodin prototyypin. Luokan muodostimessa luodaan painonappi (omanappi) ja lisätään se ikkunaan. Ikkunan näyttävässä metodissa (laitaesiin()) rekisteröidään ikkunaolio kuuntelemaan sekä ikkunaan että painonappiin liittyviä tapahtumia:

this.addwindowlistener(this); omanappi.addactionlistener(this); Ikkunan tapahtumiin liittyy monta metodia, mutta näistä useimmat voidaan jättää tässä tyhjiksi, koska halutaan reagoida ainoastaan siten, että ohjelma päätetään, kun ikkuna suljetaan. Tarvitaan koodia vain metodiin public void windowclosing(windowevent e) System.exit(0); Kun käyttäjä sulkee ikkunan, siihen liittyvä tapahtuma ohjautuu edelliselle metodille. Kun painonappia painetaan, kutsutaan ActionListener-rajapinnan metodia, jonka koodi on seuraava: public void actionperformed(actionevent e) // Tapahtuman lähdeolio Object olio = e.getsource(); // Tutkitaan miltä napilta tapahtuma lähti // ja näytetään dialogi if(olio == omanappi) JOptionPane.showMessageDialog(this,"Painoit nappia!"); Metodin parametriksi saadaan tapahtumaolio, jonka avulla saadaan selville, minkä olion tapahtumasta on kyse. Jos tapahtuma aiheutui painonapin omanappi painalluksesta, näytetään dialogi.

Lähteet [Arn] Arnold, Ken Gosling, James. The Java Programming Language, Second Edition, Addison-Wesley 1998. [Seb] Sebesta, Robert W. Concepts of Programming Languages 10th edition, Pearson 2013. [Strou] Stroustrup, Bjarne. The C++ Programming Language, 3rd edition, Murray Hill 1997.