Menu
Coddy logo textTech

Hierarchia klas wyjątków

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 59 z 87.

System obsługi wyjątków w Javie opiera się na dobrze zorganizowanej hierarchii klas, której korzeniem jest klasa Throwable. Zrozumienie tej hierarchii pomaga skuteczniej przechwytywać i obsługiwać wyjątki.

Na szczycie znajduje się Throwable, klasa nadrzędna wszystkich wyjątków i błędów. Ma dwie bezpośrednie klasy podrzędne:

Throwable
├── Error          // Poważne problemy (nie przechwytuj ich)
└── Exception      // Problemy, z których można wyjść
    └── RuntimeException  // Wyjątki niekontrolowane

Błąd oznacza poważne problemy, których aplikacje nie powinny próbować obsługiwać, takie jak OutOfMemoryError lub StackOverflowError. Wskazują one na fundamentalne problemy z samą maszyną JVM.

Wyjątek to gałąź, z którą będziesz pracować najczęściej. Dzieli się na dwie kategorie: wyjątki sprawdzane (bezpośrednie podklasy Exception) i wyjątki niesprawdzane (podklasy RuntimeException).

// Typowe klasy wyjątków
Exception
├── IOException           // Problemy z plikami lub siecią
├── SQLException          // Problemy z bazą danych
└── RuntimeException
    ├── NullPointerException
    ├── ArrayIndexOutOfBoundsException
    └── IllegalArgumentException

Ponieważ wyjątki są klasami, podlegają zasadom dziedziczenia. Gdy przechwytujesz wyjątek typu nadrzędnego, przechwytujesz również wszystkie jego typy potomne:

try {
    // jakiś kod
} catch (Exception e) {
    // Przechwytuje WSZYSTKIE wyjątki (zwykle zbyt szeroki zakres)
}
try {
    // jakiś kod
} catch (RuntimeException e) {
    // Przechwytuje tylko wyjątki czasu wykonania i ich podklasy
}

Ta hierarchia pozwala obsługiwać błędy tak szczegółowo lub ogólnie, jak tego potrzebujesz, i stanowi podstawę do tworzenia własnych niestandardowych wyjątków.

challenge icon

Wyzwanie

Łatwy

Zbudujmy analizator wyjątków, który pokaże, jak w praktyce działa hierarchia wyjątków w Javie! Utworzysz system, który przechwytuje wyjątki na różnych poziomach szczegółowości i informuje, jaki typ został przechwycony.

Rozmieścisz swój kod w trzech plikach:

  • ExceptionThrower.java: Utwórz klasę, która może wywoływać różne typy wyjątków na podstawie danych wejściowych. Twoja klasa powinna mieć trzy metody statyczne:

    triggerRuntime(String type) - W zależności od ciągu określającego typ rzuć różne wyjątki środowiska uruchomieniowego:

    • Jeśli typ to "null", rzuć NullPointerException z komunikatem "Null value encountered"
    • Jeśli typ to "index", rzuć ArrayIndexOutOfBoundsException z komunikatem "Invalid index"
    • Jeśli typ to "argument", rzuć IllegalArgumentException z komunikatem "Bad argument"

    triggerArithmetic() - Rzuć ArithmeticException z komunikatem "Division error"

    triggerGeneric() - Rzuć IllegalStateException z komunikatem "Generic runtime error" (jego hierarchia to Exception > RuntimeException > IllegalStateException)

  • ExceptionAnalyzer.java: Utwórz klasę, która przechwytuje wyjątki na różnych poziomach hierarchii i określa, co zostało przechwycone. Uwzględnij następujące metody statyczne:

    analyzeSpecific(String type) - Wywołaj ExceptionThrower.triggerRuntime(type) wewnątrz bloku try. Użyj oddzielnych bloków catch dla NullPointerException, ArrayIndexOutOfBoundsException i IllegalArgumentException. Dla każdego z nich wypisz: Caught specific: [exception class simple name], a następnie Message: [exception message]

    analyzeWithParent() - Wywołaj ExceptionThrower.triggerArithmetic() wewnątrz bloku try. Przechwyć wyjątek za pomocą RuntimeException (typu nadrzędnego). Wypisz: Caught via parent: RuntimeException, a następnie Actual type: [exception class simple name]

    analyzeWithGrandparent() - Wywołaj ExceptionThrower.triggerGeneric() wewnątrz bloku try. Przechwyć wyjątek za pomocą Exception (znajdującego się jeszcze wyżej w hierarchii). Wypisz: Caught via grandparent: Exception, a następnie Actual type: [exception class simple name]

  • Main.java: Połącz wszystkie elementy analizatora wyjątków! Otrzymasz jedno wejście: ciąg określający typ wyjątku (albo "null", "index", albo "argument").

    Najpierw wypisz === Specific Catch === i wywołaj analyzeSpecific, przekazując mu dane wejściowe.

    Następnie wypisz pustą linię, === Parent Catch === i wywołaj analyzeWithParent.

    Na koniec wypisz pustą linię, === Grandparent Catch === i wywołaj analyzeWithGrandparent.

Otrzymasz jedno wejście: typ wyjątku do wywołania ("null", "index" albo "argument").

Użyj e.getClass().getSimpleName(), aby uzyskać nazwę klasy wyjątku, oraz e.getMessage(), aby pobrać jego komunikat. To wyzwanie pokazuje, że przechwycenie wyjątku typu nadrzędnego obejmuje również wszystkie jego typy potomne: to kluczowe zagadnienie przy decydowaniu, jak szczegółowa powinna być obsługa wyjątków!

Spróbuj swoich sił

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String type = scanner.nextLine();
        
        // TODO: Wypisz "=== Specific Catch ===" i wywołaj ExceptionAnalyzer.analyzeSpecific(type)
        
        // TODO: Wypisz pusty wiersz, następnie "=== Parent Catch ===", a potem wywołaj ExceptionAnalyzer.analyzeWithParent()
        
        // TODO: Wypisz pusty wiersz, następnie "=== Grandparent Catch ===", a potem wywołaj ExceptionAnalyzer.analyzeWithGrandparent()
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Java online