1. Wprowadzenie
W programowaniu często zdarzają się sytuacje, gdy metoda natrafia na błąd, ale nie wie, jak go właściwie obsłużyć. Na przykład metoda czyta plik, lecz nie wie, co zrobić, jeśli pliku nie ma: zapytać użytkownika? Zakończyć program? Spróbować innego pliku? W takich przypadkach metoda może „przerzucić odpowiedzialność” na tego, kto ją wywołał – czyli „przekazać” wyjątek dalej w łańcuchu wywołań.
Przekazywanie wyjątku to mechanizm pozwalający metodzie nie obsługiwać błędu samodzielnie, lecz zakomunikować kodowi wywołującemu: „Mam tu problem, poradź sobie sam!”
Analogia: Wyobraź sobie, że jesteś pracownikiem call center. Dzwoni klient z pytaniem, na które nie znasz odpowiedzi. Zamiast zgadywać, mówisz: „Chwileczkę, połączę ze specjalistą”. Przekazujesz pytanie dalej.
Słowo kluczowe throws: jak działa
W Javie do przekazywania wyjątków używa się słowa kluczowego throws w deklaracji metody.
Składnia:
returnType methodName(...) throws ExceptionType
{
// kod metody
}
Po throws podaje się typ wyjątku, który może wystąpić w tej metodzie. To jak ostrzeżenie dla innych programistów: „Uwaga! Ta metoda może zgłosić wyjątek danego typu. Bądź przygotowany!”
Przykład:
public void readFile(String filename) throws FileNotFoundException
{
FileReader reader = new FileReader(filename); // może zgłosić FileNotFoundException
// ...
}
Tutaj metoda readFile nie obsługuje błędu sama, lecz informuje: „Mogę zgłosić FileNotFoundException – niech ten, kto mnie wywołał, zdecyduje, co zrobić”.
2. Jak powinien reagować kod wywołujący?
Jeśli wywołujesz metodę zadeklarowaną z throws, masz dwie opcje:
- Obsłużyć wyjątek za pomocą try-catch
- Również przekazać wyjątek dalej (dodać throws do własnej metody)
Wariant 1: obsłużyć przez try-catch
public static void main(String[] args)
{
try
{
readFile("data.txt");
}
catch (FileNotFoundException e)
{
System.out.println("Nie znaleziono pliku: " + e.getMessage());
}
}
Tutaj przechwytujemy wyjątek i sami decydujemy, co zrobić (np. wyświetlamy komunikat użytkownikowi).
Wariant 2: przekazać dalej
public static void main(String[] args) throws FileNotFoundException
{
readFile("data.txt");
}
Teraz odpowiedzialność za obsługę błędu spoczywa na tym, kto wywoła main (zwykle jest to sama JVM – jeśli błąd nie zostanie obsłużony, program zakończy się komunikatem o błędzie).
3. Przykład: odczyt pliku z przekazywaniem wyjątku
Pełny przykład z IOException i obsługą na poziomie main:
import java.io.*;
public class FileDemo
{
// Metoda deklaruje, że może zgłosić IOException
public static void printFirstLine(String filename) throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line = reader.readLine();
System.out.println("Pierwsza linia: " + line);
reader.close();
}
public static void main(String[] args)
{
try
{
printFirstLine("nofile.txt");
}
catch (IOException e)
{
System.out.println("Błąd podczas odczytu pliku: " + e.getMessage());
}
}
}
Metoda printFirstLine nie wie, co zrobić, jeśli pliku nie ma – po prostu przekazuje wyjątek dalej. W main przechwytujemy błąd i wyświetlamy komunikat.
4. Przydatne niuanse
Kiedy i po co używać przekazywania wyjątków?
- Gdy metoda nie może lub nie powinna decydować, jak obsłużyć błąd (np. kod biblioteczny).
- Gdy obsługa błędu zależy od kontekstu (w jednym przypadku zakończyć program, w innym – spróbować innego pliku).
- Aby nie zaśmiecać kodu zbędnymi try-catch tam, gdzie nie są potrzebne.
Best practice: Przekazuj wyjątki, jeśli nie możesz ich sensownie obsłużyć. Nie przechwytuj wyjątków „na odczepnego”!
Można przekazywać wiele wyjątków
public void process() throws IOException, SQLException
{
// ...
}
Wyjątki checked i unchecked: przypomnienie
Checked exceptions (np. IOException, SQLException) – kompilator wymaga ich obsługi lub przekazania dalej.
Unchecked exceptions (np. NullPointerException, IllegalArgumentException) – kompilator nie wymaga ich obsługi; nie trzeba ich wymieniać w throws.
5. Typowe błędy przy przekazywaniu wyjątków
Błąd nr 1: zapomniano zadeklarować throws dla wyjątku typu checked.
Jeśli metoda może zgłosić wyjątek typu checked, ale nie zadeklarowałeś go w throws ani nie obsłużyłeś przez try-catch – kompilator zgłosi błąd.
Błąd nr 2: przechwytujesz wyjątek, ale go nie obsługujesz.
Pisanie catch (Exception e) {} to zła praktyka! Lepiej przekazać wyjątek dalej, jeśli nie wiesz, co z nim zrobić.
Błąd nr 3: przekazujesz zbyt ogólny typ.
Jeśli metoda może zgłosić tylko FileNotFoundException, nie pisz throws Exception – to utrudnia zrozumienie kodu.
Błąd nr 4: wymieniasz wyjątki typu unchecked w throws.
Nie ma sensu pisać throws NullPointerException – kompilator tego nie wymaga i nie pomaga to w obsłudze błędów.
GO TO FULL VERSION