Znowu artykuł i znowu Java. Dzisiejszym tematem jest przechwytywanie wyjątków w języku Java, dowiemy się co to takiego, jak działa i jak się zabezpieczyć przed niepożądanym działaniem w trakcie korzystania z "ryzykownej" metody za pomocą klauzuli "try-catch". Jeśli nałogowo korzystacie z języka Java, opanujcie to jak najprędzej, to jest jeden z tematów podstawowych.
Tweet |
CZYM JEST WYJĄTEK I JAK ON DZIAŁA?
"Wyjątek" jest mechanizmem zmieniającym przebieg działania programu polegającym na wysyłaniu sygnału do systemu gdy nastąpi sytuacja nieoczekiwana, a także niepożądana. Dam przykład. Brakuje pliku tekstowego w podanym katalogu. Wyjątkiem będzie nieznalezienie wymaganego pliku o podanej ścieżce dostępu. Może to być również próba dzielenia przez zero i wtedy wyjątek jest oczywisty - sprzeczna równość niemająca sensu matematycznego. Jeśli dopiero zaznajamiacie się z wyjątkami, dobrze jest na sam początek wymyślić samemu jakieś sytuacje w których może on wystąpić. Takie proste rzeczy z życia wzięte.
Zanim przejdę do pokazywania na czym polega przechwytywanie wyjątków w języku Java, chcę napisać jeszcze o jednej istotnej rzeczy. Wyjątek w przeciwieństwie do błędu, może zostać "przechwycony" przez metodę i w ten sposób możliwe jest wykonanie pewnych instrukcji, która ten wyjątek "obsłuży" i zapobiegnie wysypaniu się programu. Wiadomym jest, że w większości przypadków nie da się "naprawić wyjątku". Można za to spróbować taki wyjątek "ładniej ubrać w słowa", osadzić w okienko komunikatu i jakoś podpowiedzieć użytkownikowi co KONKRETNIE może zrobić w tej sytuacji.
PRZECHWYTYWANIE WYJĄTKÓW W JĘZYKU JAVA JAKO PRZYKŁAD KODU ŹRÓDŁOWEGO
Pora na wyjaśnienie od strony "kodowej"! Pokażę Wam jak korzystać z przechwytywania wyjątków w kodzie źródłowym. Podstawowa struktura klauzuli "try-catch" zawsze składa się co najmniej z dwóch części (może być również trzecia, "finally" którą znajdziecie na samym dole):
try
{
ryzykownaMetoda();
}
catch (Exception e)
{
//instrukcje wykonywane w momencie przechwycenia wyjątku
}
Metoda "ryzykownaMetoda" posiada w naszym przykładzie instrukcje, które mogą się "zderzyć" z nieoczekiwaną sytuacją. Gdy takowa wystąpi, metoda "zgłasza" wyjątek i przekazuje go metodzie korzystającej z tej samej metody, która ten sygnał przesłała. Traktujcie "try-catch" jak punkt obsługi sygnału wysłanego przez wyjątek.
![]() |
Przechwytywanie wyjątków w języku Java jest możliwe po zastosowaniu klauzuli "try-catch".
PRZEKAZYWANIE SYGNAŁU DO POPRZEDNICH FUNKCJI
Możemy również sprawić, aby funkcja, która korzysta z ryzykownej metody, nie obsługiwała wyjątku tylko również go przekazała dalej, aby poprzednia metoda posiadała "try-catch". Tutaj przyda się znajomość wyrażeń ze słowem "throws". Po zakończeniu podawania argumentów formalnych w nagłówku funkcji, po zamknięciu nawiasu piszemy coś takiego:
void metoda() throws Exception
{
// instrukcje
ryzykownaMetoda();
// instrukcje
}
Ten zapis pozwala na pominięcie obsługi sygnału na tym "etapie" i przekazanie tej czynności poprzedniej funkcji, która ją wywołała. Dzięki temu, można zapisać klauzulę "try-catch" w poprzedniej metodzie. Zobaczcie przypadek gdy funkcja "main" posiada coś takiego.
PRZECHWYTYWANIE WYJĄTKÓW W JĘZYKU JAVA ROZŁOŻONE NA KILKA PRZYPADKÓW
Jak wiecie (albo i nie), wyjątki także są obiektami języka Java! Cenny wniosek nasuwa się taki, że one również mają swoje dziedziczenia. Samo "Exception" to klasa główna, a na przykład "ArithmeticException" dziedziczy od klasy bazowej. To z kolei oznacza, że przechwytywanie wyjątków w języku Java może dotyczyć wielu sytuacji w jednej klauzuli "try-catch". Pozwólcie, że zaprezentuję:
try
{
int n = 12 / 0;
ryzykownaMetoda();
}
catch (ArithmeticException ae)
{
System.out.println("Nie dzieli się przez zero!");
ae.printStackTrace();
}
catch (Exception e)
{
System.out.println("Pojawił się inny błąd niż dzielenie przez zero.");
e.printStackTrace();
}
Patrząc na ten fragment możecie dostrzec dwa kolejne istotne spostrzeżenia. Po pierwsze, można bez przeszkód podstawiać wiele bloków "catch", które mają na celu zapobiec każdemu przypadkowi wszystkich "ryzykownych" metod i instrukcji. Dlatego też należy dać osobny blok dla sytuacji dzielenia przez zero oraz dla innej dowolnej sytuacji, która może pojawić się w funkcji "ryzykownaMetoda". "try-catch" pozwala na takie rozgałęzienia.
Po drugie, bloki "catch" są sprawdzane po kolei idąc z góry na dół. To jest bardzo ważne, aby zaczynać od uwzględniania najbardziej szczególnych przypadków, a kończyć na klasie bazowej "Exception". Gdy wystąpi wyjątek i on "znajdzie" odpowiedni typ, przechodzi do niego, wykonuje instrukcje i KOŃCZY. To działa identycznie jak w instrukcji "switch" omawianej parę dni wcześniej albo nawet w zwykłej instrukcji warunkowej posiadającej kilka bloków "else if". Też wykonuje tylko jeden z nich, prawda?
"FINALLY" W BLOKU "TRY-CATCH"
Blok o powyższej nazwie jest całkowicie opcjonalny i zwykle jest rzadko wykorzystywany. Całość z "doczepionym" blokiem wygląda tak:
try
{
ryzykownaMetoda();
}
catch (Exception e)
{
//instrukcje wykonywane w momencie przechwycenia wyjątku
}
finally
{
System.out.println("Koniec ryzykownej operacji.");
}
Ta część klauzuli umieszczana jest zawsze na końcu "ciągu" bloków "catch" i jak wspomniałem, wykonuje się po przeprowadzeniu wszystkich operacji w "try" (gdy obyło się bez przeszkód) oraz określonego bloku "catch" (gdy funkcja napotkała problem i zgłasza go funkcji wywołującej), niezależnie od rezultatu. Jest rzadko widziana w kodach źródłowych z tego względu, iż po tej strukturze "try-catch" dalsze instrukcje i tak są nadal wykonywane w taki sam sposób. Zatem, użycie części "finally" przeważnie jest bezcelowe. Wniosek z tego taki, że możecie śmiało pisać dowolnie jak chcecie czyli pisać przechwytywanie wyjątków w języku Java z "finally" albo bez.
W taki oto sposób załatwiliśmy następny temat dotyczący ochrony programu.