– Cześć, Amigo. Dzisiaj czeka Cię ciekawa lekcja. Opowiem Ci o wyjątkach. Wyjątki (ang. exception) są specjalnym mechanizmem, który pozwala obsługiwać błędy w programie. Oto kilka przykładów błędów, które mogą pojawić się w programie:
1. Program może próbować zapisać plik, podczas gdy twardy dysk jest kompletnie zapełniony.
2. Program może próbować wywołać metodę na zmiennej przechowującej referencję o wartości null.
3. Program może próbować dzielić liczbę przez 0.
Wszystkie te akcje kończą się błędami. Zazwyczaj skutkuje to natychmiastowym przerwaniem działania programu, ponieważ w tym wypadku kontynuacja wykonywania kodu nie ma już sensu.
– Dlaczego?
– A jaki jest sens w toczeniu opony, jeśli samochód wypadł z drogi bądź spadł z klifu?
– Czyli program powinien przestać działać, tak?
– Tak. A przynajmniej tak do tej pory się działo. Wystąpienie jakiegokolwiek błędu przerywało działanie programu.
– To mądre podejście.
– Ale czy nie byłoby lepiej spróbować jednak kontynuować jego działanie?
– Tak. Załóżmy, że napisałeś w Wordzie długi tekst i zapisałeś go. Co jeśli operacja zapisywania się nie powiodła, ale Ty nie otrzymałeś o tym żadnej informacji? I piszesz sobie dalej. To nie miałoby sensu, prawda?
– Tak.
– Programiści mają na to interesujące rozwiązanie: każda funkcja ma zwracać status swojej pracy. 0 oznacza, że wszystko się powiodło. A każda inna wartość mówi nam, że pojawił się jakiś błąd i że zwracana wartość spowodowała błąd w funkcjonowaniu kodu.
– Oczywiście, to rozwiązanie ma także swoje wady. Po każdym (!) wywołaniu funkcji musisz sprawdzić zwracany kod (liczbę). Po pierwsze, to bardzo niewygodne: kod obsługujący błędy jest wykonywany rzadko, ale musi zostać umieszczony wszędzie. Po drugie, funkcje często zwracają różne wartości – tylko co dalej z nimi zrobić?
– Racja. Też o tym pomyślałem.
– Tak było do czasu, aż został stworzony mechanizm obsługi błędów i wyjątków. Oto, jak to działa:
1. Kiedy pojawia się błąd, Maszyna Java tworzy specjalny obiekt – wyjątek – w którym zapisuje wszystkie informacje o błędzie. Dla różnych błędów istnieją różne wyjątki.
2. Wyjątek sprawia, że program natychmiast wychodzi z bieżącej funkcji, potem z następnej i tak dalej – aż zostanie tylko metoda main. Następnie program zostaje przerwany. Programiści mówią także, że Maszyna Java „rozwija stos wywołań”.
– Przecież powiedziałeś, że nie zawsze program zostaje przerwany.
– Tak, ponieważ istnieje sposób, żeby obsłużyć wyjątek. Możemy napisać specjalny kod w odpowiednim miejscu, który obsłuży wyjątki, na których nam zależy i będzie umiał coś z nimi zrobić. To jest niezwykle ważne.
– Aby to zrobić, trzeba użyć specjalnego konstruktu, twierdzenia try-catch. Oto, jak ono działa:
Przykład programu, który obsługuje wyjątek (dzielenie przez 0) i pracuje dalej. |
---|
|
Wynik na ekranie: |
|
– A co z wyświetleniem „Po wywołaniu method1 Ten komunikat nigdy nie zostanie wyświetlony” na ekranie?
– Cieszę się, że o to pytasz. W linii 25 dzielimy przez 0, co skutkuje błędem – wyjątkiem. Maszyna Java tworzy obiekt ArithmeticException z informacją o błędzie. Obiekt jest wyjątkiem.
– Wyjątek pojawia się wewnątrz metody method1
. To powoduje natychmiastowe przerwanie metody. Skutkowałoby to przerwaniem metody main, gdyby nie blok try-catch.
– Jeśli wyjątek pojawi się wewnątrz bloku try, to zostanie obsłużony przez blok catch. Reszta kody w bloku try nie będzie wtedy wykonywana. Zamiast zostanie wykonany kod z bloku catch.
– Nic nie rozumiem.
– Innymi słowy, kod działa w ten sposób:
1. Jeśli wyjątek pojawi się wewnątrz bloku try, kod przestaje być obsługiwany w miejscu wystąpienia wyjątku, a zaczyna się wykonywanie bloku catch.
2. Jeśli nie pojawi się żaden błąd, to blok try jest wykonywany do końca, a blok catch nie jest wykonywany."
– Hę?
– Wyobraź sobie, że po każdym wywołaniu metody sprawdzamy, czy metoda zwraca oczekiwaną wartość czy może zostaje nagle przerwana w wyniku wyjątku. Jeśli wystąpił wyjątek, przechodzimy do wykonywania bloku catch (jeśli takowy istnieje), aby obsłużyć wyjątek. Jeśli nie ma żadnego bloku catch, przerywamy bieżącą metodę, a metoda, która została przez nas wywołana przeprowadza to samo sprawdzanie.
– Teraz już chyba rozumiem.
– Doskonale.
– Co oznacza „Exception” wewnątrz twierdzenia catch?
–Wszystkie wyjątki są klasami, które dziedziczą po klasie Exception. Możemy obsłużyć określony wyjątek przez wskazanie klasy wyjątku w bloku catch bądź możemy obsłużyć wszystkie wyjątki, wskazując ich wspólną klasę macierzystą – Exception. Następnie możemy uzyskać wszystkie niezbędne informacje o błędzie od zmiennej e (przechowuje ona referencję do obiektu wyjątku).
– Super! Jeśli w mojej metodzie pojawiają się różne błędy, to czy mogę przetwarzać je na różne sposoby?
– Nie tylko możesz, ale powinieneś. Możesz to zrobić w ten sposób:
public class ExceptionPrzyklad2
{
public static void main(String[] args)
{
System.out.println("Program rozpoczyna pracę");
try
{
System.out.println("Przed wywołaniem method1");
method1();
System.out.println("Po wywołaniu method1. Ten komunikat nigdy nie zostanie wyświetlony");
}
catch (NullPointerException e)
{
System.out.println("Referencja null. Wyjątek został obsłużony");
}
catch (ArithmeticException e)
{
System.out.println("Dzielenie przez zero. Wyjątek został obsłużony");
}
catch (Exception e)
{
System.out.println("Jakiekolwiek inne błędy. Wyjątek został obsłużony");
}
System.out.println("Program cały czas pracuje");
}
public static void method1()
{
int a = 100;
int b = 0;
System.out.println(a / b);
}
}
– Blok try może zostać połączony z kilkoma blokami catch, z których każdy będzie obsługiwał określone typy wyjątków.
– Myślę, że rozumiem. Nie umiem jeszcze sam tego napisać, ale jeśli zobaczę to w kodzie, to nie będę już przerażony.
GO TO FULL VERSION