Wyjątki są rzucane i propagowane tak długo jak długo pozostają nie przechwycone. Wyjątki nie przechwycone propagują się aż do metody main(…) i – jeśli także tam nie są przechwycone – powodują przerwanie wykonania programu. O tym w jaki sposób wyjątki są propagowane pisałem w artykule „Propagowanie wyjątków”. O tym, że pewne wyjątki trzeba deklarować i w jaki sposób to robić dowiadujemy się z artykułu „Deklarowanie wyjątków”. W niniejszym artykule nauczymy się wyjątki przechwytywać.
Aby przechwycić wyjątek rzucany przez pewien fragment kodu należy ten kod ując w klauzulę try-catch-finally. Składnia poniżej:
try { {kod programu} } catch( {deklaracja zmiennej dla obiektu wyjątku} ) { {kod obsługi błędu} } finally { {kod wykonywany zawsze} }
Kod którego wyjątki chcemy przechwytywać to {kod programu}. Element {deklaracja zmiennej dla obiektu wyjątku} to deklaracja zmiennej typu takiego jak obsługiwany wyjątek do której będzie przypisany obiekt rzuconego i przechwyconego wyjątku. Wewnątrz klauzuli catch umieszczamy kod obsługi błędu – kod ten będzie wykonany tylko i wyłącznie wówczas gdy kod {kod programu} rzuci wyjątek typu zgodnego z typem określonym w deklaracji {deklaracja zmiennej dla obiektu wyjątku}. Kod umieszczony wewnątrz klauzuli finally będzie wykonany zawsze, niezależnie od tego czy {kod programu} rzucił wyjątek czy nie.
Klauzula finally jest opcjonalna, za to klauzul catch może być dowolnie wiele. Może zdarzyć się – i często się zdarza – że kod ujęty w pojedynczej klauzuli try zwraca kilka różnych typów wyjątków. Możemy wtedy umieścić osobne klauzule catch dla obsługi każdego z tych wyjątków, ale możemy też umieścić jedną klauzulę catch do obsługi ich wszystkich – wystarczy, że korzystając z własności polimorfizmu (patrz artykuł „Polimorfizm”) określimy typ wyjątku jako Exception.
Zmodyfikujmy teraz przykład z artykułu „Deklarowanie wyjątków” tak, aby wyjątek rzucany w metodzie someOp(…) był przechwytywany i obsługiwany w metodzie main(…). Poniżej kod nie zawierający jeszcze obsługi wyjątku:
public class TestClass { public static void main(String[] args) throws Exception { System.out.println("Przed wywołaniem"); someOp(args); System.out.println("Za wywołaniem"); } static void someOp(String[] args) throws Exception { if (args.length == 0) throw new Exception("Brak parametrów"); System.out.println("Za wyjątkiem w metodzie"); } }
Powyższy przykład zmodyfikujemy w ten sposób, że metodę someOp(…) pozostawimy bez zmian, za to wywołanie tej metody w metodzie main(…) ujmiemy w klauzulę try. Metoda main(…) powinna wyglądać następująco:
public static void main(String[] args) { System.out.println("Przed wywołaniem"); try { someOp(args); System.out.println("Za wyjątkiem"); } catch (Exception exc) { System.out.println("Obsługa wyjątku"); } finally { System.out.println("Zawsze się wykona"); } System.out.println("Za wywołaniem"); }
Metoda someOp(…) zgłasza wyjątek, więc tekst „Za wyjątkiem w metodzie” nie zostanie wyświetlony – tu jeszcze nie ma obsługi wyjątku więc wykonanie metody będzie przerwane gdy zostanie on rzucony. Wyjątek rzucony w metodzie someOp(…) jest propagowany do metody main(…). W metodzie main(…) wyjątek ten otrzymujemy w rezultacie wywołania metody someOp(…). Wywołanie to jest ujęte w klauzulę try, więc wyjątek jest przechwycony – nie będzie przerwane wykonanie metody main(…), jednak będzie przerwane wykonanie kodu ujętego w klauzuli try, zatem tekst „Za wyjątkiem” nie zostanie wyświetlony. Od razu po wywołaniu metody someOp(…), z której otrzymaliśmy wyjątek, sekwencja wykonania przechodzi do kodu obsługi wyjątku – w naszym przypadku jest to wyświetlenie napisu „Obsługa wyjątku”. Po wykonaniu kodu obsługi wyjątku wykonywany jest kod z klauzuli finally, tak więc wyświetlany jest tekst „Zawsze się wykona”. Na tym kończy się sekwencja zdarzeń związanych z wyjątkiem. Wyjątek jest przechwycony i obsłużony, tak więc program może się teraz wykonywać normalnie – wykonanie metody main(…) nie jest przerywane a więc napis „Za wywołaniem” zostanie wyświetlony. Zauważmy także, że nie deklarujemy już że metoda main(…) rzuca wyjątek typu Exception (nie musimy w deklaracji metody main(…) pisać throws Exception). Wyjątek ten jest przez nas zawsze obsługiwany, tak więc nigdy nie będzie przez tą metodę zwrócony.