Rozdział 11 - Wyjątki - Czym są wyjątki?

W rozdziale o wartościach zwracanych przez metody korzystaliśmy z następującego przykładu:

Nazwa pliku: Rozdzial_07__Metody/WypiszWynikDzielenia.java
public class WypiszWynikDzielenia {
  public static void main(String[] args) {
    wypiszWynikDzielenia(10, 0);
    wypiszWynikDzielenia(25, 5);
  }

  public static void wypiszWynikDzielenia(int x, int y) {
    if (y == 0) {
      System.out.println("Nie mozna dzielic przez 0!");
      return;
    }

    System.out.println("Wynik dzielenia: " + (x / y));
  }
}

W metodzie wypiszWynikDzielenia wykonujemy sprawdzenie, czy liczba przekazana w argumencie y nie jest przypadkiem równa 0 – w takim przypadku nie możemy wykonać dzielenia.

Załóżmy, że nasza metoda ma zwracać wartość dzielenia, a nie ją wypisywać – zmodyfikujmy nasz przykład:

Nazwa pliku: ZwrocWynikDzielenia.java
public class ZwrocWynikDzielenia {
  public static void main(String[] args) {
    System.out.println(podziel(10, 0));
    System.out.println(podziel(25, 5));
  }

  public static int podziel(int x, int y) {
    return x / y;
  }
}

Metodę wypiszWynikDzielenia zastąpiliśmy metodą podziel, która zamiast wypisywać wynik na ekran, zwraca go. Jaki będzie wynik działania powyższego kodu? Na ekranie zobaczymy komunikat o błędzie dzielenia przez zero:

Exception in thread "main" java.lang.ArithmeticException: / by zero at ZwrocWynikDzielenia.podziel(ZwrocWynikDzielenia.java:8) at ZwrocWynikDzielenia.main(ZwrocWynikDzielenia.java:3)

Ten błąd to właśnie wyjątek, który został spowodowany nieprawidłowym działaniem naszego programu – w tym przypadku, Maszyna Wirtualna Java poinformowała nas, że wystąpił błąd dzielenia przez zero, a ten konkretny wyjątek nazywa się ArithmeticException.

Wyjątki (exceptions) to sytuacje, w których coś w programie poszło nie tak. Gdy zajdzie taka sytuacja, mówimy, że został rzucony wyjątek. Wyjątki mogą być rzucane zarówno przez Maszynę Wirtualna Java, jak i przez nas – programistów – co zobaczymy w dalszej części rozdziału.

Bardzo ważną cechą wyjątków jest to, że są to tak naprawdę klasy – mają one swoją nazwę, konstruktory, pola i metody. Co cechuje klasę, że może być traktowana jako wyjątek? Klasa taka musi rozszerzać klasę Throwable lub jedną z jej pochodnych, o czym wkrótce dokładniej sobie opowiemy. Rzucanie wyjątków sprowadza się do utworzenia słowem kluczowym new obiektu konkretnej klasy wyjątku i "rzucenie" go za pomocą słowa kluczowego throw. Zajmiemy się tymi zagadnieniami w kolejnych rozdziałach.

Z wyjątkami spotkaliśmy się już w poprzednich rozdziałach – widzieliśmy m. in. wyjątki:

  • StringIndexOutOfBoundsException – gdy próbowaliśmy odnieść się do znaku w zmiennej typu String za pomocą metody charAt przekazując indeks znaku wychodzący poza zakres stringu,
  • ArrayIndexOutOfBoundsException – gdy odnosiliśmy się do nieistniejącego elementu tablicy,
  • NullPointerException – gdy próbowaliśmy odnosić się do pól bądź metod niezainicjalizowanego obiektu, tzn. gdy zmienna typu złożonego wskazywała na null.

Inne sytuacje, w których moglibyśmy natknąć się na wyjątek, to na przykład:

  • podanie ujemnego wieku podczas tworzenia obiektu typu Osoba,
  • próba otwarcia pliku, który nie istnieje,
  • zerwanie połączenia z internetem podczas próby wysłania danych na serwer,
  • podanie nieprawidłowego hasła podczas łączenia się do serwera baz danych,
  • i wiele innych.

Sytuacje wyjątkowe możemy obsługiwać dzięki mechanizmowi łapania wyjątków, który poznamy w tym rozdziale.

Stack trace

Zauważmy w przykładzie powyżej, jak Maszyna Wirtualna Java prezentuje wyjątek:

Exception in thread "main" java.lang.ArithmeticException: / by zero at ZwrocWynikDzielenia.podziel(ZwrocWynikDzielenia.java:8) at ZwrocWynikDzielenia.main(ZwrocWynikDzielenia.java:3)

Po rodzaju wyjątku i skojarzonym z nim komunikatem w pierwszej linii, następują informacje o ścieżce wykonania programu, która doprowadziła do występienia tego wyjątku.

Jest to tzw. stack trace – ścieżka wykonań metod, które doprowadziły do błędu. Często będziemy analizować stack trace'y programując w Javie.

Stack trace powinno się śledzić się od dołu, ponieważ prezentowana w nim kolejność metod jest odwrotna do kolejności wykonywania tych metod – ostatnia metoda (ta na dole stack trace'a) została wywołana jako pierwsza, a ta na samej górze – jako ostatnia – i to w niej rzucony został wyjątek.

W praktyce jednak patrzymy zazwyczaj na kilka pierwszych linii stack trace'a, bo zazwyczaj wystarczają nam one do zrozumienia dlaczego, a przynajmniej gdzie, wystąpił wyjątek.

W naszym przypadku najpierw wywołana została metoda main z klasy ZwrocWynikDzielenia:

at ZwrocWynikDzielenia.main(ZwrocWynikDzielenia.java:3)

W nawiasach mamy dodatkowo podany plik, z którego klasa pochodzi, a także linię kodu, w której wykonanie metody przeszło do kolejnej metody – ta metoda, jak widzimy patrząc dalej na stack trace, to podziel:

at ZwrocWynikDzielenia.podziel(ZwrocWynikDzielenia.java:8)

Więcej metod nie zostało wywołanych – oznacza to, że wyjątek został rzucony właśnie w metodzie podziel. Dodatkowo mamy także podany numer 8 w nawiasach – to numer linii programu (a nie linii metody), w której wyjątek został rzucony. Są to bardzo przydatne informacje dla nas, programistów, podczas analizy błędów zaistniałych w naszych programach – dzięki stack trace'om łatwiej znaleźć miejsce, gdzie program zadziałał nieprawidłowo.

Jeżeli spojrzymy ponownie na kod naszej klasy:

Nazwa pliku: ZwrocWynikDzielenia.java
public class ZwrocWynikDzielenia {
  public static void main(String[] args) {
    System.out.println(podziel(10, 0));
    System.out.println(podziel(25, 5));
  }

  public static int podziel(int x, int y) {
    return x / y;
  }
}

to zobaczymy, że linia nr 3 umieszczona w nawiasach w stack trace odnosi się do:

System.out.println(podziel(10, 0));

a linia 8 do:

return x / y;

Oznaczenia linii w stack trace zgadzają się z wykonaniem programu, które doprowadziło do zaistnienia wyjątku ArithmeticException:

  • program zostaje uruchomiony – rozpoczyna się wykonywanie metody main,
  • w pierwszej linii metody main (zauważmy, że jest to jednocześnie trzecia linia całego programu) następuje wywołanie metody podziel z argumentami 10 i 0,
  • wykonanie programu przechodzi do metody podziel,
  • z racji tego, że podaliśmy 0 jako argument y, Maszyna Wirtualna Java rzuca wyjątek ArithmeticException, gdy próbujemy wykonać dzielenie przez 0 – dzieje się to w ósmej linii programu,
  • program kończy działanie, a na ekran zostaje wypisany zaistniały błąd: typ wyjątku, jego komunikat, oraz omówiony już stack trace.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Nie musisz podawać swojego imienia, e-mailu, ani strony www, aby opublikować komentarz. Komentarze muszą zostać zatwierdzone, aby były widoczne na stronie.