Java logo Pred

Exceptions


Preco exceptions?

Kazdy, kto uz programoval, vie aka je to otrava, zabezpecit program pre rozne udalosti, ktore nie su zelane, ale vyskytujuce sa a to nie az tak zriedka. V principe treba osetrit kazdu moznu chybu v programe, pretoze iba tak dosiahneme jeho robustnost.
V klasickom Cecku, to vacsinou vyzera tak, ze funkcia vrati -1 pri chybe (alebo null). Na nas je potom testovat hodnotu, ktoru nam vrati a podla toho sa zariadit. Ale sami dobre vieme, ze je to velmi nepohodlne testovat *kazdu* funkciu a casto nechavame navratovu hodnotu niekde pomimo. Java obsluhuje chyby pri behu programu pomocou mechanizmu exceptions.


exceptions - vynimky implementuju mechanizmus zabezpecenia vzniknutej chyby pri behu programu a preklade.


Exception moze vzniknut z roznych pricin, ci uz z hardwerovych ( zakopnutie o sietovy kabel, havaria disku) alebo softwerovych ( delenie nulou, neexistencia subora).
Velmi vdacnym prikladom na exceptions je indexovanie pola. V pripade, ze sa snazime pouzit index vacsi ako sme uviedli pri deklaracii, potom musime ocakavat exception.

Zaciatok

Mechanizmus vyvolania exception

Pokial dojde k vyskytu chyby v metode, metoda vytvori objekt typu exception a preda ho systemu. V tomto objekte su uchovane informacie o stave programu pred vyvolanim exception a informacie o type exception. Potom interpreter najde miesto kde sa ma toto exception obsluzit a zavola tento kod, ktoremu preda ako parameter vytvoreny objekt typu exception.
Vytvorenie objektu typu exception a jeho predanie systemu, Americania nazyvaju throwing an exception.

Sposob vyhladania kodu na obsluhu exception

Otazkou je ako najst kod, ktory je schopny obsluzit exception daneho typu. Vsetko je postavene na organizacii tried a ich metod.

Ako priklad si mozeme uviest citanie zo subora. Predpokladajme, ze vo vasom objektovom navrhu mame tri metody metoda1, metoda2, metoda3. Samotne citanie je implementovane v metode3. My volame metodu1 a zaujima nas spracovanie chyby iba v tejto metode! Klasicke spracovanie chyb vyzera priblizne takto:
metoda1 {
	int error;
	error = call metoda2;
	if (error)
		OsetriChybu;
	else
		Pokracuj;
}

int metoda2 {
	int error;
	error = call metoda3;
	if (error)
		return error;
	else
		Pokracuj;
}

int metoda3 {
	int error;
	error = call readFile;
	if (error)
		return error;
	else
		Pokracuj;
}
Ako vidime, v pripade, ze nastane chyba, posuvame ju v zasobniku od metody3 az k metode1, cez metodu2.
Jazyk Java nam umoznuje nasledovny zapis.
metoda1 {
	try {
		call metoda2;
	} catch (ErroReadException) {
		OsetriChybu;
	}
}

metoda2 throws ErrorReadException {
	call metoda3;
}

metoda3 throws ErrorReadException {
	call readFile;
}
V pripade, ze volame metodu1, a nastane chyba pri citani v metode3, ta vyvola (throws) exception ErrorReadException, ktore potom zachyti metoda1 tym sposobom, ze system prehladava zasobnik a nakoniec najde kod, ktory dokaze spracovat vzniknute exception v metode1 na zaklade catch (ErrorReadException).
Teraz je nam mozno jasne, ze kazde exception, ktore moze nastat musi byt spracovane na 'nejakej' urovni, aby system mohol najst obsluhu tohoto exception.

Zaciatok

Skupiny exceptions

Casto spadaju exceptions do skupin. Napriklad v pripade poli moze nastat niekolko exceptions, avsak jednotlive metody mozu obsluhovat niektore exceptions detailnejsie. Majme nasledujucu skupinu exceptions:
Exception  // zakladne exception, od ktoreho sa odvadzaju dalsie
 +
 |
 +
ArrayException -- InvalidIndexException   // pri adresovani do pola
               +- NoSuchElementException  // pri vyhladavani v poli
               +- ElementTypeException    // pri vkladani do pola
Ma to vyznam ak chceme v niektorej metode obsluzit vsetky exception, ktore sa mozu vyskytnut pri praci s polom. Potom pomocou
	catch (ArrayException e) {
		...  // osetrenie exception
	}
osetrime vsetky chyby pri praci s polom.


Zaciatok

try - catch - finally, throw, throws

Exceptions sa implementuju v tychto blokoch try, catch, finally. Niekedy vsak nie je vyhodne zachytavat mozne exceptions, ale je lepsie sa ich vzdat a predat ich inej metode (pomocou zasobnika). Toto mozeme dosiahnut pomocou throws. Okrem toho existuje aj podobny prikaz throw, ktory vytvara/ vyvolava exception.



Zaciatok

try blok

Do tohto bloku zahrnieme vsetok kod, ktory moze vyvolat exception, cize kde moze vzniknut chyba (napr. pri otvarani subora). V praxi to vyzera takto:
	try {
		OtvorSubor(NazovSubora);
	}
	...
Pri rozhodovani, ci dane volanie metody by sme mali/nemali zahrnut do bloku try, by nam malo pomoct to, ci volana metoda vyvolava (throws) exception. Druhou vecou je, ci tuto metodu budeme volat my, alebo bude volana inou metodou, ktoru budeme pouzivat namiesto nej. V pripade, ze ju budeme volat priamo, mali by ju uviest v bloku try a zachytit potrebne exception. Vacsinu exception musime obsluzit v metode, ktora je na poslednom stupni volania. Toto je kontrolovane priamo prekladacom a taketo exceptions volame checked exceptions. V inom pripade, ak danu metodu metoda1 chceme len vyuzit v dalsej metode metoda2, ktora ju vola a my budeme priamo vyuzivat len metodu metoda2, potom staci obsluzit exceptions len v metode metode2 a v metode metoda1 staci vyvolat (posunut) vzniknute exceptions metode, ktora ju volala.
Citim, ze by sa tu hodil priklad, ktory sme uz sice uviedli, ale je este aktualnejsi ako pred tym.
metoda1 {
	try {
		call metoda2;
	} catch (ErroReadException) {
		OsetriChybu;
	}
}

metoda2 throws ErrorReadException {
	call metoda3;
}

metoda3 throws ErrorReadException {
	call readFile;
}
Ako ste si asi vsimli blok try nikdy nie je uvedeny sam. Pravidlo je take, ze musi byt za nim aspon jeden blok catch alebo jeden blok finally. Podme sa rychlo pozriet na catch.

Zaciatok

catch blok

S kazdym try blokom musime spojit aspon jeden catch blok, aby bolo mozne, v pripade vzniku exception urcit, kto/co bude obsluhovat vzniknute exception. Tychto catch blokov moze byt aj viac ako jeden, na jeden blok try.
Ukazme si najprv ten jednoduchsi priklad.
	try {
		handle = OtvorSubor(NazovSubora);
	} catch (ErrorOpenFileException e) {
		VykonajObsluhuChyby;
	}
V jednom bloku try mozem vsak vykonat aj viac metod, ktore mozu vyvolat aj viac druhov exceptions. Potom potrebujeme uviest prave tolko blokov catch, kolko potencialnych exceptions mozeme ocakavat.
Povedzme si, ze hned ako otvorime subor, chceme z neho citat udaje. Nas predchadzajuci priklad sa zmeni nasledovne:
	try {
		handle = OtvorSubor(NazovSubora);
		CitajUdaje(handle);
	} catch (ErrorOpenFileException e) {
		VykonajObsluhuChyby_NemozemOtvoritSubor;
	} catch (ErrorReadFileException e) {
		VykonajObsluhuChyby_NemozemCitatZoSubora;		
	}
Argumentom catch moze byt lubovolny objekt odvodeny od triedy Throwable a popisuje typ exception.



Zaciatok

finally blok

Tento posledny blok je urceny pre 'zaverecne upratovanie' po obsluhe exception.
System vzdy vola blok finally, nezalezi na tom, co sa stane v bloku try.
Nas priklad s otvaranim suboru by potom mohol vyzerat takto:
	try {
		handle = OtvorSubor(NazovSubora);
	} catch (ErrorOpenFileException e) {
		VykonajObsluhuChyby;
	} finally {
		if ( handle )     // je dobre to testovat
			ZatvorSubor(handle);
	}


Zaciatok

throw & throws

Ludovo povedane: prikaz throw vytvara nove exception a preda ho systemu. Prikaz throws ho posuva po zasobniku az ku najblizsiemu bloku catch. Z prikladu to bude urcite jasnejsie:
public OpenFileId OpenFile(String fname) throws FileNotFoundException {
	OpenFileId = new OpenFileId();

	OpenFileId = OpenFile(fname);
	if ( ! OpenFileId )
		throw new FileNotFoundException();

	return FileOpenId;
}
Metodu OpenFile(...) mozeme dalej pouzit v inych triedach, resp. ich metodach. Tam mozeme znovu obist obsluhu exception posunutim ho dalsej metode podla zasobnika. Naviazanim na predchadzajuci priklad ...
public OpenFileId OpenTXTFile(String fname) throws FileNotFoundException {
	return OpenFile(fname);
}
Na koniec obsluhe exception aj tak neujdete. Volanie metody OpenTXTFile. V realnom svete Javy to vyzera priblizne takto:
class PrintFile {

public static void main(String argv[]) {
	OpenFileId of;

	try {
		of = OpenTXTFile(arg[0]);
	} catch (FileNotFoundException e) {
		System.err.println("Nemozem otvorit subor" + arg[0]);
		System.out.println("usage: java PrintFile 'txtfile'");
	}
	...
}

}// class PrintFile


Zaciatok

Vytvorenie noveho exception

Ukazme si na skutocne realnom - skompilovatelnom - priklade, ako vytvorit nove exception, ako ho obsluzit v konecnej aplikacii.
/* 
   Definujem si nove exception. Budem ho volat 'IsNullException' a
   nastane vtedy, ak chcem vypisat na obrazovku retazec
   pomocou triedy PrintString.
*/

/*
   kazde exception musi byt odvodene od triedy 'Throweable'
   alebo od triedy, ktora je potomkom tejto triedy, ci uz
   priamym alebo nepriamym.
*/
import java.lang.Throwable;

// samotna definicia naseho exception

public class IsNullException extends Throwable {

	public IsNullException() {
		super();    // volam konstruktor z 'Throweable'
	}

	public IsNullException(String s) {
		super(s);   // volam konstruktor z 'Throweable'
	}

}//class IsNullException

// nebolo to nic zlozite a mnoho exceptions, ktore v Jave najdete
// , vyzera presne takto (boli odvodene rovnakym sposobom)


Zaciatok

Ako zakomponovat nove exception do kodu.

/*
   trieda 'PrintString' sluzi na vypisovanie retazcov na
   obrazovku, na standardny vystup. V pripade, ze sa pokusim
   vypisat retazec, ktory neexistuje, cize je 'null', tak
   vyvolam IsNullException,
   definovane v predchadzajucom priklade.
*/

// importnem si potrebne triedy
import java.lang.String;
import java.io.*;

// realizacia vypisovania na obrazovku

public class PrintString {

    String str;

    public PrintString() {    // konstruktor bez parametrov
        str = null;
    }

    public PrintString(String s) { // konstruktor s parametrami
        str = new String(s);
    }


    // pomocou tejto metody zabezpecim samotne vypisovanie

    public void PrintStr(String s) throws IsNullException {
        if ( s == null )  // ak retazec neexistuje => throw exception
            throw new IsNullException("***IsNullException***");
        else
            System.out.println(s);
    }

    // podobne obsluzim aj bezparametricke volanie PrintStr()

    public void PrintStr() throws IsNullException {
        if ( str == null )
            throw new IsNullException("***IsNullException***");
        else
            System.out.println(str);
    }


}//class PrintString


Zaciatok

A vyuzitie v konecnej aplikacii - A

/*
   Tento priklad ukazuje ako pouzit throws, pri NEobsluhe exception.
   Vyuzivame triedy IsNullException a 
   PrintString.
*/

// importnem si potrebne triedy (uz to omielam stale dookola)
import java.io.*;


// hlavna trieda - Applikacia
public class ExceptionApp4 {

    public static void main(String argv[]) throws IsNullException {

    PrintString meno = new PrintString("Lena");
    PrintString priezvisko = new PrintString();

    // ak si to spustite, mali by ste to hned pochopit
    meno.PrintStr();
    priezvisko.PrintStr();

    }//public static void main(String argv[])

}//class ExceptionApp4
V pripade, ze spustime triedu ExceptionApp4 dostaneme vysledok:
C:\WEB-DATA\Java\jazyk\priklady>java ExceptionApp4
Symantec Java! ByteCode Compiler Version 1.02a
Copyright (C) 1996 Symantec Corporation
Lena
IsNullException: ***IsNullException***

C:\WEB-DATA\Java\jazyk\priklady>
// ani som necakal, ze to nakoniec pochopim, ale stalo sa!
// TIP pre tych, ktori sa dostali az sem:
//        V tomto priklade som zistil, ze ak mate nejaku triedu
//        zadefinovanu ako 'public', potom musi byt uvedena v
//        samostatnom subore, ktory ma take iste meno ako dana
//        'public' trieda.


Zaciatok

A vyuzitie v konecnej aplikacii - B

/*
   Tento priklad ukazuje ako pouzit try-catch, pri obsluhe exception.
   Vyuzivame triedy IsNullException a 
   PrintString.
*/

// (vy uz viete co ...)
import java.io.*;

// hlavna trieda - Aplikacia
public class ExceptionApp4b {

    public static void main(String argv[]) throws IsNullException {

    PrintString meno = new PrintString("Lena");
    PrintString priezvisko = new PrintString();

    // ak vznikne chyba => jej vlastna obsluha
    try {
        meno.PrintStr();
        priezvisko.PrintStr();
    } catch (IsNullException e) {
        System.err.println("Nastalo IsNullException");
    }

    }//public static void main(String argv[])

}//class ExceptionApp4b
V pripade, ze spustite triedu ExceptionApp4b, dostanete vystup
C:\WEB-DATA\Java\jazyk\priklady>java ExceptionApp4b
Symantec Java! ByteCode Compiler Version 1.02a
Copyright (C) 1996 Symantec Corporation
Lena
Nastalo IsNullException

C:\WEB-DATA\Java\jazyk\priklady>

Zaciatok