1. Klasa Throwable: korzeń wszystkich wyjątków
Teraz szczegółowo omówimy, jak działa system wyjątków w Javie: czym jest Throwable, czym różnią się Exception i Error, a także co oznacza „sprawdzane” i „niesprawdzane” wyjątki. To podstawa poprawnej obsługi błędów w twoich programach.
W Javie wszystkie wyjątki i błędy — to obiekty dziedziczące po klasie java.lang.Throwable.
Throwable — to „przodek” całej hierarchii obsługi problemów w Javie.
Schematycznie:
Throwable
├── Exception
└── Error
Throwable — klasa bazowa dla wszystkiego, co może zostać „wyrzucone” (throw) i „złapane” (catch) w Javie. Nie używa się jej bezpośrednio — służy jako podstawa dla bardziej konkretnych typów błędów.
Exception — dla „normalnych” błędów
Exception — klasa bazowa dla wszystkich wyjątków, które mogą wystąpić w programie i które można i należy obsługiwać. To „robocze” błędy: problemy z plikami, siecią, wejściem-wyjściem, błędami użytkownika itd. Większość twoich bloków try-catch będzie pracować właśnie z potomkami Exception.
Przykłady:
- IOException — błąd podczas pracy z plikami lub siecią.
- SQLException — błąd podczas pracy z bazą danych.
- FileNotFoundException — plik nie został znaleziony.
Error — dla błędów krytycznych JVM
Error — klasa bazowa dla błędów, które występują na poziomie wirtualnej maszyny Javy (JVM). Zwykle są to krytyczne awarie, których program nie może i nie powinien obsługiwać. Jeśli pojawi się Error — najprawdopodobniej aplikacja nie będzie w stanie kontynuować pracy.
Przykłady:
- OutOfMemoryError — brak pamięci.
- StackOverflowError — przepełnienie stosu (np. z powodu nieskończonej rekursji).
- NoClassDefFoundError — nie znaleziono wymaganej klasy.
Ważne:
Łapanie i obsługiwanie Error to prawie zawsze zły pomysł. To nie są błędy twojego programu, lecz awarie środowiska wykonawczego.
2. Checked vs Unchecked exceptions: co to oznacza?
W Javie wszystkie wyjątki dzielą się na dwie duże grupy:
Wyjątki sprawdzane (Checked)
Co to jest? Wyjątki, których kompilator wymaga obsłużenia lub jawnego przekazania dalej.
Kiedy występują? Zwykle są związane z zasobami zewnętrznymi: plikami, siecią, bazami danych, wejściem użytkownika.
Jak obsługiwać? Należy albo opakować kod w try-catch, albo dodać throws do sygnatury metody.
Przykłady: IOException, SQLException, FileNotFoundException
Przykład:
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // może zgłosić IOException
// ...
}
Jeśli nie obsłużysz lub nie przekażesz dalej — program się nie skompiluje!
Wyjątki niesprawdzane (Unchecked)
Co to jest? Wyjątki, które nie wymagają obowiązkowej obsługi przez kompilator.
Kiedy występują? Zwykle są to błędy w logice programu: dzielenie przez zero, wyjście poza zakres tablicy, odwołanie do null.
Jak obsługiwać? Można je łapać, ale nie jest to konieczne. Lepiej zapobiegać takim błędom poprzez odpowiednie sprawdzenia.
Gdzie w hierarchii? Wszystkie dziedziczą po RuntimeException.
Przykłady: NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException, ArithmeticException
Przykład:
int[] arr = {1, 2, 3};
System.out.println(arr[10]); // ArrayIndexOutOfBoundsException
Kompilator nie wymaga obsługi tego wyjątku — ale program zakończy działanie z błędem.
3. Cała hierarchia na jednym obrazku
graph TD
Throwable --> Error
Throwable --> Exception
Exception --> RuntimeException
Exception --> CheckedExceptions["(inne checked exceptions)"]
Error --> OutOfMemoryError
Error --> StackOverflowError
RuntimeException --> NullPointerException
RuntimeException --> IndexOutOfBoundsException
RuntimeException --> IllegalArgumentException
%% Style
style Throwable fill:#ffa64d,color:#000
style Exception fill:#ffa64d,color:#000
style CheckedExceptions fill:#ffa64d,color:#000
style Error fill:#ff4d4d,color:#fff
style OutOfMemoryError fill:#ff4d4d,color:#fff
style StackOverflowError fill:#ff4d4d,color:#fff
style RuntimeException fill:#4dff88,color:#000
style NullPointerException fill:#4dff88,color:#000
style IndexOutOfBoundsException fill:#4dff88,color:#000
style IllegalArgumentException fill:#4dff88,color:#000
Tabela: najważniejsze różnice
| Grupa | Klasa nadrzędna | Wymaga obsługi? | Przykłady |
|---|---|---|---|
| Checked Exception | |
Tak | |
| Unchecked | |
Nie | |
| Error | |
Nie | |
4. Jak to wygląda w kodzie?
Wyjątek checked: przykład z plikami
import java.io.*;
public class FileDemo {
public static void main(String[] args) {
try {
FileReader reader = new FileReader("nofile.txt"); // FileNotFoundException (checked)
int data = reader.read();
System.out.println(data);
reader.close();
} catch (IOException e) {
System.out.println("Błąd podczas pracy z plikiem: " + e.getMessage());
}
}
}
Kompilator wymusi obsłużenie IOException!
Wyjątek unchecked: przykład z dzieleniem przez zero
public class ExceptionDemo {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a / b; // ArithmeticException (unchecked)
System.out.println("Wynik: " + c);
}
}
Kompilator nie wymaga obsługi, ale program zakończy działanie z błędem.
5. Po co jest hierarchia wyjątków?
- Elastyczność obsługi: Można łapać zarówno konkretne błędy (FileNotFoundException), jak i całe grupy (IOException lub Exception).
- Wykorzystanie ponowne kodu: Można centralnie obsługiwać błędy jednego typu.
- Czystość kodu: Logika główna nie jest zaśmiecana sprawdzeniami każdego drobiazgu.
Przykład:
try {
// niebezpieczny kod
} catch (FileNotFoundException e) {
System.out.println("Plik nie został znaleziony!");
} catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia!");
} catch (Exception e) {
System.out.println("Coś poszło nie tak: " + e.getMessage());
}
6. Typowe błędy przy pracy z wyjątkami
Błąd nr 1: Ignorowanie wyjątków. Pisać catch (Exception e) {} — to zły pomysł! Tracisz informacje o przyczynie błędu.
Błąd nr 2: Łapiemy zbyt wiele. catch (Exception e) łapie wszystko jak leci, nawet to, czego się nie spodziewasz. Lepiej łapać tylko te wyjątki, które potrafisz obsłużyć.
Błąd nr 3: Łapiemy błędy (Error). Nie warto łapać Error, jeśli nie piszesz niskopoziomowego kodu. To problemy JVM, a nie twojego programu.
Błąd nr 4: Nie rozróżniamy checked i unchecked. Nie wszystkie wyjątki są takie same! Checked wymagają obsługi (Exception), unchecked — nie (RuntimeException i potomkowie).
Błąd nr 5: Nie dodajemy informacji do wyjątków. Jeśli tworzysz własne wyjątki — zawsze dodawaj informacyjny komunikat.
GO TO FULL VERSION