Obsługa wyjątków

Część pierwsza.

Wstęp

Pisanie kodu, tworzenie aplikacji jest niesamowitą przygodą. Niestety, nie jesteśmy w stanie napisać programu, który działałby zawsze. Problemy mogą nadejść ze strony nierozważnego użytkownika, próbującego użyć naszej aplikacji niezgodnie z przeznaczeniem lub np. ze strony systemu/sprzętu kiedy nie będziemy mogli uzyskać dostępu do zasobów potrzebnych uruchomienia programu. Do radzenia sobie z takimi zdarzeniami służy obłsuga wyjątków.

Jako programiści musimy przewidzieć takie zachowania i pomóc użytkownikowi oraz osobie odpowiedzialnej za utrzymanie naszej aplikacji na szybkie znalezienie problemu poprzez wskazanie zainteresowanym stronom, gdzie powstał problem.

Aby wprowadzić w życie powyższe założenie musimy dowiedzieć się czym są wyjątki w języku Java. Wyjątki (ang. exceptions) to właśnie takie zdarzenie, które muszą zostać obsłużone (np. użytkownik nie podał adresu email, a jest to wymagane). Biblioteki Javy dają nam dostęp do puli predefiniowanych wyjątków. Pewnie większość z czytających miała już niewątpliwą przyjemność spotkać NullPointerException albo ArrayOutOfBoundException. Więcej o wyjątkach w dokumentacji Javy.

Jak to ugryźć?

Na początku drogi do profesjonalizmu może nam się wydawać, że do wyjątków wystarczy instrukcja warunkowa. Niestety nie jest to element kodu, który można bezpiecznie użyć w tym przypadku.

Popatrzmy zatem na przykład w programie. Mamy tutaj metodę generującą wirtualne konto. Obiekt Customer posiada pola, nazwę (String) oraz id (long). Obiekt Money zawiera dwa pola BigDecimal, jako balans konta oraz jednostkę monetarną (PLN, EUR etc);

public Account generateNewAccountForCustomer(Customer customer, Money money) {
           if (!(customer.getName() == null)) {
               return new Account(money, customer);
           } else {
               System.out.println("Customer does not exists");
               return new Account();  // konstruktor domyślny
       }
   }

Wywołajmy taką metodę w klasie Main. Stworzę obiekt typu Customer używając konstruktora domyślnego.

public class Main {

   public static void main(String[] args) {
       Customer customer = new Customer();
       AccountFactory accountFactory = new AccountFactory();
       Account account = accountFactory.generateNewAccountForCustomer(customer, new Money(BigDecimal.valueOf(2500)));
       System.out.println(account.getCustomer().toString());
   }
}

Po uruchomieniu programu dostaję:

Customer does not exists
Exception in thread "main" java.lang.NullPointerException
	at com.company.main.Main.main(Main.java:07)

Jak widać, wysłałem informację o tym, że nie można znaleźć imienia użytkownika, jednak nadziałem się na inny wyjątek NullPointerException. Dzieje się tak dlatego, że domyślny konstruktor nie wymagał ode mnie podania wartości pól. Skoro nic nie podałem przypisał im wartości null. Próba odwołania się do wartości null często kończy się właśnie takim zachowaniem programu.

Rzucanie wyjątkiem

Skoro to nie jest najlepsza droga to, co należałoby zrobić?
Można “rzucić” wyjątkiem predefiniowanym np IllegalArgumentException. Jak się rzuca wyjątek pokaże poniższy przykład.

  public Account generateNewAccountForCustomer(Customer customer, Money money) {
           if (!(customer.getName() == null)) {
               return new Account(money, customer);
           } else {
               System.out.println("Customer does not exists");
               throw new IllegalArgumentException();  // rzucenie wyjątkiem
       }
   }

Odpowiedzią jest: 

Customer does not exists
Exception in thread "main" java.lang.IllegalArgumentException 
at  com.company.application.AccountFactory.generateNewAccountForCustomer(AccountFactory.java:06)
at com.company.main.Main.main(Main.java:06)

Program zatrzymuje się na wywołaniu metody generateNewAccountForCustomer (nie przechodzi w metodzie main() do linii w której chcemy wyświetlić dane użytkownika konta – linia 07 snippetu metody main() jw.).

Świetnie!!! 

System wskazał jakiego typu jest to błąd, w której linijce wystąpił i przekazał zdefiniowaną informację.

Łapanie wyjątku

Jeżeli naszą intencją było zakończenie działania programu po rzuceniu błędu, moglibyśmy zakończyć pracę tutaj. Jednak tak naprawdę nie obsłużyliśmy ew zachowania aplikacji po rzuceniu wyjątkiem. Jak wcześniej napisałem, aplikacja zakończyła swoje działanie i pokazała nam przyczynę. A co jeśli chcielibyśmy aby sytuacja wywołująca wyjątek została obsłużona w jakiś sposób w programie? Powiedzmy, że istotne jest aby sytuacja została zalogowana używając standardowego loggera.

W takim przypadku musimy w jakiś sposób przechwycić rzucony wyjątek i wykonać operację logowania, zanim działanie aplikacji zostanie wstrzymane. Aby przechwycić wyjątek musimy użyć konstrukcji try-catch.

W klasie w której będziemy obsługiwać logowanie można dodać jakiegoś loggera np: W tym przypadku będzie to klasa Main i w jej ciele dodajemy następujący fragment:

static Logger logger = Logger.getLogger(Main.class.getName());

Do wywołania metody dodajemy słowo throws, które wskazuje, że metoda może rzucić wyjątkiem.

public Account generateNewAccountForCustomer(Customer customer, Money money) throws IllegalArgumentException { ……}

Następnie w metodzie main() używamy bloku try-catch do złapania tego wyjątku i obsłużenia go na swój sposób.

public class Main {

   public static void main(String[] args) {
       Customer customer = new Customer();
       AccountFactory accountFactory = new AccountFactory();
       try {
   	Account account = accountFactory.generateNewAccountForCustomer(customer, new Money(BigDecimal.valueOf(2500)));
  	 System.out.println(account.getCustomer()toString());
            } catch (IllegalArgumentException e) {
                   logger.warning("Customer does not exists or not provided ");
                 }

   }
}

W wyniku otrzymujemy w konsoli informację ze złapanego wyjątku:

Customer does not exists
kwi 05, 2020 4:50:24 PM com.company.main.Main main
WARNING: Customer does not exists or not provided java.lang.IllegalArgumentException

Jak widać udało nam się przechwycić rzucony wyjątek IllegalArgumentException (nie odpaliła się część account.getCustomer().toString(), która rzuciłaby NullPointerException). Następnie wywołaliśmy logger’a, który zaalarmował użytkownika.

To już koniec części pierwszej. Już wiemy, że możemy rzucać wyjątkiem i mamy rozeznanie w tym, po co to robimy. W następnej części będzienmy tworzyć własny wyjątek, i tłumaczyć jeden wyjątek na inny. Do zobaczenia!

P.S. Chcesz zacząć przygodę z Javą? A może zastnawiasz się jak się przebranżowić? Zerknij na www.lekcjekodu.pl i pobierz darmowy ebook.

1 myśl na temat “Obsługa wyjątków

  1. Pingback: Obsługa wyjątków - { Lekcje Kodu }

Dodaj komentarz

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