Olio-ohjelmointi 2016 Virhetilanteiden käsittely
Poikkeustilanteet n Java-järjestelmässä voidaan ottaa kiinni ohjelman suoritusaikana tapahtuvia virhetilanteita, joita ei saada kiinni tavanomaisilla ohjausrakenteilla n Poikkeusten käsittely (Exception handling) n Toimenpiteitä, jotka mahdollistavat ohjelman suorituksen jatkumisen n Poikkeukset ovat olioita Javalla 2
Poikkeustilanteet metodi, jossa virhe tapahtuu metodi, jossa ei ole poikkeuksenkäsittelijää Metodi, jossa on poikkeuksenkäsittelijä main-metodi metodikutsu metodikutsu metodikutsu kutsupino Javalla 3
Poikkeustilanteet palauttaa poikkeuksen Siirtää poikkeuksen edelleen ottaa poikkeuksen käsittelyyn metodi, jossa virhe tapahtuu metodi, jossa ei ole poikkeuksenkäsittelijää Metodi, jossa on poikkeuksenkäsittelijä Etsitään sopivaa käsittelijää Etsitään sopivaa käsittelijää main-metodi käydään läpi kutsupinoa sopivan poikkeuksenkäsittelijän löytämiseksi Javalla 4
Poikkeustilanteet n Ei ole pakollista käsitellä: n java.lang.error- ja java.lang.runtimeexception luokista perityville poikkeuksille ja virheille n Error luokan virheet ovat yleensä sellaisia, että niistä ei edes voi toipua n RuntimeException virheet kertovat ohjelmoijan virheistä, hyvässä ohjelmassa näitä ei tietenkään ole n Kaikki muut poikkeukset on käsiteltävä Javalla 5
Poikkeustilanteet n Error-luokan tyyppillisimpiä virheitä OutOfMemoryError, virtuaalikoneelle varattu muisti loppuu StackOverFlowError, pinomuisti loppuu Linkitysaikaiset virheilmoitukset n NoSuchMethodError, luokalla ei ole ko. metodia n NoSuchFieldError, luokalle ei ole ko. attribuuttia! n InstantiationError, yritys luoda liittymän tai abstraktin luokan ilmentymä n IllegalAccessError, yritys käyttää metodia tai attribuuttia, johon ei ole käyttöoikeutta! n AbstractMethodError! n NoClassDefFoundError, luokkalataaja ei löydä luokkaa Javalla 6
Poikkeustilanteet n RuntimeException -poikkeuksia, yleensä ohjelmoijan aiheuttamia virhetilanteita AritmeticException ArrayIndexOutOfBoundsException! ArrayStoreException! ClassCastException! IllegalArgumentException! NullPointerException! NumberFormatException! SecurityException! StringIndexOutOfBoundsException! Javalla 7
n Ohjelma, jossa ei käsitellä poikkeustilanteita class Poikkeuksia {!! public void teejakolasku(){! System.out.println("Jakolaskuyritys: ");! jaanollalla();! System.out.println("Jakolasku onnistui");!! public void jaanollalla(){! int jaettava = 6, jakaja = 0;! int osamaara = jaettava/jakaja;! System.out.println(jaettava + "/" + jakaja +" -> "! + osamaara);! Javalla 8
public class PoikkeusDemo_1 {! public static void main( String [] args ){! Poikkeuksia ekayritys = new Poikkeuksia();! ekayritys.teejakolasku();! System.out.println("Valmista tuli");! n Ohjelman ajoritys: Poikkeukset irasanen$ java PoikkeusDemo_1 Jakolaskuyritys: Exception in thread "main" java.lang.arithmeticexception: / by zero at Poikkeuksia.jaaNollalla(PoikkeusDemo_1.java:11) at Poikkeuksia.teeJakolasku(PoikkeusDemo_1.java:5) at PoikkeusDemo_1.main(PoikkeusDemo_1.java:23)
Poikkeustilanteet n Poikkeustilanteiden käsittely n Poikkeuksen aiheuttava koodi laitetaan try-catchrakenteeseen try {! // epäilyttävä koodi, jossa voi tapahtua poikkeus! // poikkeuksen syntyessä etsitään sopivaa poikkeuksen! // tyypin mukaista käsittelijää catch-lauseista,! // joita voi olla yksi tai useampi! }catch( Poikkeuksentyyppi nimi ){! // tänne joudutaan vain kun Poikkeuksentyyppi tyypinen! // poikeus on syntynyt! }finally{! // tämä tehdään aina! }! Javalla 10
public class PoikkeusDemo_2 {!! public static void main( String [] args ){!! Poikkeuksia ekayritys = new Poikkeuksia();!! try {!! ekayritys.teejakolasku();!! }catch (ArithmeticException ae){!! System.out.println("Jakolaskussa tapahtui virhe");! System.out.println(ae.toString());! System.out.println("Valmista tuli");! }! Javalla 11
n Java 7:ään lisätty multi-catch try {! // poikkeuksen aiheuttavaa koodia! }catch (PoikkeuksenTyyppi_a PoikkeuksenTyyppi_b pnimi){! // poikkeuksen käsittely, kun on tapahtunut joko! // PoikkeuksenTyyppi_a tai PoikkeuksenTyyppi_b! // tyyppinen poikkeustilanne!! n catch-lohkoja voi olla useita, poikkeuksen synnyttyä etsitään sellaista catch-lohkoa, jonka tyyppi vastaa poikkeustilannetta Javalla 12
n Käsiteltävien poikkeusten tyyppin määrittelyllä voi valita mitkä poikkeukset käsitellään, try {! for(int i = 0; i < 10; i++ ){! System.out.print("Anna luku > ");! taulukko[i] = sc.nextint();! sc.nextline();! }catch(inputmismatchexception ime){! System.out.println("Virhe lukujen syöttämisessä");! System.out.println(ime.toString());! }catch(arrayindexoutofboundsexception aioe){! System.out.println("Taulukon indeksi yli rajojen");! System.out.println( aioe.tostring());! Javalla 13
n Edellisessä esimerkissä on yritetty paikata huonoa koodausta poikkeusten käsittelyllä! n Jos catch-lohkossa määritellyn poikkeuksen tyyppi on Throwable, käsitellään tässä lohkossa kaikki mahdolliset poikkeustilanteet n Kaikki poikkeustyypit ovat Throwable-luokan jälkeläisiä n Jos poikkeustilannetta ei synny, ei catch-lohkoa suoriteta Javalla 14
n try-lohkon perään voi lisätä finally-lohkon n Normaalipoikkeustilanteessa ensin suoritetaan catchlohko ja lopuksi aina finally-lohko n catch-lohko voi puuttua, tällöin suoriteaan finallylohkon toimenpiteet n finally-lohkossa tapahtuva uusi poikkeus peittää alkuperäisen poikkeuksen n finally-lohkon sisällä tapahtuvat uudet poikkeukset käsitelltävä lohkossa, muuten alkuperäinen poikkeus hukkuu Javalla 15
n Resurssist sulkeva try! n Helpottaa sellaisten resurssien käyttöä, jotka on suljettava käytön jälkeen Tietokannat Tiedostot n Suljettava resurssi kirjoitetaan sulkeisiin ennnen lohkoa try (FileWriter fw = new FileWriter("koe.txt")){! fw.write("tässäpä rivi tekstiä");! n Lohkosta poistuttaessa tiedosto suljetaan automaattisesti, myös poikkeuksen sattuessa Javalla 16
n Suljettavia resursseja voi olla useita, erotetaan puolipisteellä try (FileWriter fw = new FileWriter("koe2.txt");! FileReader fr = new FileReader("koe.txt2)){! fw.write( fr.read());! n Resurssit suljetaan takaperoisessa järjestyksessä Javalla 17
n Pakolliset käsiteltävät poikkeukset n Kääntäjä ei suostu kääntämään ohjelmaa, jos poikkeukselle ei löydy käsittelijää public static void main(string [] args) {! FileInputStream is = new FileInputStream("koe.txt");! java:7: error: unreported exception FileNotFoundException; must be caught or declared to be thrown FileInputStream is = new FileInputStream("koe.txt"); Javalla 18
n Poikkeus on käsiteltävä public static void main(string [] args){! try {! FileInputStream is = new FileInputStream("koe.txt");! }catch( FileNotFoundException fne ){! System.out.println("Virhe, tiedostoa ei löydy!");! n Poikkeuksen voi lähettää eteenpäin ilmoittamalla metodin otsikossa throws-avainsanalla public static void main(string [] args)! throws FileNotFoundException{! FileInputStream is = new FileInputStream("koe.txt");! Javalla 19
n Luokan konstruktori ei voi palauttaa virhearvoa n Jos oliota yritetään luoda virheellisillä parametreilla, voidaan heittää poikkeus Oliota ei luoda class Positiivinen {!!private int posluku;!!public Positiivinen(int luku) throws Exception!!{!!!if( luku <= 0 ) {!!!!throw new Exception("Luku huono");!!!!!else {!!!!posluku = luku;!!!!}!! Javalla 20
n Ohjelmoija voi määritellä omia poikkeuksia n Periytetään jokin Throwable-luokan jälkeläisluokka n Yleensä joko Exception- tai RunTimeExceptionluokista Excepetion-luokasta, jos halutaan viestittää, että virheestä voi vielä toipua RunTimeException-luokasta, jos poikkeukset ilmaisevat että koodissa on ohjelmointivirheitä n Nollalla jako, taulukon rajojen ylitys n Error-luokka on varattu järjestelmän virheille Javalla 21
n Oma poikkeus nimentään yleensä Exception- tai Error-päätteiseksi n Esimerkkki: public class AikaException extends Exception {!! public AikaException(String param){! super("merkkijonoa ei voi muuntaa ajaksi: " + param);!! public AikaException(String param, Throwable syy){! super("merkkijonoa ei voi muuntaa ajaksi: " + param, syy);! Javalla 22
n Poikkeus on periytetty Exception-luokasta, koska kyseessä ei ole ohjelmointivirhe n Poikkeus aiheutetaan throws-lauseella n muodostaaika-metodissa poikkeukset otetaan kiinni ja aiheutetaan uusi poikkeus, joka kuvaa paremmin virhetilannetta. n Syyksi annetaan alkuperäinen poikkeus Joko parseint-metodin NumberFormatException! tai Aika-luokan rakentajan IllegalArgumentException! Javalla 23
n Poikkeuksen virheilmoituksen saa Throwable-luokan metodilla getmessage! n Metodi getcause palauttaa poikkeuksen syyn tai arvon null! n Metodi printstacktrace tulostaa kutsupolun, jota kautta pääsee poikkeuksen aiheuttaneeseen metodiin n Poikkeusten käyttö ei ole ilmaista, ei kannata käyttää paikkaamaan huonoa ohjelmointia Javalla 24
n Käytä poikkeuksia (Bruce Eckelin mukaan) Käsittelemään ongelmia asianmukaisella tavalla Korjaamaan ongelma, jonka jälkeen kutsutaan uudelleen poikkeuksen aiheuttanutta metodia Paikkaamaan ongelma yrittämättä kutsua uudelleen ongelman aihetuttanutta metodia Laskeman vaihtoehtoinen tulos ongelman aiheuttaneen metodin oletettavan tuloksen sijaan Javalla 25
Tekemään mitä tahansa voitavaa ja palauttamaan sama poikkeus seuraavalle ylemmälle tasolle Tekemään mitä tahansa voitavaa ja palauttamaan eri poikkeus seuraavalle ylemmälle tasolle Lopettamaan ohjelma suoritus Yksinkertaistamaan (poikkeusten käsittely ei saa tehdä asioista mutkallisempia) Tekemään ohjelmista turvallisempia Javalla 26