CodeGym /Kursy /JAVA 25 SELF /Zaawansowana obsługa wyjątków i najlepsze praktyki

Zaawansowana obsługa wyjątków i najlepsze praktyki

JAVA 25 SELF
Poziom 24 , Lekcja 3
Dostępny

1. Wiele bloków catch: obsługa różnych wyjątków

W rzeczywistych programach ten sam blok kodu może zgłaszać różne typy wyjątków. Na przykład podczas odczytu pliku może wystąpić zarówno FileNotFoundException, jak i IOException, a przy parsowaniu danych — również NumberFormatException. Aby poprawnie obsłużyć każdą sytuację, w Javie można użyć kilku następujących po sobie bloków catch.

Składnia

try {
    // niebezpieczne operacje
} catch (FileNotFoundException e) {
    System.out.println("Nie znaleziono pliku: " + e.getMessage());
} catch (IOException e) {
    System.out.println("Błąd wejścia/wyjścia: " + e.getMessage());
} catch (NumberFormatException e) {
    System.out.println("Nieprawidłowy format liczby: " + e.getMessage());
}

Ważna uwaga:
Bloki catch powinny iść od bardziej specyficznych do bardziej ogólnych! Jeśli najpierw napiszemy catch (Exception e), to wszystkie pozostałe bloki staną się nieosiągalne, a kompilator zgłosi błąd.

Dlaczego to jest ważne?

  • Specyficzna obsługa: Można różnie reagować na różne błędy (np. zasugerować wybór innego pliku lub powtórzenie wprowadzania).
  • Czytelność: Kod staje się bardziej zrozumiały — widać, jakich błędów się oczekuje i jak są obsługiwane.

2. catch z wieloma typami (multi-catch)

Czasem dla różnych wyjątków potrzebna jest taka sama obsługa. Na przykład chcesz po prostu wyświetlić komunikat o błędzie lub go zalogować, niezależnie od tego, czy był to błąd odczytu pliku, czy konwersji liczby.

Od Java 7 pojawiła się składnia multi-catch, która pozwala połączyć kilka typów wyjątków w jednym bloku:

try {
    // niebezpieczna operacja
} catch (IOException | NumberFormatException e) {
    System.out.println("Błąd podczas przetwarzania pliku lub liczby: " + e.getMessage());
}

Jak to działa:

  • Typy wyjątków podaje się rozdzielone pionową kreską (|).
  • Zmienna e będzie miała typ najbardziej ogólnej klasy bazowej (zwykle Exception).
  • Wewnątrz bloku nie można przypisywać nowej wartości do zmiennej e — jest ona automatycznie traktowana jako final.

Przykład:

try {
    BufferedReader reader = new BufferedReader(new FileReader("numbers.txt"));
    String line = reader.readLine();
    int number = Integer.parseInt(line);
    System.out.println("Liczba: " + number);
    reader.close();
} catch (IOException | NumberFormatException e) {
    System.out.println("Błąd: " + e.getMessage());
}

3. Najlepsze praktyki obsługi wyjątków

Nie tłumcie wyjątków

Źle:

try {
    // coś niebezpiecznego
} catch (Exception e) {
    // nic nie robimy!
}

Dlaczego to jest złe?
Pusty blok catch „wycisza” błąd — tracisz informację o przyczynie awarii, a program staje się niemożliwy do debugowania.

Lepiej: przynajmniej wyświetl komunikat albo zaloguj błąd.

catch (Exception e) {
    e.printStackTrace();
}

Jeśli nie możesz obsłużyć wyjątku — przekaż go dalej:

catch (IOException e) {
    throw e; // albo throw new RuntimeException(e);
}

Rzucaj jak najbardziej specyficzne wyjątki

Jeśli piszesz własne metody lub API, zawsze rzucaj możliwie najbardziej konkretne wyjątki, a nie po prostu Exception lub RuntimeException. Dzięki temu twój kod i interfejs są bardziej zrozumiałe dla innych programistów.

Przykład:

public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException("Niewystarczające środki");
    }
    // ...
}

Nie używaj wyjątków do sterowania przepływem programu

Wyjątki są przeznaczone do obsługi sytuacji wyjątkowych — czyli błędów, które nie powinny występować w normalnym przebiegu działania programu. Nie używaj ich do zwykłej logiki (np. do wyjścia z pętli albo sprawdzania warunków).

Źle:

try {
    while (true) {
        String line = reader.readLine();
        if (line == null) break;
        // przetwarzanie wiersza
    }
} catch (Exception e) {
    // używamy wyjątku do wyjścia z pętli — źle!
}

Dobrze:

String line;
while ((line = reader.readLine()) != null) {
    // przetwarzanie wiersza
}

4. Praktyczne przykłady

Przykład: różna obsługa różnych błędów

try {
    BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
    int number = Integer.parseInt(line);
    System.out.println("Liczba: " + number);
    reader.close();
} catch (FileNotFoundException e) {
    System.out.println("Nie znaleziono pliku!");
} catch (IOException e) {
    System.out.println("Błąd odczytu pliku!");
} catch (NumberFormatException e) {
    System.out.println("W pliku nie ma liczby!");
}

Przykład: multi-catch dla jednakowej obsługi

try {
    // coś niebezpiecznego
} catch (IOException | NumberFormatException e) {
    System.out.println("Błąd: " + e.getMessage());
}

5. Ważne niuanse i typowe błędy

Błąd nr 1: Nieprawidłowa kolejność bloków catch. Najpierw powinny iść bardziej specyficzne wyjątki, a potem — ogólne. W przeciwnym razie wykonanie nigdy nie dotrze do szerokiego bloku.

Błąd nr 2: Użycie pokrewnych typów w multi-catch. Na przykład catch (IOException | Exception e) się nie skompiluje, ponieważ IOException dziedziczy po Exception.

Błąd nr 3: Próba zmiany zmiennej e w multi-catch. W takim przypadku e jest traktowana jako final — nie można przypisać jej nowej wartości.

Błąd nr 4: Puste bloki catch. Ignorowanie błędów prowadzi do „cichych” błędów. Minimum — wyświetlaj komunikat lub loguj wyjątek.

Błąd nr 5: Łapanie wszystkich Exception naraz. Użycie catch (Exception e) ukrywa rzeczywiste problemy i utrudnia debugowanie. Lepiej łapać tylko oczekiwane wyjątki.

1
Zadanie
JAVA 25 SELF, poziom 24, lekcja 3
Niedostępne
Raport finansowy: Dokładna diagnostyka problemów z danymi
Raport finansowy: Dokładna diagnostyka problemów z danymi
1
Zadanie
JAVA 25 SELF, poziom 24, lekcja 3
Niedostępne
Ustawienia gry: Jednolita reakcja na błędy podczas ładowania
Ustawienia gry: Jednolita reakcja na błędy podczas ładowania
1
Zadanie
JAVA 25 SELF, poziom 24, lekcja 3
Niedostępne
Tajne archiwum: Priorytet dostępu do danych poufnych
Tajne archiwum: Priorytet dostępu do danych poufnych
1
Zadanie
JAVA 25 SELF, poziom 24, lekcja 3
Niedostępne
Usługa logowania: Żaden błąd nie może zostać utracony!
Usługa logowania: Żaden błąd nie może zostać utracony!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION