• Nie Znaleziono Wyników

Java niezbędnik programisty spotkanie nr 6. Obsługa błędów

N/A
N/A
Protected

Academic year: 2022

Share "Java niezbędnik programisty spotkanie nr 6. Obsługa błędów"

Copied!
30
0
0

Pełen tekst

(1)

spotkanie nr 6

Obsługa błędów

(2)

Obsługa błędów

Mechanizm obsługi błędów jest wbudowany w język.

Nietypowe sytuacje (błędy na które nie można w danym kontekście zareagować) są reprezentowane przez specjalne obiekty – wyjątki.

Aby ułatwić przekazywanie wyjątków pojawiających się w ciągu

zagnieżdżonych wywołań istnieje możliwość przerwania normalnego toku wykonania programu i zgłoszenia wyjątku przy pomocy instrukcji

throw.

Wyjątki, które mogą być zgłoszone na zewnątrz muszą (wyjątkowe wyjątki będą omówione później) być wymienione w klauzuli throws

każdej metody.

(3)

Przykład

class Wyjątki {

public static void main(String[] args) throws Exception { (new Wyjątki()).głęboko();

}

void głęboko() throws Exception { głębiej();

}

void głębiej() throws Exception { throw new Exception();

//kompilator wykryje miejsca, do których //sterowanie nigdy nie dotrze

(4)

Nieobsłużone wyjątki

Nieobsłużone wyjątki wydostając się poza main powodują przerwanie wykonania programu oraz wypisanie komunikatu o błędzie:

Exception in thread "main" java.lang.Exception at niezbędnik6.Wyjątki.main(Wyjątki.java:34)

Szczegółowość informacji o umiejscowieniu błędu w kodzie zależy od ustawień kompilatora.

(5)

Obsługa błędów

Obsługa zgłaszanych wyjątków jest możliwa dzięki blokom try i catch.

Blokiem try otaczamy instrukcje (np. wywołania metod) mogące zgłosić wyjątek.

W C trzeba było obsługiwać każdą instrukcję po kolei

Klauzula catch pełni rolę metody wykonywanej aby obsłużyć wyjątek, jeżeli może on być użyty jako jej argument. Jeżeli typ wyjątku nie pasuje, nie jest obsługiwany w tym bloku, a jego zgłaszanie jest kontynuowane.

try { //...

} catch (Typ wyj) {

(6)

Przykład

class Wyjątki {

public static void main(String[] args) { //throws Exception { (new Wyjątki()).głęboko();

}

void głęboko() { //throws Exception { try {

głębiej(); //obsługujemy cały blok

głębiej(); //a nie tylko pojedynczą instrukcję throw new Exception();

} catch (Exception e) {

System.out.println("obsługuję wyjątek");

} }

void głębiej() throws Exception { throw new Exception();

(7)

Throwable i jej podklasy

Jedynie obiekty dziedziczące po Throwable mogą być używane jako wyjątki. Podstawowym źródłem informacji o rodzaju błędu jest klasa reprezentująca wyjątek.

Zalecane jest aby każda klasa reprezentująca wyjątek miała przynajmniej dwa konstruktory: bezargumentowy oraz przyjmujący napis z

dodatkowym opisem błędu.

Od wersji 1.4 do Throwable dodano mechanizm podczepiania wyjątków, które spowodowały dany wyjątek (często obsługa wyjątku polega na

zgłoszeniu innego) - kolejne dwa konstruktory oraz metoda initCause().

Metoda initCause()jest przykładem niekonsekwencji, bo opis błędu można ustawić jedynie za pomocą konstruktora.

(8)

Throwable i jej podklasy

Throwable ma dwie podklasy Error i Exception.

Error to poważne błędy niezwiązane z działaniem aplikacji, np.

VirtualMachineError, które nie powinny się zdarzyć. Metody nie mają obowiązku deklarować możliwości ich wystąpienia, a zadeklarowanie nie ma żadnego znaczenia.

Exception to błędy, na które aplikacja powinna reagować.

RuntimeException to podklasa Exception reprezentująca typowe błędy powstające podczas działania maszyny wirtualnej, np.

NullPointerException. Te wyjątki są wyróżnione i tak jak w wypadku

Error nie ma obowiązku ich deklarować.

(9)

Własne wyjątki

class MójWyjątek extends Exception { MójWyjątek(String s) {

super(s);

}

MójWyjątek() {}

}

(10)

Własne wyjątki c.d.

public static void main(String[] args) { try {głęboko();} catch (Exception e) { System.out.println(e.getMessage());

System.out.println(e.getCause().getMessage());

} }

static void głęboko() throws Exception { try {głębiej();} catch (MójWyjątek w) {

System.out.println("obsługuję MójWyjątek");

throw new Exception("W kooprocesorze wykryto wodę",w);

} }

static void głębiej() throws MójWyjątek {

throw new MójWyjątek("Włóż dwa hamburgery napędu cd.");

(11)

RuntimeException

class Wyjątki {

public static void main(String[] args) { try {

if (true) throw new RuntimeException();

Wyjątki w = null;

w.głęboko();

} catch (RuntimeException e) {

System.out.println(e.getMessage());

}

Wyjątki w = null;

w.głęboko(); //tego wyjątku main nie deklaruje }

(12)

Obsługa błędu uzależniona od rodzaju wyjątku

try { //...

} catch (NadklasaObsługiwanychWyjątków wyj) { if (wyj instance Klasa1) {

//...

} else if (wyj instance Klasa2) { //...

} else if (wyj instance Klasa3) { //...

} ...

}

(13)

Kilka catch i klauzula finally

Dla jednego bloku try można podać kilka części catch i w różny sposób obsługiwać wyjątki z różnych klas:

try {...}

catch (Klasa1 wyj) {...}

catch (Klasa2 wyj) {...}

catch (Klasa3 wyj) {...}

...

Dopasowywana jest pierwsza klauzula catch, do której pasuje wyjątek.

Pozostałe klauzule są ignorowane.

Kompilator nie pozwoli, by któraś klauzula przesłoniła występujące po niej (np. by Klasa2 dziedziczyła po Klasa1).

(14)

Finally

import java.util.*;

class Wyjątki {

Random r = new Random(new Date().getTime());

void losowyWyjątek() throws MójWyjątek {

if (r.nextBoolean()) throw new MójWyjątek();

}

public static void main(String[] args) { try {

new Wyjątki().losowyWyjątek();

} catch (MójWyjątek w) {

System.out.println("złapany");

} finally {

System.out.println("finally");

}

(15)

Uwaga na wyjątki w finally

Finally jest potrzebne, bo daje możliwość zwolnienia nadaremnie przydzielonych zasobów niesie jednak ze sobą niebezpieczeństwo zgubienia już zgłoszonego wyjątku:

try {

//przydzielamy zasoby throw new MójWyjątek1();

} finally {

//zwalniamy zasoby

throw new MójWyjątek2();

}

(16)

Stos wywołań

Zgłaszany wyjątek zbiera informacje o rozwijanym stosie wywołań.

class Wyjątki {

public static void main(String[] args) throws Exception { głęboko();

}

static void głęboko() throws Exception { głębiej();

}

static void głębiej() throws MójWyjątek { throw new MójWyjątek();

} }

Exception in thread "main" niezbędnik6.Wyjątki at niezbędnik6.Wyjątki.głębiej(Wyjątki.java:114)

(17)

Stos wywołań

Zbierany ślad stosu wywołań jest domyślnie wypisywany na standardowe wyjście błędu, jeżeli nieobsłużony wyjątek wydostanie się poza main().

Za wypisywanie odpowiada odziedziczona z klasy Throwable metoda

printStackTrace(). Dostępne są również jej dwie przeciążone wersje wypisujące informacje na zadany strumień.

Jeżeli zdecydujemy się ponownie zgłosić właśnie obsługiwany wyjątek, ślad stosu będzie dalej uaktualniany i będzie nadal prowadził do jego rzeczywistego miejsca powstania.

Jeżeli chcemy, aby ślad stosu prowadził do miejsca ponownego zgłoszenia wyjątku należy użyć metody fillInStackTrace().

Ta metoda jest również wywoływana w momencie tworzenia wyjątku.

(18)

Ponowne zgłaszanie obsługiwanego wyjątku

static void głęboko() throws MójWyjątek { try {

głębiej();

} catch (MójWyjątek w) { w.fillInStackTrace();

throw w;

//throw (MójWyjątek) w.fillInStackTrace();

//ta metoda jest zazwyczaj dziedziczona, aż z Throwable //czy przesłaniając można zmienić jej typ zwrotny

} }

Exception in thread "main" niezbędnik6.MójWyjątek at niezbędnik6.Wyjątki.głęboko(Wyjątki.java:120)

(19)

Wyjątki a tworzenie egzemplarza

Konstruktor musi definiować wszystkie wyjątki mogące powstać podczas tworzenia egzemplarza obiektu.

W trakcie inicjalizacji statycznej nie można zgłaszać wyjątków.

(20)

Przykład

class MojaKlasa {

static void możeZgłosićWyjątek() throws WyjA {}

{ możeZgłosićWyjątek(); }

//static { możeZgłosićWyjątek(); }

static int statyczna() throws WyjB {return 1;}

int normalna() throws WyjC {return 2;}

int i = normalna();

int j = statyczna();

//static k = statyczna();

MojaKlasa() throws WyjA, WyjB, WyjC, WyjD { throw new WyjD();

(21)

Zgłaszanie wyjątków w podklasach

Obiekty podklasy mogą być używane w zamian obiektów nadklasy.

Rozszerzane metody nie mogą zgłaszać wyjątków, których dotychczas nie trzeba było obsługiwać.

Rozszerzane metody mogą ograniczyć zgłaszane wyjątki – niektórych nie zgłaszać, niektóre uogólnić.

Konstruktory podklasy nie mają możliwości obsłużyć wyjątków powstających podczas tworzenia obiektu nadklasy.

(22)

Przykład

class BrakPaliwa extends Exception {}

class BrakBenzyny extends BrakPaliwa {}

class BrakGazu extends BrakPaliwa {}

class Samochód {

void jedź() throws BrakPaliwa {}

}

class SamochódNaBenzynę extends Samochód { void jedź() throws BrakBenzyny {}

}

class SamochódONapędzieHybrydowym extends Samochód {

(23)

Przykład c.d.

class BłądContinuum extends Exception {}

class ŁamiePrawaFizyki extends Exception {}

interface WehikułCzasu {

void jedź() throws ŁamiePrawaFizyki;

void przenieśSięWCzasie() throws BłądContinuum;

}

class PerpetuumMobile extends Samochód implements WehikułCzasu {

public void jedź() {} //throws ŁamiePrawaFizyki

(24)

Przykład c.d. c.d.

class BrakGotówki extends Exception {}

class NiedostępnyWSprzedaży extends Exception {}

class Samochód {

Samochód() throws BrakGotówki {}

void jedź() throws BrakPaliwa {}

}

class SamochódONapędzieHybrydowym extends Samochód { SamochódONapędzieHybrydowym() throws BrakGotówki,

NiedostępnyWSprzedaży {}

void jedź() throws BrakBenzyny, BrakGazu {}

}

(25)

Założenia w naszym kodzie

Często w pisanym przez siebie kodzie robimy założenia i opisujemy je w komentarzach.

int x = 0;

int y = 0;

while (true) { x++;

if (y > 5) break;

}System.out.println("Ta maszyna wirtualna jest do bani!");

Wykonywanie pewnych testów może być bardzo przydatne podczas pracy nad programem, ale gdy już się upewnimy, że wszystko działa jak planowaliśmy wypadałoby je wykomentować zamiast wdrażać taki kod u

(26)

Asercje

Od wersji 1.4 Java posiada wsparcie do sprawdzania takich założeń.

class Pracownik { float pensja;

private void dajPodwyżkę(float oIle) {

assert (oIle > 0); //może zgłosić wyjątek AssertionError ...

}

private void obniżPensję(float oIle, String dlaczego) {

assert (oIle > 0) : "Pensję można obniżać tylko o dodatnią wartość";

...

} }

Pracownik p = new Pracownik();

(27)

AssertionError

AssertionError jest podklasą Error, czyli można go nie obsługiwać.

Czy ten kod robi to samo?

assert (warunek);

if (warunek) throw new AssertionError();

(28)

Włączanie/wyłączanie asercji

Domyślnie asercje nie są włączone (są ignorowane).

java -enableassertions lub java -ea

java -disableassertions lub java -da

Można podać dodatkowe parametry

bez parametrów - dotyczy wszystkich klas

z nazwą pakietu kończącą się „...” - dotyczy całego pakietu i jego podpakietów (same „...” wskazują pakiet domyślny)

z nazwą klasy – dotyczy klasy

Wersja bezparametrowa nie dotyczy klas systemowych

(-enablesystemassertions -esa –disablesystemassertions -dsa)

Można podać kilka przełączników naraz są one przetwarzane od lewej do prawej.

Zaczynamy od wyłączonych asercji dla wszystkich klas i wykonujemy

(29)

Jak nie używać asercji!

Po assert nie należy podawać wyrażenia, które ma efekty uboczne.

taki program zachowywałby się inaczej w zależności od tego czy asercje są włączone czy wyłączone

Nie należy obsługiwać wyjątku AssertionError.

Nie używać do kontroli argumentów w publicznych metodach.

metoda powinna działać nawet jak użytkownik źle ją wywołał

działanie metody nie może zależeć od włączanie assercji

Nie używać asercji do kontroli argumentów przekazywanych z linii komend.

(30)

Kiedy można używać asercji

Do kontroli argumentów w metodach prywatnych.

tylko nasz kod je wykonuje, więc możemy coś zakładać

Do kontroli założeń, które na pewno powinny być prawdziwe (również w metodach publicznych).

Cytaty

Powiązane dokumenty

* Use the Blasius theorem to prove the Joukowski theorem which states that the result of the previous problem holds for any body shape.. Acheson, Elementary Fluid Dynamics,

• Jeśli kod wewnątrz metody moŜe spowodować wyjątek, a nie dostarczymy procedury jego obsługi zostaniemy. „zdyscyplinowani”

class mojAdapter extends MouseAdapter { public void mouseClicked(MouseEvent e) { //implementacja obsługi zdarzenia. }

” Na twarzy znowu pojawił się uśmiech: najwyżej podpicuje się trochę brykę i

Jego użycie wiąże się z koniecznością ponoszenia wydatków eksploatacyjnych (przykładowo wymianę opon, oleju), na ubezpieczenie pojazdu. Podstawowym kosztem, który wiąże się

Przedstawione informacje o produktach i usługach, w tym w szczególności ceny, nie stanowią oferty w rozumieniu przepisów kodeksu cywilnego.. W celu uzyskania informacji o

Ty, Wiesiu, zapamiętaj to sobie, ty się dobrze przyglądaj, co ja robię, ty się ucz myśleć, tu jest samochód a nie uniwersytet.. Taki ciężar - powiada

Since in his ten years long life he had seen almost everything, his satisfaction from life every day was dependent on consumption of usually not eaten products: icecreams i