Java 6
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. )
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:
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:
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.
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:
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:
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 throws a throw
tak, ako to vidíme nižšie:
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:
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
Dva bloky, prislúchajúce try a 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...
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“:
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()“:
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...
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.
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“.
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.
Vaše komentáre:
kupka@fmph.uniba.sk