try, catch, finally… Obsługa wyjątków w Javie

Wyjątki są nierozłączną częścią programowania i choćbyśmy nie wiem jak się starali, to i tak nie uchronimy się przed ich pojawieniem. Jednak możemy odpowiednio zabezpieczyć się na wypadek wystąpienia wyjątku, czyli „wyłapać” wyjątek za pomocą bloków try, catch finally.

Wyobraźmy sobie program, który obsługuje dane obywateli. Obiekt Citizen ma między innymi pola z imieniem, nazwiskiem i adresem. Obiekt Address natomiast ma pole miasto (obiekt klasy City). Poniższy kod powinien więc zwrócić nam miasto zamieszkania obywatela.

Citizen citizen = new Citizen();
City city = citizen.getAddress().getCity();

Czy jednak każdy obywatel ma przypisany jakiś adres? Początkowo może wydawać się, że tak. I w danych testowych testowych też możemy nie trafić na taki przypadek, a jednak przecież to oczywiste, że są obywatele bez przypisanego jakiegokolwiek adresu. Co wtedy zwróci nasz kod? Mniej więcej coś takiego:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Address.getCity()" because the return value of "Citizen.getAddress()" is null

Nie obsłużyliśmy przypadku, w którym wystąpić mógł popularny NPE. Działanie programu zostało przerwane, a być może adres w ogóle nie jest kluczowy do żadnej funkcjonalności. I tu właśnie można użyć jako jedno z możliwych rozwiązań blok try.

Citizen citizen = new Citizen();
City city;
try {
    city = citizen.getAddress().getCity();
} catch (NullPointerException ex) {
    e.printStackTrace();
}

Dzięki temu program nie zostanie przerwany, lecz oczywiście nie będziemy mieć w takich przypadkach wypełnionego pola City. Należy jednak pamiętać, że dany blok catch obsługuje tutaj tylko ten określony wyjątek NullPointerException. Każdy inny spowoduje przerwanie działanie programu. Chyba nie do końca zalecanym, lecz spotykanym rozwiązaniem jest wychwycenie w bloku catch wszystkich wyjątków, czyli catch (Exception ex) {}.

Czym jest wspomniany blok finally? To blok, który zostanie wykonany niezależnie od tego czy wyjątek zostanie rzucony, czy nie. Jest to idealne rozwiązanie w przypadkach, w których wymagana jest jakaś akcja np. zamknięcie FileReadera. Blok finally nie jest wymagany po bloku try, podobnie jak catch. Musi zostać bowiem wykorzystana jedna z tych opcji. Poniżej przykład z blokiem catch finally z osobnym blokiem try-catch. Niestety stosując takie rozwiązania każdy możliwy przypadek wystąpienia wyjątku należy obsłużyć. Jednak i na to znajdzie się rozwiązanie, o czym poniżej.

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("citizen.txt"));
    citizen.setName(reader.readLine());
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

try-with-resources to blok, który pozwala oszczędzić parę linijek kodu. Dzięki umieszczeniu naszego readeara w nawiasach po słowie try zostanie on nawet automatycznie zamknięty. Nie musimy więc ręcznie wywołać metody close(). Prawda, że prościej?

try (BufferedReader reader = new BufferedReader(new FileReader("citizen.txt"))) {
    citizen.setName(reader.readLine());
} catch (IOException e) {
    e.printStackTrace();
}

Co warto zapamiętać z obsługi wyjątków blokami try, catch, finally lub try-with-resources?

  • są one pomocne, zwłaszcza dla wyjątków unchecked (tych, których obsługa nie jest konieczna);
  • po bloku try musi nastąpić blok catch lub finally;
  • blok finally zostanie wykonany niezależnie czy wyjątek zostanie rzucony, czy nie;
  • blok try-with-resources pozwala ograniczyć liczbę linijek kodu i odpowiada za zamknięcie wskazanego zasobów (resources);

Jeżeli macie uwagi bądź pytania, piszcie w komentarzach.

try, catch, finally… Obsługa wyjątków w Javie

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Przewiń do góry