autor: Ivan Kupka

Verzia 1

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

Java 6

 

Výnimky

 

Cieľom kapitoly je oboznámiť sa s mechanizmom, ktorý v Jave umožňuje ošetriť chyby aj za behu programu, s mechanizmom výnimiek.

 

                Niekedy bezchybné zbehnutie programu závisí na faktoroch, ktoré  pri písaní programu nemáme celkom pod kontrolou. Takým faktorom môže byť napríklad existencia  alebo neexistencia nejakého súboru v adresári používateľa, prekročenie- neprekročenie indexu poľa  a podobne. Java ponúka na ošetrenie takýchto prípadov veľmi praktický mechanizmus. Ide o takzvaný mechanizmus spracovania výnimiek.

 

  Uvažujme jednoduchý prípad. Počas behu programu dôjde k neošetrenému prekročeniu indexu poľa. Čo sa stane?

 

Príklad. Kód 6.1

 

public class IndexPola {

       

          public static void main(String[] args){

                  String[]  triPozdravy = new String[]{"Ahoj","Cau","Hi"};

                 

                  System.out.println("O chvilu vygenerujem vynimku.");

                            // teraz sposobime pretecenie indexu pola

                            triPozdravy[10] = "Zdravim";

System.out.println("A este som chcel pozdravit staru mamu.");                  

                  }

        }

       

Výpis z programu:

O chvilu vygenerujem vynimku.

 

java.lang.ArrayIndexOutOfBoundsException

        at IndexPola.main(IndexPola.java:9)

 Exception in thread "main" Process Exit...

 

  Virtuálny stroj Javy v tomto prípade ukončil program okamžite po výskyte výnimky. Pomocou spracovania výnimky môžeme zabrániť predčasnému ukončeniu programu:

 

Kód 6.2

 

public class IndexPola {

       

        public static void main(String[] args){

                  String[]  triPozdravy = new String[] {"Ahoj","Cau","Hi"};

                 

                  try{

                            System.out.println("O chvilu vygenerujem vynimku.");

                            // teraz sposobime pretecenie indexu pola

                            triPozdravy[10] = "Zdravim";

                            System.out.println("Toto sa uz nezobrazi");

                  }

                 

                  catch(ArrayIndexOutOfBoundsException vynimka){

                            //zachytili sme vynimku

                            System.out.println("Prekroceny index pola triPozdravy.");

                  }

                           

                  System.out.println("Po zachyteni vynimky:");        

                  System.out.println("A este som chcel pozdravit staru mamu.");

                 

                  }

        }

Výpis:

O chvilu vygenerujem vynimku.

 

Prekroceny index pola triPozdravy.

 

Po zachyteni vynimky:

 

A este som chcel pozdravit staru mamu.

 

Process Exit...

 

  Vo všeobecnosti sa spracovanie výnimiek v Jave ošetruje pomocou týchto kľúčových slov:  try, catch, throw, throws, finally.  V prechádzajúcom kóde sme videli pri práci kľúčové slová try  a catch, ktoré sa vždy používajú vo dvojici.

Za slovom try nasleduje blok, v ktorom je uzavretý kontrolovaný kód. Ten, ktorý by mohol vyvolať výnimku.

Keď výnimka nastane, kód, umiestnený v bloku za catch ju zachytí a spracuje  podľa našich predstáv. Samozrejme, dôležité je, že sme v kóde 6.2 zachytili ten správny typ výnimky : v tomto prípade ArrayIndexOutOfBoundsException.

 

V kóde 6.2 sa vyskytuje nasledujúci výraz:

catch(ArrayIndexOutOfBoundsException vynimka){

 

  Premenná vynimka ukazuje na objekt triedy ArrayIndexOutOfBoundsException. V dokumentácii zistíme, že táto trieda je potomkom triedy java.lang.RuntimeException

ktorá je zasa potomkom triedy java.lang.Exception. Táto posledná – a teda aj jej potomkovia – má k dispozícii metódu printStackTrace(). Použime túto metódu v našom kóde. Prepíšme blok za „catch“ v kóde 6.2 takto:

 

catch(ArrayIndexOutOfBoundsException vynimka){

                            //zachytili sme vynimku

                            System.out.println("Prekroceny index pola triPozdravy.");

          vynimka.printStackTrace();

 

                  }

 

Výpis po zbehnutí programu:

O chvilu vygenerujem vynimku.

 

Prekroceny index pola triPozdravy.

 

java.lang.ArrayIndexOutOfBoundsException

 

        at IndexPola.main(IndexPola.java:9)

 

Po zachyteni vynimky:

 

A este som chcel pozdravit staru mamu.

 

Process Exit...

 

Vidíme, že virtuálny stroj Javy vypísal to isté hlásenie, s akým sme sa stretli v reakcii na kód 6.2. Lenže tentokrát máme výnimku ošetrenú a preto program nie je prerušený.

 

(Poznámka: výpis pomocou  metódy printStackTrace() bez uvedenia parametra je možný pri výstupe na konzolu. Pri použití grafického užívateľského prostredia treba ešte chybový výpis presmerovať – napr. do súboru – pomocou System.setErr() alebo System.setOut(). Viď Herout 19.2.1. str. 300-302. )

 

Vyhodenie výnimky po jej ošetrení

 

Metóda, definovaná v nasledujúcom kóde slúži na prečítanie prvého riadku z dokumentu „najdolezitejsiMail.txt“ a jeho zobrazenie na konzole.

Najprv začnime  písať jej prvý riadok a preložme kód:

 

Kód 6.3prvyPokus

import java.io.*;

public class Sekretarka {

       

        public void citajZaciatokNajdolezitejsiehoMailu(){

                  BufferedReader citatel = new BufferedReader(new FileReader(new File("najdolezitejsiMail.txt")));    

        }

}//EOClass

 

Pri pokuse o preklad nám prekladač oznámi:

Sekretarka.java:5: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown

                  BufferedReader citatel = new BufferedReader(new FileReader(new File("najdolezitejsiMail.txt")));

                                                            ^

1 error

 

Ošetrime najprv výnimku:

Kód 6.3druhyPokus

import java.io.*;

public class Sekretarka {

       

        public void citajZaciatokNajdolezitejsiehoMailu(){

                  try{

        BufferedReader citatel = new BufferedReader(new FileReader(new File("najdolezitejsiMail.txt")));

        }       

        catch(FileNotFoundException e){

                   System.out.println("Sorry, subor s mailom som nenasiel.");

        }

                 

        }

       

Tentokrát sa kód preloží bez problémov. Dopíšme teraz telo   metódy do konca.

 

Kód 6.3tretiPokus

import java.io.*;

public class Sekretarka {

       

        public void citajZaciatokNajdolezitejsiehoMailu(){

                  try{

                            // konstruujem objekt, schopny citat z daneho File

        BufferedReader citatel = new BufferedReader(new FileReader(new File("najdolezitejsiMail.txt")));

        //citam prvy riadok

        String prvyRiadok = citatel.readLine();

        //tlacim ho

        System.out.println("Prvy riadok najdolezitejsieho mailu je: \n" + prvyRiadok);

// zatvaram prud

          citatel.close();

        }       

        catch(FileNotFoundException e){

                   System.out.println("Sorry, subor s mailom som nenasiel.");

        }

                 

        }       

}//EOClas

 

Pri pokuse o preklad dostaneme hlásenie:

Sekretarka.java:9: unreported exception java.io.IOException; must be caught or declared to be thrown

        String prvyRiadok = citatel.readLine();

                                   ^

1 error

 

Teraz ošetríme aj druhú výnimku:

Kód 6.3štvrtýPokus

import java.io.*;

public class Sekretarka {

       

        public void citajZaciatokNajdolezitejsiehoMailu(){

                  try{

                            // konstruujem objekt, schopny citat z daneho File

        BufferedReader citatel = new BufferedReader(new FileReader(new File("najdolezitejsiMail.txt")));

        //citam prvy riadok

        String prvyRiadok = citatel.readLine();

        //tlacim ho

        System.out.println("Prvy riadok  najdolezitejsieho mailu je: \n" + prvyRiadok);

// zatvaram prud

          citatel.close();

        }       

        catch(FileNotFoundException e){

                   System.out.println("Sorry, subor s mailom som nenasiel.");

        }

       catch(IOException ioe){

       System.out.println("Nastala chyba. Citanie - input - z file neprebehlo.");

       }        

        }       

}//EOClass

 

 

 

V tomto prípade prekladač kód preloží bez problémov. Výpis:

 O chvilu vygenerujem vynimku.

Prekroceny index pola triPozdravy.

java.lang.ArrayIndexOutOfBoundsException

        at IndexPola.main(IndexPola.java:9)

Po zachyteni vynimky:

A este som chcel pozdravit staru mamu.

 

Teraz je už objekt Sekretarka aj so svojou metódou schopný pracovať aj pre iné kódy:

 

Kód 6.4

 

public class Pracuj{

        public static void main(String[] args){

          Sekretarka virtuella = new Sekretarka();    

          virtuella.citajZaciatokNajdolezitejsiehoMailu();

        }

}

Výpis:

Sorry, subor s mailom som nenasiel.

 

Process Exit...

 

(Samozrejme, pokiaľ sa v adresári so súborom Pracuj.class nenachádza aj súbor najdolezitejsiMail.txt).

 

Metódu v triede Sekretarka upravíme ešte jeden raz. Predstavme si, že užívateľ nemá prístup ku zdrojovému kódu triedy Sekretarka a používa metódu citajZaciatokNajdolezitejsiehoMailu().  Chceli by sme užívateľa (programátora, ktorý ďalej s touto metódou pracuje) prinútiť, aby aj on vhodne, podľa potrieb jeho programu, zareagoval na možnosť, že daný súbor sa nenájde. Chceme, aby aj on ošetril výnimku typu FileNotFoundException. Urobíme to pomocou kľúčových slov throwsthrow tak, ako to vidíme nižšie:

 

Kód 6.3

 

import java.io.*;

public class Sekretarka {

       

        public void citajZaciatokNajdolezitejsiehoMailu() throws FileNotFoundException{

                  try{

                            // konstruujem objekt, schopny citat z daneho File

        BufferedReader citatel = new BufferedReader(new FileReader(new File("najdolezitejsiMail.txt")));

        //citam prvy riadok

        String prvyRiadok = citatel.readLine();

        //tlacim ho

        System.out.println("Prvy riadok najdolezitejsieho mailu je: \n" + prvyRiadok);

          // zatvaram prud

          citatel.close();

        }       

        catch(FileNotFoundException e){

                   System.out.println("Sorry, subor s mailom som nenasiel.");

                   throw e;

        }

       catch(IOException ioe){

       System.out.println("Nastala chyba. Citanie - input - z file neprebehlo.");

       }        

      

      

        }       

}//EOClass

 

Ak kód 6.3 preložíme a potom spustíme kód 6.4 , dostaneme nasledujúci výpis:

 

Pracuj.java:4: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown

          virtuella.citajZaciatokNajdolezitejsiehoMailu();

                   ^

1 error

 

  Musíme v kóde 6.4 niečo zmeniť,  zareagovať na výskyt výnimky. Dve najčastejšie odpovede sú asi nasledujúce:

1.        Odpoveď lenivca, často provizórne riešenie pri prvom návrhu

import java.io.*;

public class Pracuj{

        public static void main(String[] args) throws FileNotFoundException{

          Sekretarka virtuella = new Sekretarka();    

          virtuella.citajZaciatokNajdolezitejsiehoMailu();

        }

       

}

 

Vyhodili sme výnimku von z metódy main a tak odovzdali starosti s možným vyskytnutím sa výnimky na plecia užívateľa tejto metódy. (Aby nám prekladač rozumel, museli sme ešte importovať balík java.io.*.)

 

2.        Ozajstné ošetrenie výnimky:

Kód 6.5

import java.io.*;

public class Pracuj{

        public static void main(String[] args) {

          Sekretarka virtuella = new Sekretarka();    

          try{

          virtuella.citajZaciatokNajdolezitejsiehoMailu();

          }

          catch(FileNotFoundException noFile){

                  System.out.println("Vsetkym sekretarkam: HLADAJTE TEN MAIL!");

          }

        }

       

}//EOClass

 

 

 

 

Konštrukcia try-catch-finally

Dva bloky, prislúchajúce  try catch sa musia v programe vyskytovať spolu za sebou. Môžeme, ale nemusíme, za ne pridať ešte tretí blok, začínajúci kľúčovým slovom finally. Čo je účelom tohto tretieho bloku? Zabezpečiť, aby určitý kód prebehol za všetkých okolností. Aj vtedy, keď v blokoch try alebo catch bude príkaz return, poprípade sa v nich vyvolá ďalšia, nezachytená výnimka.

Príklad: Kód 6.5A

import java.io.*;

public class Pracuj{

        public static void main(String[] args) {

          Sekretarka virtuella = new Sekretarka();    

          try{

   // naschval sposobim aritmeticku vynimku  

    int i = 5/0;

          virtuella.citajZaciatokNajdolezitejsiehoMailu();

          

          }

          catch(FileNotFoundException noFile){

                  System.out.println("Vsetkym sekretarkam: HLADAJTE TEN MAIL!");

          }

        }

//EOClass

Výpis:

java.lang.ArithmeticException: / by zero

 

        at Pracuj.main(Pracuj.java:6)

 

Exception in thread "main" Process Exit...

Kód 6.5B

import java.io.*;

public class Pracuj{

        public static void main(String[] args) {

          Sekretarka virtuella = new Sekretarka();    

          try{

       int i = 5/0;

          virtuella.citajZaciatokNajdolezitejsiehoMailu();

          

          }

          catch(FileNotFoundException noFile){

                  System.out.println("Vsetkym sekretarkam: HLADAJTE TEN MAIL!");

          }

          finally {

                  // nech sa dialo co sa dialo, vyznaj lasku sekretarke

                  System.out.println("Mozno neviete matematiku, slecna, ale milujem Vas.");

          }

        }

}

Výpis:

Mozno neviete matematiku, slecna, ale milujem Vas.

 

java.lang.ArithmeticException: / by zero

 

        at Pracuj.main(Pracuj.java:6)

 

Exception in thread "main" Process Exit...

 

Vytváranie vlastných výnimiek

 Niekedy sa prvky tried, ktoré sami vytvoríme, môžu dostať do stavu, ktorý považujeme za chybový. Na takéto prípady je dobré vytvoriť vlastnú výnimku. Používateľ už výnimku ošetrí tak, ako uzná za vhodné.  Použime znova našu známu triedu Tovar. Dopíšeme do nej metódu „predaj()“. Metóda si najprv zistí cenu tovaru. Ak je nulová alebo dokonca záporná (to by sme museli zákazníkovi ešte doplácať!), odmietne tovar predať a vyvolá výnimku. Najprv vytvorme triedu výnimky. Vytvorí sa ľahko dedením od triedy „Exception“:

Kód P6.6

public class VynimkaChybaVCene extends Exception {

          public VynimkaChybaVCene(){

                    super("Cena je zle nastavena.");

                    System.out.println("Zistite si spravnu cenu.");

          }

}

Teraz kód triedy Tovar s novou metódou a upravenou metódou „main()“:

Kód P6.7Pokus

public class Tovar {

         

          private int cena;

         

          public Tovar(int kolkoStoji){

                    System.out.println("Hlasi sa konstruktor triedy Tovar.");

                    cena = kolkoStoji;

          }

         

          public Tovar(){}

         

           public int getCena(){

                    return cena;

           }

         

           public void predaj () throws VynimkaChybaVCene {

                    if ( cena <= 0){

                     System.out.println("Pardon, tovar je momentalne nepredajny.");

                     throw new VynimkaChybaVCene();

                    }

                    else{

                    System.out.println("Zaplatite " + cena + " euro."); 

                    }       

                    }

          public static void main(String[] args){

         

            Tovar prvy = new Tovar( 22);

            Tovar druhy = new Tovar(0);

              prvy.predaj();

            druhy.predaj();   

          }

}//EndOFClass

Výpis po preložení takéhoto kódu znie:

Tovar.java:29: unreported exception VynimkaChybaVCene; must be caught or declared to be thrown

              prvy.predaj();

                ^

Tovar.java:30: unreported exception VynimkaChybaVCene; must be caught or declared to be thrown

            druhy.predaj();   

               ^

2 errors

 

Výnimku v triede main() musíme nejako ošetriť.  Uveďme najjednoduchšie, ale väčšinou nie najlepšie riešenie. A tiež výstup z kódu:

Kód P6.7

public class Tovar {

         

          private int cena;

         

          public Tovar(int kolkoStoji){

                    System.out.println("Hlasi sa konstruktor triedy Tovar.");

                    cena = kolkoStoji;

          }

         

          public Tovar(){}

         

           public int getCena(){

                    return cena;

           }

         

           public void predaj () throws VynimkaChybaVCene {

                    if ( cena <= 0){

                     System.out.println("Pardon, tovar je momentalne nepredajny.");

                     throw new VynimkaChybaVCene();

                    }

                    else{

                    System.out.println("Zaplatite " + cena + " euro."); 

                    }       

                    }

          public static void main(String[] args)throws VynimkaChybaVCene{

         

            Tovar prvy = new Tovar( 22);

            Tovar druhy = new Tovar(0);

              prvy.predaj();

            druhy.predaj();   

          }

}//EndOFClass

Výstup:

Hlasi sa konstruktor triedy Tovar.

Hlasi sa konstruktor triedy Tovar.

Zaplatite 22 euro.

Pardon, tovar je momentalne nepredajny.

Zistite si spravnu cenu.

VynimkaChybaVCene: Cena je zle nastavena.

        at Tovar.predaj(Tovar.java:19)

        at Tovar.main(Tovar.java:30)

Exception in thread "main" Process Exit...

 

Cvičenia

 

Cv6.1

Do adresára, v ktorom sa nachádzajú súbory Pracuj.class a Sekretarka.class uložte aj súbor najdolezitejsiMail.txt, ktorý má aspoň 1 riadok. Spustite program Pracuj ešte raz.

 

Cv6.2 V kóde Kód 6.3štvrtýPokus presuňte riadok

 

s textom „citatel.close()“ pred riadok  „String prvyRiadok = citatel.readLine();“. Preložte kód a spustite program „Pracuj“. Čo sa udialo?

Cv6.3 Spustite kód 6.5 najprv keď je v spoločnom adresári so súborom najdolezitejsiMail.txt. Súbor najdolezitejsiMail.txt potom dočasne premenujte a znovu spustite kód 6.5.

Cv6.4  Už poznáme, ako pracuje postupnosť blokov „try, catch, finally“. Ak chceme zabezpečiť,  aby sa nejaký úsek kódu vykonal vždy, stačí použiť iba dva bloky: „try“ a „finally“. Blok „finally“ je práve ten, ktorý musí vždy prebehnúť. Napíšte program, kde v bloku „try“ budete tlačiť prvok poľa, index poľa sa bude voliť náhodne. Dajte hodnotu indexu naprv vytlačiť spolu s dĺžkou poľa, potom dajte príkaz na vytlačenie príslušného prvku poľa. V bloku „finally“ vytlačte záverečný pozdrav. Sledujte, či sa vytlačí aj v prípade, keď index prekročí dĺžku poľa.

CV6.5

V kóde P6.7 ošetrite v metóde main()  použitie metódy „predaj()“ pomocou blokov „try“ a „catch“ tak, aby program „nespadol“ na výnimke „VynimkaChybaVCene“.

 

Riešenia

6.2:   Výpis z programu bude:

Nastala chyba. Citanie - input - z file neprebehlo.

Process Exit...

Prirodzene, čítanie neprebehlo preto, lebo prúd, Stream, ktorý bol po svojom vytvorení schopný čítať daný súbor, bol zavretý skôr, ako prišiel prvý príkaz na čítanie.

 

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

Vaše komentáre: kupka@fmph.uniba.sk