if (x <= 0) {
throw new IllegalArgumentException("x was expected to be positive");
}
Tomáš Pitner, Radek Ošlejšek, Marek Šabo
Výjimky jsou mechanizmem umožňujícím reagovat na nestandardní (tj. chybové) běhové chování programu, které může mít různé příčiny:
chyba okolí: uživatele, systému
vnitřní chyba programu: tedy programátora.
Proč výjimky?
Mechanizmus, jak psát robustní, spolehlivé programy odolné proti chybám "okolí" i chybám v samotném programu.
Výjimky v Javě fungují podobně jako v dalších objektových jazycích (C++, Python). |
Výjimka, Exception
je objektem třídy (nebo podtřídy) java.lang.Exception
.
Existují 2 základní konstruktory:
Constructor | Description |
---|---|
NullPointerException() | Constructs a NullPointerException with no detail message. |
NullPointerException(String s) | Constructs a NullPointerException with the specified detail message. |
Preferujeme konstruktor se zprávou. |
Objekt výjimky je vyhozen:
automaticky běhovým systémem Javy, nastane-li nějaká běhová chyba — např. dělení nulou
samotným programem použitím klíčového slova throw
, zdetekuje-li nějaký chybový stav, na nějž je třeba reagovat
např. do metody je předán špatný argument
if (x <= 0) {
throw new IllegalArgumentException("x was expected to be positive");
}
Vyhozený objekt výjimky je buďto:
Zachycen v rámci metody, kde výjimka vznikla (v bloku catch
).
Výjimka propadne do nadřazené (volající) metody, kde je buďto v bloku catch
zachycena nebo opět propadne atd.
Výjimka tedy "putuje programem" tak dlouho, než je zachycena.
Pokud není nikde zachycena, program skončí s hlášením o výjimce.
Jestli výjimku nezachytíme, způsobí pád programu.
Proto existuje mechanismus try-catch
bloku, který umožňuje reagovat na vyhození výjimky.
try
blok vymezující místo, kde může výjimka vzniknout
catch
blok, který se vykoná, nastane-li výjimka odpovídajícího typu
Blok catch
výjimku zachytí.
Vyhození výjimky programátorem, výjimka je zachycena v catch
bloku:
try {
throw new NullPointerException();
} catch(NullPointerException e) {
System.out.println("NPE was thrown and I caught it");
}
Vyhození výjimky chybou programu (a její zachytění):
int[] array = new int[] { 16, 25 };
try {
int x = array[2];
} catch(ArrayIndexOutOfBoundsException e) {
System.err.println("Only index 0 or 1 can be used");
}
Jak funguje try
blok?
vykonává se příkaz za příkazen
jestli dojde k vyhození výjimky, další kód v try
se přeskočí a kontroluje se catch
blok
jestli nedojde k vyhození výjimky, kód se vykoná a bloky s catch
se ignorují
Jak funguje catch
blok?
syntax je catch(ExceptionType variableName) { … }
jestli se typ výjimky zhoduje anebo je nadtřídou, vykoná se kód catch
bloku
jestli se typ výjimky nezhoduje, výjimka není zachycena
catch
bloků může být víc, pak se prochází postupně
vždy se vykoná maximálně jeden catch
blok
catch
blokytry {
String s = null;
s.toString(); // NPE exception is thrown
s = "This will be skipped";
} catch(IllegalArgumentException iae) {
System.err.println("This will not be called");
} catch(NullPointerException npe) {
System.err.println("THIS WILL BE CALLED");
} catch(ArrayIndexOutOfBoundsException e) {
System.err.println("This entire block will be skipped");
}
catch
Catch blok kromě typu výjimky obsahuje i proměnnou, která se dá použít v rámci bloku:
try {
new Long("xyz");
} catch (NumberFormatException e) {
System.err.println(e.getMessage());
}
catch
blokůtry {
Person p = new Person(null);
} catch(NullPointerException e) {
System.err.println("Invalid name.");
} catch(IllegalArgumentException e) {
System.err.println("Invalid name.");
}
Operátor |
sloučí stejné catch
bloky:
try { ... }
catch(NullPointerException | IllegalArgumentException e) {
System.err.println("Invalid name.");
}
Jak můžeme na vyhozenou výjimku reagovat?
opakovat akci (např. znovu nechat načíst vstup)
poskytnout náhradu za chybný vstup (např. implicitní hodnotu)
sdělit chybu výše tím, že výjimku propustíme z metody (propadne z ní)
Oracle Java Tutorials: Lesson: Handling Errors with Exceptions |
catch
Pokud catch
řetězíme, musíme respektovat, že výjimka bude zachycena nejbližším příhodným catch
.
Překladač si ohlídá, že kód neobsahuje nedosažitelné catch-bloky, např:
try {
...
} catch (Exception e) {
...
} catch (IllegalArgumentException e) {
// won't compile, unreachable code
}
Výjimka z podtřídy (speciálnější) musí být zachycována dříve než výjimka obecnější. |
try {
...
} catch ( Exception e ) {
...
}
Problém: Zachytávame všechny výjimky, některé výjimky ale vždy chceme propouštět.
Řešení:
Použít v catch
speciálnější typ třídy.
try {
...
} catch ( NullPointerException e ) {
}
Problém:
Prázdny catch
blok — nedozvíme se, že výjimka byla vyhozena.
Řešení:
Logovat, vypsat na chybový výstup nebo použít e.printStackTrace();
try {
throw new NoSuchMethodException();
} catch ( NoSuchMethodException e ) {
throw e;
}
Problém: Kód zachytí a následně vyhodí stejnou výjimku.
Řešení:
Blok catch
smazat — je zbytečný.