#03 — konwersja typów, obsługa wyjątków

By 28 August 2014 September 2nd, 2014 Kurs Javy

W tej lekcji wypełn­imy nasz obiekt kota braku­ją­cy­mi dany­mi — datą oraz masą. Jest to trud­niejsze zadanie, ponieważ użytkown­ik może wpisać cokol­wiek (np. “abcde”) a niekoniecznie musi to być popraw­na data / liczba.

W dal­szych lekc­jach nauczymy się bardziej poprawnego sposobu (uży­wa­jąc wyrażeń reg­u­larnych), na tę chwilę wyko­rzys­tamy możli­woś­ci obsłu­gi błędów (wyjątków) które daje nam język Java (miejmy na uwadze, że nie jest to dobre pode­jś­cie — powin­niśmy nie dop­uś­cić do pojaw­ienia się błę­du, a nie go obsługi­wać; zajmiemy się tym w kole­jnych lekcjach).

Lekcja

W tej lekcji nauczymy się, jak uży­wać pętli do-while, jak obsługi­wać błędy (wyjąt­ki) oraz jak kon­wer­tować daty.

Pętla do-while

Pęt­la do while służy do pow­tarza­nia pewnej czyn­noś­ci dopó­ki określony warunek jest prawdzi­wy. To, co cechu­je tę kon­strukcję to gwaranc­ja, że kod wewnątrz pętli zostanie wyko­nany co najm­niej raz. Kon­strukc­ja pętli wyglą­da następująco:

do {
    rozne_czynnosci();
} while (warunek_logiczny);

rozne_czynnosci() to oczy­wiś­cie jakikol­wiek kod, który chce­my wykon­ać (oczy­wiś­cie może to być wiele instrukcji, a nie tylko jedna).

Instrukcja sterująca break

Może się zdarzyć sytu­ac­ja, w której nieza­leżnie od warunku while () chce­my prz­er­wać pętlę dokład­nie w tym momen­cie nie wykonu­jąc nawet pozostałych instrukcji. Zobaczmy na poniższy przykład (oczy­wiś­cie ktoś może mieć inne zdanie ;) ):

do {
    Potrawa potrawa = wezZTalerzaKolejnaPotrawe();
    if (potrawa.getName().equals("brukselka") {
        break;
    }
    potrawa.zjedz();
} while (czyJestesGlodny());

 

W pętli tej, bierze­my kole­jną potrawę z talerza i zjadamy ją dopó­ki jesteśmy głod­ni. Jes­li jed­nak trafimy na bruk­selkę, naty­ch­mi­ast przes­ta­je­my jeść. Uży­wanie tej kon­strukcji jest nieza­le­cane, ponieważ nie jest bard­zo czytelne i może wprowadzać w bład innych pro­gramistów pracu­ją­cych z naszym kodem, cza­sem jest ono jed­nak niezbędne.

Instrukcję tą może­my uży­wać w każdym rodza­ju pętli w Javie (do-while, while, for).

Obrazowe działanie instrukcji break

Obra­zowe dzi­ałanie instrukcji break

Instrukcja sterująca continue

Podob­nie jak powyższa instrukc­ja, może ona być uży­wana w dowol­nej pętli (ale nigdy poza nią). Różni­ca pole­ga na tym, że con­tin­ue pomi­ja obec­ny krok i przeskaku­je do koń­ca bloku instrukcji (w przy­pad­ku pętli do-while, do sprawdzenia warunku). Mody­fiku­jąc wcześniejszy przykład w następu­ją­cy sposób:

do {
    Potrawa potrawa = wezZTalerzaKolejnaPotrawe();
    if (potrawa.getName().equals("brukselka") {
        continue;
    }
    potrawa.zjedz();
} while (czyJestesGlodny());

Tym razem nie prz­er­wiemy posiłku po natrafie­niu na bruk­selkę, a jedynie pominiemy jej zjedze­nie (jeśli trafimy na bruk­selkę to od razu ponown­ie sprawdz­imy, czy nadal jesteśmy głodni).

Obrazowe działanie instrukcji continue

Obra­zowe dzi­ałanie instrukcji continue

Wyjątki i ich obsługa

Wyjąt­ki w Javie to spec­jalne obiek­ty które mogą być nie tyle zwracane, co rzu­cane. Syg­nal­izu­ją one sytu­ację niecodzi­en­ną, która nie powin­na mieć miejs­ca. Ponieważ obsłu­ga wyjątków powodu­je duże zmi­any w sposo­bie wykony­wa­nia kodu naszej aplikacji, nie należy ich uży­wać do syg­nal­i­zowa­nia nor­mal­nych sytu­acji lub przekazy­wa­nia infor­ma­cji (co w pewnym stop­niu zro­bimy w tej lekcji, ale popraw­imy się w następ­nej!) — należy zawsze w miarę możli­woś­ci sprawdz­ić, czy wszys­tkie warun­ki są spełnione żeby wyjątek nie został rzucony.

Wyjąt­ki dzie­limy na dwa typy — te, które musimy obsłużyć (tzw. checked excep­tion) oraz te, których obsługi­wać nie musimy, ale może­my (unchecked — te, które dziedz­iczą po klasie Run­time­Ex­cep­tion; o dziedz­icze­niu i jego znacze­niu w Javie powiemy szerzej już wkrótce).

Pier­wszy rodzaj wyjątków to także takie, które sami tworzymy i rzu­camy. Rzu­canie wyjątków doty­czy sytu­acji w których pro­gra­mu­je­my ele­men­ty bard­zo niskopoziomowe (np. obsługę pro­tokołu) i raczej nie będziesz miała potrze­by robić tego w najbliższym cza­sie (w swo­jej kari­erze od kilku lat nie miałem sytu­acji, która wyma­gała­by rzuce­nia wyjątku przeze mnie w kodzie), dlat­ego pominiemy to.

Dru­gi rodzaj wyjątków to te, które obsługi­wać może­my, ale nie musimy (i cokol­wiek by się dzi­ało — nie tworzymy sami tego rodza­ju wyjątków!). To są wyjąt­ki, które mogą wys­tąpić w miejs­cach gdzie byśmy tego nie oczeki­wali (to duże uproszcze­nie i nie do koń­ca prawdzi­we, ale nie wiedzi­ałem jak to napisać poprawnie i zwięźle, a nie jest to bard­zo istotne :) ) i kod stał­by się bard­zo nieczytel­ny i wyma­gał­by dużo kodu do samej obsłu­gi wyjątków. Przykła­dem takiego wyjątku jest Null­Point­erEx­cep­tion który teo­re­ty­cznie mógł­by się pojaw­ić w każdym miejs­cu, gdzie wywołu­je­my jakąś metodę (czyli praw­ie wszędzie).

Jeśli chce­my zobaczyć jaki wyjątek może rzu­cić uży­wana przez nas meto­da zaglą­damy do doku­men­tacji. Oczy­wiś­cie środowisko Eclipse samo pod­powie nam jakie wyjąt­ki musimy obsłużyć. Ale nauczmy się przy okazji korzys­tać z doku­men­tacji (naw­iasem mówiąc — bard­zo dobrej) języ­ka Java.

Zobaczmy dla przykładu doku­men­tację dla metody Integer.valueOf(String string) (http://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#valueOf(java.lang.String)), która zamienia ciąg znaków w liczbę (opis, co robi ta meto­da, także zna­jdziemy w jej doku­men­tacji). W doku­men­tacji na dole jest sekc­ja ‘Throws’, która wyp­isu­je wyjąt­ki jakie meto­da może zwró­cić oraz krótko opisu­je sytu­ację, w której są one rzu­cane. Aku­rat w tym wypad­ku jest to wyjątek Num­ber­For­ma­tEx­cep­tion, który jest unchecked (po kliknię­ciu na niego może­my to sprawdz­ić — na samym początku jest infor­ma­c­ja o tym, że dziedz­iczy po Run­time­Ex­cep­tion, ponieważ klasa ta jest wyp­isana w ‘drzewku’).

Może­my więc spoko­jnie napisać poniższy fragment:

Integer liczba = Integer.valueOf("jeden");

I skom­pilu­je się on poprawnie, nato­mi­ast próbu­jąc uru­chomić taki pro­gram, otrzy­mamy wyjątek w trak­cie dzi­ała­nia pro­gra­mu (ponieważ go nie obsłużyliśmy). Nauczmy się obsługi­wać więc wyjąt­ki za pomocą bloku try-catch-final­ly, który ma następu­jącą budowę:

try{
    //kod który może rzucić wyjątek
} catch (TypWyjatku wyjatek) {
    //kod, który wykona się, kiedy wystąpi wyjątek
} finally {
    //kod, który wykona się zawsze, niezależnie czy wystąpi wyjątek czy nie
}

Jeśli chodzi o for­malne wymo­gi, to wyma­gana jest część try {}, oraz część final­ly {} lub jed­na (lub więcej) część catch (Typ­Wy­jatku nazwaZmi­en­nej) {} . Korzys­ta­jąc z tych infor­ma­cji, poprawne są wszys­tkie poniższe przykłady:

Integer liczba = null;
try {
    liczba = Integer.valueOf(jakisCiagZnakow);
} catch (NumberFormatException nfe) {
    System.out.println("Wyjątek - zły format liczby");
} catch (Exception e) {
    System.out.println("Wyjątek, ale jakiś inny");
} finally {
    System.out.println("To się zawsze wykonuje");
}
Integer liczba = null;
try {
    liczba = Integer.valueOf(jakisCiagZnakow);
} finally {
    System.out.println("To się zawsze wykonuje");
}
Integer liczba = null;
try {
    liczba = Integer.valueOf(jakisCiagZnakow);
} catch (NumberFormatException nfe) {
    System.out.println("Wyjątek - zły format liczby");
}
Integer liczba = null;
try {
    liczba = Integer.valueOf(jakisCiagZnakow);
} catch (NumberFormatException nfe) {
    System.out.println("Wyjątek - zły format liczby");
} catch (Exception e) {
    System.out.println("Wyjątek, ale jakiś inny");
}

W ramach ćwiczenia wyjaśnij, jakie są różnice pomiędzy nimi — odpowiedź napisz w komen­tarzu. Dla auto­ra najlep­szego wyjaśnienia bonus-niespodzianka ;)

Konwertowanie daty

Do kon­wer­sji ciągu znaków do daty może­my użyć np. klasy Sim­ple­Date­For­mat, która ofer­u­je bard­zo wiele możli­woś­ci. Aby ją utworzyć, musimy podać wzorzec daty, z którym będziemy pra­cow­ać. Wzorzec ten określa co wys­tępu­je w jakiej kole­jnoś­ci, oraz w jakim jest for­ma­cie. Klasy tej może­my uży­wać także do wyp­isy­wa­nia daty we wskazanym for­ma­cie. Zaczni­jmy od prostego przykładu:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");

Co oznacza­ją poszczególne liter­ki poz­wolę Ci samej odszyfrować z pomocą doku­men­tacji tej klasy :)

Jeśli mamy ciąg znaków i chce­my zamienić go w datę musimy użyć metody parse, która przyj­mu­je ciąg znaków i zwraca obiekt typu java.util.Date. Meto­da ta może rzu­cić wyjątek, który musimy obsłużyć, przykład­owe uży­cie wyglą­da więc następująco:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
Date date = null;
try {
    date = sdf.parse(dataJakoCiagZnakow);
} catch (ParseException pe) {
    System.out.println("Coś jest nie tak z formatem daty!");
}
//tu sprawdzamy

Jeśli data­Jako­Ci­agZ­nakow będzie w dobrym for­ma­cie, w miejs­cu gdzie jest komen­tarz ‘tu sprawdza­my’ zmi­en­na date będzie miała przyp­isany obiekt typu java.util.Date prze­chowu­ją­cy naszą datę. Jeśli jed­nak nasz ciąg znaków będzie w innym for­ma­cie niż określil­iśmy w Sim­ple­Date­For­mat, meto­da parse zwró­ci wyjątek, a zmi­en­na date nadal będzie miała wartość null (pod­powiedź: przy­da się to w zada­niu do dzisiejszej lekcji).

Jak sama widzisz, zami­ana ciągu znaków w datę jest banal­nie pros­ta i w łatwy sposób może­my zbu­dować włas­ny for­mat daty, jaki chce­my wczytywać.

Zadanie

Popraw klasę z poprzed­niego zada­nia tak, aby:

  • odczy­ty­wała i zapisy­wała w obiek­cie kot datę w for­ma­cie RRRR-MM-DD
  • odczy­ty­wała i zapisy­wała w obiek­cie kot jego masę w for­ma­cie zmiennoprzecinkowym 

Jeśli wal­i­dac­ja które­goś pola się nie powiedzie, pro­gram powinien pytać o poprawne dane aż do skutku (zan­im prze­jdzie dalej).

W ramach zada­nia do samodziel­nego czy­ta­nia, zapoz­naj się z mate­ri­ała­mi o pętli while a nastep­nie odpowiedz na poniższe pytania:

  1. Jak wyglą­da budowa pętli while? Czy moż­na jej użyć w powyższym zada­niu? Czy zmieni to sposób działania?
  2. Czym różni się pęt­la while od do-while?

zip Pobierz rozwiązanie tego zadania

#3 konwersja typów, obsługa wyjątków

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!