You are on page 1of 23

Java.

Efektywne
programowanie. Wydanie II
Autor: Joshua Bloch
Tumaczenie: Pawe Gonera
ISBN: 978-83-246-2084-5
Tytu oryginau: Effective Java (2nd Edition)
Format: 158x235 , stron: 352
Poznaj specyfik jzyka Java i zosta mistrzem programowania
Jak korzysta z bibliotek jzyka Java?
Jak pisa funkcjonalny i klarowny kod?
Jak stworzy profesjonalny i efektowny program?

Jzyk Java jest jzykiem obiektowym z dziedziczeniem jednobazowym. Wewntrz


kadej metody korzysta on ze zorientowanego na instrukcje stylu kodowania.
Aby dobrze pozna jakikolwiek jzyk, naley nauczy si posugiwa jego reguami,
zasadami i skadni podobnie jest z jzykiem programowania. Jeli chcesz zyska
moliwo efektywnego programowania w jzyku Java, powiniene pozna struktury
danych, operacje i udogodnienia, oferowane przez biblioteki standardowe, a take
czsto stosowane i efektywne sposoby tworzenia kodu. Ca potrzebn Ci wiedz
znajdziesz wanie w tym podrczniku.
W ksice Java. Efektywne programowanie w sposb zrozumiay i klarowny
przedstawiono zasady opisujce mechanizmy uywane w najlepszych technikach
programowania. Ten podrcznik podpowie Ci, jak najbardziej racjonalnie korzysta
z jzyka Java oraz jego podstawowych bibliotek. Dowiesz si, jak stosowa wyjtki
przechwytywalne i wyjtki czasu wykonania, poznasz take zalety stosowania
statycznych klas skadowych. Opanujesz metody sprawdzania poprawnoci
parametrw i projektowania sygnatur oraz wszelkie instrukcje, ktre pozwol Ci
na wydajne i profesjonalne programowanie.
Tworzenie i usuwanie obiektw
Klasy i interfejsy
Zapewnianie niezmiennoci obiektu
Projektowanie i dokumentowanie klas przeznaczonych do dziedziczenia
Zalety stosowania statycznych klas skadowych
Typy oglne
Typy wyliczeniowe i adnotacje
Metody
Programowanie
Wykorzystanie oglnie przyjtych konwencji nazewnictwa
Wyjtki
Wspbieno i serializacja
Dokumentowanie bezpieczestwa dla wtkw

Nie wystarczy samo poznanie jzyka Java.


Trzeba wiedzie, jak z niego efektywnie korzysta!

Spis treci
Sowo wstpne .................................................................................. 11
Przedmowa ...................................................................................... 13
Podzikowania .................................................................................. 17
Rozdzia 1. Wprowadzenie ............................................................... 21
Rozdzia 2. Tworzenie i usuwanie obiektw ...................................... 25
Temat 1. Tworzenie statycznych metod factory
zamiast konstruktorw ............................................................... 25
Temat 2. Zastosowanie budowniczego
do obsugi wielu parametrw konstruktora ........................ 31
Temat 3. Wymuszanie waciwoci singleton
za pomoc prywatnego konstruktora .............................. 37
Temat 4. Wykorzystanie konstruktora prywatnego
w celu uniemoliwienia utworzenia obiektu ....................... 39
Temat 5. Unikanie powielania obiektw .......................................... 40
Temat 6. Usuwanie niepotrzebnych referencji do obiektw ........... 44
Temat 7. Unikanie finalizatorw ...................................................... 47

Rozdzia 3. Metody wsplne dla wszystkich obiektw ....................... 53


Temat 8. Zachowanie zaoe w trakcie przedefiniowywania
metody equals ..................................................................... 53
Temat 9. Przedefiniowywanie metody hashCode wraz z equals ..... 65
Temat 10. Przedefiniowywanie metody toString ............................... 70
Temat 11. Rozsdne przedefiniowywanie metody clone ................... 73
Temat 12. Implementacja interfejsu Comparable ............................. 81

SPIS TRECI

Rozdzia 4. Klasy i interfejsy ............................................................ 87


Temat 13. Ograniczanie dostpnoci klas i ich skadnikw .............. 87
Temat 14. Stosowanie metod akcesorw zamiast pl publicznych
w klasach publicznych ........................................................ 91
Temat 15. Zapewnianie niezmiennoci obiektu ................................. 93
Temat 16. Zastpowanie dziedziczenia kompozycj ........................ 101
Temat 17. Projektowanie i dokumentowanie klas przeznaczonych
do dziedziczenia ............................................................... 107
Temat 18. Stosowanie interfejsw zamiast klas abstrakcyjnych ..... 112
Temat 19. Wykorzystanie interfejsw jedynie
do definiowania typw ...........................................................117
Temat 20. Zastpowanie oznaczania klas hierarchi ....................... 119
Temat 21. Zastosowanie obiektw funkcyjnych
do reprezentowania strategii ........................................... 122
Temat 22. Zalety stosowania statycznych klas skadowych ............. 125

Rozdzia 5. Typy oglne ................................................................ 129


Temat 23.
Temat 24.
Temat 25.
Temat 26.
Temat 27.
Temat 28.

Nie korzystaj z typw surowych w nowym kodzie ......... 129


Eliminowanie ostrzee o braku kontroli ....................... 135
Korzystanie z list zamiast tablic ......................................... 137
Stosowanie typw oglnych ............................................. 142
Stosowanie metod oglnych ............................................ 146
Zastosowanie zwizanych szablonw
do zwikszania elastycznoci API .................................... 151
Temat 29. Wykorzystanie heterogenicznych kontenerw
bezpiecznych dla typw .................................................... 158

Rozdzia 6. Typy wyliczeniowe i adnotacje ..................................... 165


Temat 30.
Temat 31.
Temat 32.
Temat 33.
Temat 34.

Uycie typw wyliczeniowych zamiast staych int .......... 165


Uycie pl instancyjnych zamiast kolejnoci .................. 176
Uycie EnumSet zamiast pl bitowych ........................... 177
Uycie EnumMap zamiast indeksowania kolejnoci .... 178
Emulowanie rozszerzalnych typw wyliczeniowych
za pomoc interfejsw ...................................................... 182
Temat 35. Korzystanie z adnotacji zamiast wzorcw nazw ............. 186
Temat 36. Spjne uycie adnotacji Override .................................... 192
Temat 37. Uycie interfejsw znacznikowych
do definiowania typw ...........................................................195

Rozdzia 7. Metody ....................................................................... 197


Temat 38.
Temat 39.
Temat 40.
Temat 41.

Sprawdzanie poprawnoci parametrw .......................... 197


Defensywne kopiowanie .................................................. 200
Projektowanie sygnatur metod ........................................ 204
Rozsdne korzystanie z przeciania .............................. 206

SPIS TRECI

Temat 42. Rozsdne korzystanie z metod varargs ........................... 212


Temat 43. Zwracanie pustych tablic lub kolekcji
zamiast wartoci null ..............................................................215
Temat 44. Tworzenie komentarzy dokumentujcych
dla wszystkich udostpnianych elementw API ............. 217

Rozdzia 8. Programowanie ........................................................... 225


Temat 45. Ograniczanie zasigu zmiennych lokalnych ................... 225
Temat 46. Stosowanie ptli for-each
zamiast tradycyjnych ptli for ......................................... 228
Temat 47. Poznanie i wykorzystywanie bibliotek ............................ 231
Temat 48. Unikanie typw float i double,
gdy potrzebne s dokadne wyniki .................................. 234
Temat 49. Stosowanie typw prostych
zamiast opakowanych typw prostych ............................ 236
Temat 50. Unikanie typu String,
gdy istniej bardziej odpowiednie typy ........................... 239
Temat 51. Problemy z wydajnoci
przy czeniu cigw znakw ........................................... 242
Temat 52. Odwoywanie si do obiektw poprzez interfejsy .......... 243
Temat 53. Stosowanie interfejsw zamiast refleksyjnoci ............... 245
Temat 54. Rozwane wykorzystywanie metod natywnych .............. 248
Temat 55. Unikanie optymalizacji .................................................... 249
Temat 56. Wykorzystanie oglnie przyjtych
konwencji nazewnictwa .................................................... 252

Rozdzia 9. Wyjtki ........................................................................ 257


Temat 57. Wykorzystanie wyjtkw
w sytuacjach nadzwyczajnych .......................................... 257
Temat 58. Stosowanie wyjtkw przechwytywanych
i wyjtkw czasu wykonania ............................................ 260
Temat 59. Unikanie niepotrzebnych
wyjtkw przechwytywanych ........................................... 262
Temat 60. Wykorzystanie wyjtkw standardowych ....................... 264
Temat 61. Zgaszanie wyjtkw waciwych dla abstrakcji .............. 266
Temat 62. Dokumentowanie wyjtkw zgaszanych
przez metod ..................................................................... 268
Temat 63. Udostpnianie danych o bdzie ....................................... 270
Temat 64. Zachowanie atomowoci w przypadku bdu ................. 272
Temat 65. Nie ignoruj wyjtkw ....................................................... 274

10

SPIS TRECI

Rozdzia 10. Wspbieno ............................................................ 275


Temat 66. Synchronizacja dostpu
do wsplnych modyfikowalnych danych ........................ 275
Temat 67. Unikanie nadmiarowej synchronizacji ........................... 280
Temat 68. Stosowanie wykonawcw i zada zamiast wtkw ......... 286
Temat 69. Stosowanie narzdzi wspbienoci
zamiast wait i notify ......................................................... 288
Temat 70. Dokumentowanie bezpieczestwa dla wtkw ............... 293
Temat 71. Rozsdne korzystanie z pnej inicjalizacji .................... 296
Temat 72. Nie polegaj na harmonogramie wtkw ......................... 300
Temat 73. Unikanie grup wtkw ..................................................... 302

Rozdzia 11. Serializacja .................................................................. 305


Temat 74.
Temat 75.
Temat 76.
Temat 77.

Rozsdne implementowanie interfejsu Serializable ....... 305


Wykorzystanie wasnej postaci serializowanej ............... 310
Defensywne tworzenie metody readObject .................... 317
Stosowanie typw wyliczeniowych
zamiast readResolve do kontroli obiektw ..................... 323
Temat 78. Uycie porednika serializacji
zamiast serializowanych obiektw .................................. 327

Dodatek A

Tematy odpowiadajce pierwszemu wydaniu ............... 331

Dodatek B

Zasoby ........................................................................ 335

Skorowidz ....................................................................................... 339

9
Wyjtki
K

orzystajc z najlepszych cech wyjtkw, mona dziki nim poprawi czytelno programu, jego solidno i atwo konserwacji. Uyte niewaciwie bd
miay odwrotny efekt. W rozdziale tym zamieszczamy wskazwki, pomagajce
efektywnie wykorzystywa wyjtki.

Temat 57. Wykorzystanie wyjtkw


w sytuacjach nadzwyczajnych
By moe kiedy spotkasz si z fragmentem kodu, ktry bdzie wyglda nastpujco:
// Naduycie wyjtkw. Nigdy tak nie rb!
try {
int i = 0 ;
while (true)
range[i++].climb();
} catch (ArrayIndexOutOfBoundsException e) {
}

Co robi ten fragment? Dziaanie to nie jest oczywiste na pierwszy rzut oka i jest
to wystarczajcy powd, aby odradzi stosowanie takiego stylu programowania
(temat 55.). Jest to bardzo zy sposb na przegldanie tablicy. Ptla nieskoczona
jest przerywana przez zgoszenie, przechwycenie i zignorowanie wyjtku ArrayIndex
OutOfBoundsException, gdy program bdzie prbowa odwoa si do pierwszego
elementu spoza zakresu tablicy. Jest to kolawy odpowiednik standardowej metody
przegldania tablicy, natychmiast rozpoznawany przez kadego programist:
for (Mountain m : range)
m.climb();

258

ROZDZIA 9. WYJTKI

Skd wzi si wic pomys zastpienia wyprbowanej metody przegldania tablicy


metod wykorzystujc wyjtki? Jest to nieudolna prba poprawy wydajnoci,
oparta o nieprawidowe zaoenie, e maszyna wirtualna kontroluje dostp do elementw spoza zakresu tablicy przy wszystkich odwoaniach, wic zwyky test zakoczenia ptli ukryty przez kompilator, ale nadal wystpujcych w ptli for-each
jest nadmiarowy i powinien by usunity. Zaoenie to powoduje wystpienie
trzech niekorzystnych zjawisk:
Poniewa wyjtki s zaprojektowane do obsugi sytuacji wyjtkowych,
implementacje JVM sabo optymalizuj kod obsugi wyjtku.
Tworzenie, zgaszanie i przechwytywanie wyjtkw to operacje
stosunkowo kosztowne.
Umieszczenie kodu w bloku try-catch wyklucza niektre
optymalizacje, ktre nowoczesne maszyny wirtualne mog zastosowa.
Standardowa metoda przegldania elementw tablicy niekoniecznie
musi wykonywa nadmiarowe sprawdzenia nowoczesne implementacje
JVM potrafi to zoptymalizowa.
W rzeczywistoci zaprezentowana metoda przegldania tablicy z wykorzystaniem
wyjtku na niemal wszystkich implementacjach JVM jest o wiele wolniejsza, ni
metoda najczciej wykorzystywana. Na moim komputerze kod, przegldajcy
stuelementow tablic metod wykorzystujc wyjtki, wykonywa si dwa razy
duej ni fragment korzystajcy ze standardowej metody.
Metoda wykorzystujca wyjtki nie tylko zaciemnia kod i spowalnia program,
ale rwnie nie gwarantuje poprawnej pracy. W przypadku wystpienia cakowicie
innego bdu metoda ta moe zawie bez adnego ostrzeenia, maskujc bd, co
ogromnie skomplikuje proces uruchamiania programu. Zamy, e obliczenia wykonywane w ptli zawieraj bd przekroczenia zakresu cakowicie innej tablicy.
Jeeli zostaaby wykorzystana standardowa metoda przegldania tablicy, bd ten
spowodowaby zgoszenie wyjtku, co z kolei natychmiast zakoczyoby wtek wygenerowaniem odpowiedniego komunikatu bdu. Jeeli jednak zostanie uyta metoda z przechwytywaniem wyjtku, to wyjtek zwizany z bdem zostanie mylnie
zinterpretowany jako normalne zakoczenie ptli.
Przykad ten ilustruje, e wyjtki, tak jak ich nazwa wskazuje, powinny by wykorzystywane do obsugi sytuacji wyjtkowych nie powinny by nigdy uywane
do sterowania normalnym przebiegiem kodu programu. Mwic oglniej, powiniene stosowa standardowe, rozpoznawalne konstrukcje programowe zamiast
sprytnych, stosowanych jedynie w celu osignicia lepszej wydajnoci. Nawet, gdy
zysk wydajnoci jest znaczny, moe szybko zosta zniwelowany w kolejnych wersjach
szybko rozwijajcych si implementacji JVM. Pozostan jednak kopoty przy usuwaniu subtelnych bdw, wystpujce w przypadku zastosowania sprytnych metod.

TEMAT 57. WYKORZYSTANIE WYJTKW W SYTUACJACH NADZWYCZAJNYCH

Zasada ta powinna by rwnie stosowana przy projektowaniu API. Dobrze zaprojektowane API nie powinno wymusza na klientach wykorzystania wyjtkw
do sterowania zwykym przebiegiem programu. Klasa z metodami zalenymi od
stanu, ktre s wywoywane jedynie w okrelonych, nieprzewidywalnych warunkach,
powinny posiada zwykle oddzielne metody testujce stan, czyli sprawdzajce,
czy naley wywoa pierwsz metod. Na przykad, klasa Iterator posiada zalen
od stanu metod next, ktra zwraca nastpny element wyliczenia oraz skojarzon
z ni metod hasNext, testujc stan. Pozwala to na skorzystanie ze standardowego
idiomu, sucego do przegldania kolekcji w standardowych ptlach for (jak
rwnie w ptlach for-each, w ktrych metoda hasNext jest wykorzystywana
wewntrznie):
for (Iterator<Foo> i = collection.iterator(); i.hasNext(); ) {
Foo foo = i.next();
// ...
}

Jeeli klasa Iterator nie posiadaaby metody hasNext, klient zostaby zmuszony do
skorzystania z poniszej metody:
// Nie korzystaj z tej okropnej metody przegldania kolekcji!
try {
Iterator<Foo> i = collection.iterator();
while (true) {
Foo foo = i.next();
// ...
}
} catch (NoSuchElementException e) {
}

Przedstawiony kod wyglda podobnie do przykadu rozpoczynajcego ten temat.


Oprcz tego, e przykad jest rozwleky, dziaa duo wolniej ni standardowy oraz
moe ukrywa bdy, pochodzce z innych czci systemu.
Innym sposobem zapewnienia osobnej metody testujcej stan jest zwracanie przez
metod zalen od stanu specjalnej wartoci, jak na przykad null, jeeli obiekt jest
w nieprawidowym stanie. Technika ta nie nadaje si dla klasy Iterator, poniewa
warto null jest prawidow wartoci zwracan przez metod next.
Warto wymieni kilka sugestii, dotyczcych wyboru pomidzy metod testujc
stan i zwracaniem wartoci specjalnej. Jeeli do obiektu mona si odwoywa
rwnolegle bez zewntrznej synchronizacji lub jego stan zmienia si pod wpywem
zewntrznych czynnikw, wykorzystanie specjalnej wartoci moe by kluczowe
dla prawidowego dziaania, poniewa obiekt moe zmieni swj stan pomidzy
wywoaniem metody testujcej stan i odpowiadajcej jej metody zalenej od stanu.
Jeeli wana jest wydajno, naley skorzysta ze zwracania wartoci specjalnej
(jeeli wykonanie osobnej metody testujcej stan wie si z wykonaniem tych

259

260

ROZDZIA 9. WYJTKI

samych operacji, co w odpowiadajcej jej metodzie zalenej od stanu). Jeeli nie


zachodz te przypadki, zalecane jest tworzenie metody testujcej stan. Pozwala ona
osign lepsz czytelno kodu, a w przypadku jej niewaciwego stosowania atwiej
mona odszuka i poprawi bd.
Podsumujmy. Wyjtki s zaprojektowane do wykorzystania w sytuacjach wyjtkowych. Nie naley ich uywa do zwykej kontroli przepywu sterowania i nie naley
pisa API wymuszajcych takie dziaanie.

Temat 58.
Stosowanie wyjtkw przechwytywanych
i wyjtkw czasu wykonania
Jzyk Java pozwala na stosowanie trzech rodzajw wyjtkw: wyjtkw przechwytywanych, wyjtkw czasu wykonania i bdw. Czsto pojawiaj si wtpliwoci,
dotyczce zastosowania odpowiedniego rodzaju wyjtku. Wybr nie zawsze jest
jednoznaczny, ale istniej oglne zasady, uatwiajce podjcie decyzji.
Podstawow zasad jest okrelenie, czy wyjtek jest przechwytywany czy nieprzechwytywany. Wyjtki przechwytywane naley stosowa w przypadkach, w ktrych
spodziewamy si, e wywoujcy moe przywrci normalny stan. Zgaszajc
wyjtek przechwytywany, zmuszasz wywoujcego do obsuenia tego wyjtku
w klauzuli catch lub do jego propagacji na wyszy poziom. Kady przechwytywany
wyjtek deklarowany przez metod jest wskazwk dla uytkownika API, e w trakcie
wykonywania metody mona spodziewa si, e taki bdzie wynik wykonania danej
metody.
Przekazujc przechwytywany wyjtek uytkownikowi API, projektant nakazuje
wykonanie operacji przywracajcych normalny stan systemu. Uytkownik moe
zgodzi si na ten nakaz, przechwytujc wyjtek, lub zignorowa go, cho nie jest to
najlepszym pomysem (temat 65.).
Istniej dwa rodzaje wyjtkw nieprzechwytywanych wyjtki czasu wykonania
i bdy. Ich dziaanie jest identyczne oba rodzaje nie mog i zwykle nie powinny
by przechwytywane. Jeeli program zgasza wyjtek nieprzechwytywany lub bd,
zwykle przywrcenie waciwego stanu systemu jest niemoliwe i kontynuowanie
dziaania programu moe spowodowa wicej zego ni dobrego. Jeeli program
nie przechwyci takiego wyjtku, spowoduje to zakoczenie biecego wtku z odpowiednim komunikatem bdu.

TEMAT 58. STOSOWANIE WYJTKW PRZECHWYTYWANYCH I WYJTKW CZASU WYKONANIA

Do wskazywania bdw programistycznych naley uywa wyjtkw czasu wykonania. Wikszo wyjtkw czasu wykonania wskazuje na naruszenia zaoe.
Naruszenie zaoenia wystpuje, gdy klient API nie moe dotrzyma zaoe, wymienionych w specyfikacji API. Dla przykadu, zaoenia dla dostpu do tablicy
okrelaj, e indeks tablicy musi by liczb od zera do wielkoci tablicy minus jeden.
Wyjtek ArrayIndexOutOfBoundsException wskazuje na niedotrzymanie tego
zaoenia.
Mimo e specyfikacja jzyka Java nie wymusza tego, istnieje bardzo silna konwencja,
mwica, e bdy s zarezerwowane przez maszyn wirtualn do wskazywania
braku zasobw, naruszenia niezmiennikw lub innych bdw, powodujcych
niemoliwo kontynuacji wykonania programu. Z powodu niemal cakowitej
akceptacji tej konwencji najlepiej nie tworzy nowych podklas dla klasy Error.
Wszystkie nieprzechwytywane wyjtki definiowane przez Ciebie powinny dziedziczy po RuntimeException (bezporednio bd porednio).
Moliwe jest zdefiniowanie wyjtku, ktry nie dziedziczy po klasie Exception, Run
TimeException lub Error. Specyfikacja jzyka nie zajmuje si bezporednio takimi
wyjtkami, ale niejawnie okrela, e maj one identyczne dziaanie, jak zwyke,
przechwytywane wyjtki (ktre dziedzicz po Exception, a nie RunTimeException).
Kiedy wic powinnimy z nich skorzysta? Prawdopodobnie nigdy. Nie maj one
adnych zalet w porwnaniu ze zwykymi przechwytywanymi wyjtkami i prawdopodobnie bd jedynie myliy uytkownikw Twojego API.
Podsumujmy. Wyjtkw przechwytywanych naley uywa do obsugi przypadkw,
w ktrych mona naprawi bd, a wyjtkw nieprzechwytywanych naley uywa
do wykonania obsugi bdw programowych. Oczywicie sytuacja nie zawsze musi
by czarno-biaa. Na przykad wyczerpanie si zasobw moe by spowodowane
przez bd programu, taki jak niepotrzebne przydzielenie zbyt duej tablicy, albo
przez zwyky brak zasobw. Jeeli brak zasobw jest spowodowany przez tymczasowy niedobr lub przez tymczasowo zwikszone potrzeby, operacja ma szans
powodzenia. W gestii projektanta API ley decyzja, czy dana sytuacja braku zasobw
pozwala na przywrcenie normalnego stanu. Jeeli uwaasz, e sytuacj da si naprawi, naley skorzysta z wyjtku przechwytywanego jeeli nie, wyjtku czasu
wykonania. Jeeli nie jeste pewien, czy moliwe jest przywrcenie normalnego
stanu, z powodw omwionych w temacie 59. lepiej skorzysta z wyjtku nieprzechwytywanego.
Projektanci API czsto zapominaj, e wyjtki s w peni funkcjonalnymi obiektami,
w ktrych mog by definiowane normalne metody. Podstawowym zastosowaniem
takich metod jest dostarczenie kodu przechwytujcego wyjtek i dodatkowych
informacji na temat zdarzenia, ktre spowodowao zgoszenie wyjtku. W przypadku braku takich metod programici musz analizowa cig zwracany przez
wyjtek w celu wyuskania dodatkowych danych. Jest to bardzo za praktyka

261

262

ROZDZIA 9. WYJTKI

(temat 10.). Klasy rzadko okrelaj szczegy ich reprezentacji jako cig znakw.
Reprezentacja ta moe rni si w rnych implementacjach i wersjach. Dlatego
kod analizujcy cig reprezentujcy wyjtek najprawdopodobniej bdzie nieprzenony i podatny na bdy.
Poniewa wyjtki przechwytywane najczciej wskazuj sytuacje naprawialne, szczeglnie wane jest definiowanie w klasach wyjtkw metod, ktre dostarczaj danych pomagajcych przywrci prawidowy stan. Na przykad, przechwytywany
wyjtek moe by zgaszany w sytuacji, gdy wykonana zostaa prba zadzwonienia z automatu telefonicznego bez wrzucenia odpowiedniej kwoty. Wyjtek ten
powinien zawiera metod zwracajc brakujc kwot, dziki czemu niedobr
moe by wywietlony uytkownikowi telefonu.

Temat 59. Unikanie niepotrzebnych


wyjtkw przechwytywanych
Przechwytywane wyjtki s niezwykle uytecznym mechanizmem jzyka Java.
W przeciwiestwie do zwracanych wartoci wymuszaj one na programicie zajcie
si sytuacj wyjtkow, co niezmiernie zwiksza solidno aplikacji. Jednak naduywanie wyjtkw moe spowodowa, e API bdzie mao wygodne. Jeeli metoda
zgasza jeden lub wicej wyjtkw, kod wywoujcy j musi obsugiwa te wyjtki
w jednym lub wikszej liczby blokw catch albo musi zadeklarowa, e bdzie
zgasza te wyjtki, co umoliwi ich propagacj. Obie czynnoci powoduj zwikszone obcienie programistw.
Obcienie to jest uzasadnione, gdy nie mona unikn sytuacji wyjtkowych przy
waciwym stosowaniu API oraz gdy programista wykorzystujcy to API ma moliwo przedsiwzi sensowne akcje po wystpieniu takiego wyjtku. W sytuacji,
gdy nie s spenione oba te warunki, bardziej waciwe jest zastosowanie wyjtku
nieprzechwytywanego. Musimy odpowiedzie sobie na pytanie, w jaki sposb programista ma obsuy wyjtek. Czy jest to najlepsze, co moe by wykonane?
} catch(TheCheckedException e) {
throw new AssertionError(); // Nie powinien si zdarzy!
}

I drugi przypadek.
} catch (TheCheckedException e) {
e.printStackTrace();
System.exit(1);
}

// To ju koniec.

TEMAT 59. UNIKANIE NIEPOTRZEBNYCH WYJTKW PRZECHWYTYWANYCH

Jeeli programista nie moe lepiej obsuy wyjtku, lepszy bdzie wyjtek nieprzechwytywany. Przykadem wyjtku, ktry nie spenia tej zasady, jest CloneNot
SupportedException. Jest on zgaszany przez metod Object.clone, ktra powinna
by wywoywana jedynie przez obiekt implementujcy interfejs Cloneable (temat 11.).
W praktyce blok catch nieomal zawsze ma charakter niepowodzenia asercji.
Przechwycenie tego wyjtku nie daje programicie adnych dodatkowych moliwoci, wymaga jedynie dodatkowej pracy i komplikuje program.
Dodatkowo, obcienie programisty przez obsug wyjtku jest nieco wiksze, jeeli
metoda zgasza jeden wyjtek. Jeeli zgaszane jest wicej wyjtkw, metoda musi
by umieszczona w bloku try, a obsuga poszczeglnych wyjtkw jest umieszczona
w kolejnych blokach catch. Jeeli metoda zgasza tylko jeden wyjtek, jest on odpowiedzialny za umieszczenie wywoania tej metody w bloku try. W tej sytuacji opaca
si pomyle o sposobach uniknicia przechwytywania wyjtkw.
Jedn z technik zmiany wyjtku przechwytywanego na nieprzechwytywany jest
podzia metody na dwie czci pierwsza zwraca warto boolean, wskazujc
na to, czy powinien by zgoszony wyjtek. Stosujc t metod powodujemy, e
ponisza sekwencja wywoa:
// Wywoanie ze sprawdzaniem wyjtkw
try {
obj.action(args);
} catch (TheCheckedException e) {
// Obsuga sytuacji wyjtkowej
// ...
}

zmienia si na:
// Wywoanie z uyciem metody testujcej stan i wyjtku nieprzechwytywanego
if (obj.actionPermitted(args)) {
obj.action(args);
} Else {
// Obsuga sytuacji wyjtkowej
// ...
}

Transformacja taka nie jest zawsze prawidowa, ale tam, gdzie mona j zastosowa, moe ona uatwi uywanie API. Cho druga sekwencja wywoa nie jest
adniejsza od pierwszej, to jednak API jest bardziej elastyczne. W przypadkach,
gdy programista wie, e wywoanie na pewno si uda lub pozwala na zakoczenie
programu w przypadku niepowodzenia, pokazana transformacja pozwala na zastosowanie najprostszej sekwencji wywoa:
obj.action(args);

263

264

ROZDZIA 9. WYJTKI

Jeeli podejrzewasz, e najprostsza sekwencja wywoania bdzie powszechnie stosowana, to taka modyfikacja API jest waciwa. Po takiej modyfikacji API bdzie
nieomal identyczne, jak w przypadku zastosowania metody testujcej stan, przedstawionej w temacie 57. i naley tu przestrzega tych samych zasad. Jeeli do obiektu
wystpuj jednoczesne odwoania bez zewntrznej synchronizacji lub jego stan
zmienia si pod wpywem czynnikw zewntrznych, przedstawiona modyfikacja
jest nieprawidowa, poniewa stan obiektu moe zmieni si pomidzy wywoaniem
metody actionPermitted i action. Jeeli metoda actionPermitted bdzie musiaa
powtrzy operacje wykonywane w metodzie action, transformacja ta spowoduje
obnienie wydajnoci systemu.

Temat 60.
Wykorzystanie wyjtkw standardowych
Jednym z atrybutw najlepiej odrniajcych ekspertw w dziedzinie programowania od mniej zaawansowanych programistw jest dbao ekspertw o wysoki stopie ponownego wykorzystania kodu. Wyjtki nie s tutaj wyjtkiem od zasady
powtrnego wykorzystania kodu. Biblioteki platformy Java zawieraj podstawowy
zbir wyjtkw nieprzechwytywanych, ktry obejmuje wikszo sytuacji wyjtkowych spotykanych w API. Temat ten zawiera omwienie tych powszechnie wykorzystywanych wyjtkw.
Ponowne wykorzystanie istniejcych wyjtkw niesie ze sob wiele korzyci. Najwaniejsz z nich jest uatwienie nauki stosowania API, poniewa korzysta ono
z dobrze ugruntowanych konwencji, z ktrymi programista jest ju zaznajomiony.
Nie mniej wan korzyci jest czytelno programw korzystajcych z takiego API,
poniewa kod nie jest zabaaganiony nieznanymi wyjtkami. Na koniec, mniej klas
wyjtkw powoduje zmniejszenie iloci zuywanej pamici i skraca czas adowania klas.
Najczciej wykorzystywanym wyjtkiem jest IllegalArgumentException. Najczciej jest on zgaszany, gdy wywoujcy przekae argument o niewaciwej wartoci.
Na przykad, mona skorzysta z tego wyjtku, jeeli jako parametr reprezentujcy
liczb powtrze zostanie przekazana warto ujemna.
Innym czsto stosowanym wyjtkiem jest IllegalStateException. Jest to wyjtek
zgaszany, jeeli w aktualnym stanie obiektu wywoanie jest nielegalne. Na przykad,
jeeli wywoujcy bdzie prbowa skorzysta z obiektu przed jego prawidowym
zainicjowaniem.

TEMAT 60. WYKORZYSTANIE WYJTKW STANDARDOWYCH

Mona uzna, e wszystkie bdne wywoania metod sprowadzaj si do nieprawidowego argumentu i do niewaciwego stanu obiektu, ale do sygnalizowania niektrych rodzajw nieprawidowych stanw i argumentw wykorzystywane s najczciej specjalizowane wyjtki. Jeeli wartoci parametru bdzie null w miejscu,
gdzie wartoci null s niedozwolone, konwencja nakazuje zgosi wyjtek NullPointer
Exception a nie IllegalArgumentException. Podobnie, jeeli przekazana warto
parametru oznaczajcego indeks wykracza poza dozwolony zakres, naley zgosi
wyjtek IndexOutOfBoundsException, a nie IllegalArgumentException.
Innym wyjtkiem oglnego przeznaczenia, jaki powinnimy zna, jest Concurrent
ModificationException. Wyjtek ten powinien by zgaszany, jeeli obiekt zaprojektowany do wykorzystywania przez jeden wtek lub korzystajcy z zewntrznej
synchronizacji wykryje, e jest (lub by) rwnolegle modyfikowany.
Ostatnim wyjtkiem oglnego przeznaczenia, jaki tu omwimy, jest Unsupported
OperationException. Wyjtek ten jest zgaszany, gdy obiekt nie obsuguje wywoywanej operacji. Jest on dosy rzadko wykorzystywany, poniewa wikszo obiektw obsuguje wszystkie implementowane przez siebie metody. Wykorzystywany
jest w sytuacjach, gdy implementacja interfejsu nie posiada obsugi jakiej opcjonalnej operacji definiowanej przez interfejs. Na przykad, implementacja interfejsu List,
przeznaczona tylko do doczania elementw, zgosi ten wyjtek, gdy kto bdzie
prbowa usun element.
W tabeli 7.1 przedstawione jest zestawienie czsto wykorzystywanych wyjtkw.
Tabela 7.1. Czsto wykorzystywane wyjtki
Wyjtek

Zastosowanie

IllegalArgumentException

Nieprawidowa warto parametru

IllegalStateException

Nieprawidowy stan obiektu dla wywoywanej metody

NullPointerException

Zabronione stosowanie wartoci null w parametrze

IndexOutOfBoundsException

Warto indeksu poza zakresem

ConcurrentModificationException

Wykryta rwnolega modyfikacja stanu obiektu


w przypadku, gdy jest ona zabroniona

UnsupportedOperationException

Obiekt nie obsuguje metody

Cho s to najczciej wykorzystywane wyjtki w bibliotekach platformy Java, to


w przypadkach, gdy sytuacja upowania do uycia innego wyjtku, naley go zastosowa. Na przykad, w przypadku tworzenia obiektw matematycznych, takich jak
liczby zespolone lub macierze, bardziej waciwe bdzie zastosowanie wyjtkw
ArihtmeticException i NumberFormatException. Jeeli wyjtek spenia Twoje potrzeby wykorzystaj go, jednak jedynie wtedy, gdy warunki, w ktrych zgaszasz

265

266

ROZDZIA 9. WYJTKI

ten wyjtek, s zgodne z warunkami opisanymi w jego dokumentacji. Wykorzystanie


wyjtkw musi by uzasadnione semantyk, a nie jedynie nazw. W przypadkach,
gdy chcesz uzupeni wyjtek o dodatkowe informacje, moesz rwnie dziedziczy po istniejcych wyjtkach (temat 63.).
Musisz wiedzie, e wybr waciwego wyjtku do wykorzystania nie jest czasami
jednoznaczny, a Zastosowania z tabeli 7.1 nie zawsze wzajemnie si wykluczaj.
Pomyl na przykad o przypadku obiektu reprezentujcego tali kart. Zamy, e
mamy metod wydajc karty, ktra jako argument posiada liczb kart. Zamy,
e wywoujcy t metod przekaza warto parametru wiksz ni liczba kart
pozostaych w talii. Moe by to zinterpretowane jako IllegalArgumentException
(warto parametru jest zbyt dua) lub IllegalStateException (obiekt talia posiada
za mao kart do zrealizowania dania). W tym przypadku wydaje si, e waciwy
bdzie wyjtek IllegalArgumentException, ale nie ma tu adnej sztywnej reguy.

Temat 61. Zgaszanie


wyjtkw waciwych dla abstrakcji
Niewaciwe jest zgaszanie przez metod wyjtku, ktry nie ma widocznego zwizku
z zadaniem, jakie ta metoda wykonuje. Czsto si to zdarza, gdy metoda propaguje
wyjtek zgoszony przez mechanizmy niskiego poziomu. Oprcz wprowadzania
zamieszania powoduje to zamiecenie API wyszej warstwy szczegami implementacji warstwy niszej. Jeeli w przyszej wersji implementacja wyszej warstwy zostanie zmieniona, zgaszane wyjtki rwnie mog si zmieni, co moe spowodowa,
e programy klientw przestan dziaa.
Aby unikn tego problemu, wysze warstwy abstrakcji powinny przechwytywa
wyjtki z warstwy niszej i w ich miejsce zgasza wyjtki, ktre s odpowiednie
dla wyszej warstwy. Idiom ten, nazywany translacj wyjtkw, wyglda nastpujco:
// Translacja wyjtkw
try {
// Wykorzystanie operacji niszej warstwy
// ...
} catch (LowerLevelException e) {
throw new HigherLevelException(...);
}

Poniej przedstawiamy przykad translacji wyjtkw, zaczerpnity z klasy Abstract


SequentialList, ktra jest szkieletow implementacj (temat 18.) interfejsu List.
W przykadzie tym wykorzystanie translacji wyjtkw jest wymuszone przez specyfikacj metody get z interfejsu List<E>.

TEMAT 61. ZGASZANIE WYJTKW WACIWYCH DLA ABSTRAKCJI


/**
* Zwraca element z podanej pozycji w tej licie.
* @throws IndexOutOfBoundsException, jeeli indeks jest poza zakresem
*
(index < 0 || index >= size()).
*/
public E get(int index) {
ListIterator<E> i = listIterator(index);
try {
return i.next();
} catch(NoSuchElementException e) {
throw new IndexOutOfBoundsException("Indeks: " + index);
}
}

W przypadku, gdy wyjtki niszego poziomu mog by przydatne w procesie


szukania przyczyny wyjtku, mona zastosowa odmian translacji wyjtkw,
nazywan czeniem wyjtkw. W przypadku tego podejcia wyjtek niszego poziomu (przyczyna) jest zapamitywany przez wyjtek wyszego poziomu, ktry
zapewnia publiczn metod (Throwable.getCause), umoliwiajc pobranie wyjtku
niszego poziomu.
// czenie wyjtkw
try {
// Wykorzystanie operacji niszej warstwy
// ...
} catch (LowerLevelException cause) {
throw new HigherLevelException(cause);
}

Konstruktor wyjtku wyszego poziomu przekazuje przyczyn do konstruktora


klasy bazowej obsugujcego czenie, wic jest ostatecznie przekazywany do jednego z konstruktorw Throwable obsugujcych czenie, takiego jak Throwable
(Throwable):
// Wyjtki z konstruktorem obsugujcym czenie
class HigherLevelException extends Exception {
HigherLevelException(Throwable cause) {
super(t);
}
}

Wikszo ze standardowych wyjtkw posiada konstruktor obsugujcy czenie.


Dla wyjtkw, ktre ich nie maj, mona ustawi przyczyn przy uyciu metody
initCause z Throwable. czenie wyjtkw nie tylko pozwala nam na programowy
dostp do przyczyny (za pomoc getCause), ale rwnie integruje zapis ladu przyczyny z wyjtkiem wyszego poziomu.
Poniewa translacja wyjtkw doskonale nadaje si do bezmylnej propagacji
wyjtkw midzy warstwami aplikacji, nie powinna by naduywana. Tam, gdzie
jest to moliwe, najlepsz metod obsugi wyjtkw pochodzcych z niszych warstw

267

268

ROZDZIA 9. WYJTKI

jest ich unikanie, przy upewnieniu si przed wywoaniem metody niszej warstwy,
e zostanie ona prawidowo wykonana. Czasami mona to zrealizowa przez sprawdzenie poprawnoci argumentw metody wyszej warstwy przed ich przekazaniem
do metody niszej warstwy.
Jeeli niemoliwe jest zabezpieczenie przed zgaszaniem wyjtkw z niszej warstwy,
kolejnym rozwizaniem jest obejcie tego wyjtku w wyszej warstwie, dziki czemu
izoluje si metody wyszej warstwy od problemw, pochodzcych z warstwy niszej.
W takich przypadkach waciwe jest zapisanie informacji o wystpieniu wyjtku
przy wykorzystaniu mechanizmu rejestrujcego, na przykad java.util.logging.
Pozwala to administratorowi przeledzi problem bez zakcania pracy kodu klienta
i kocowego uytkownika.
Jeeli zatem niemoliwe jest uniemoliwienie wyjtkw z niszych warstw lub ich
obsuga, naley uy tumaczenia wyjtkw, o ile metoda niszego poziomu gwarantuje, e wszystkie jej wyjtki s propagowane do wyszego poziomu. czenie
zapewnia najlepsze cechy obu metod pozwala na zgaszanie odpowiedniego
wyjtku wyszego poziomu oraz przechwytywanie przyczyny w celu pniejszej
analizy awarii (temat 63.).

Temat 62. Dokumentowanie wyjtkw


zgaszanych przez metod
Wan czci dokumentacji, wymagan do waciwego wykorzystywania metod,
jest opis wyjtkw zgaszanych przez metod. Dlatego niezwykle wane jest powicenie pewnego czasu w celu jasnego udokumentowania wszystkich wyjtkw zgaszanych przez metody.
Zawsze deklaruj (za pomoc znacznika Javadoc @throws) poszczeglne przechwytywane wyjtki i opisuj precyzyjnie warunki, w ktrych s one zgaszane. Nie
uatwiaj sobie zadania piszc, e metoda zgasza wyjtek pewnej klasy bazowej,
ktra jest baz dla wielu klas wyjtkw. Jako ekstremalny przykad mona przytoczy deklarowanie, e metoda zgasza wyjtek Exception lub, co gorsza, Throwable
jest to niedopuszczalne. Oprcz tego, e deklaracja taka nie daje adnych wskazwek programicie wykorzystujcemu metod, to jeszcze utrudnia wykorzystanie
tej metody, poniewa zasania wszystkie wyjtki, jakie mog by zgoszone w tym
samym kontekcie.
Cho jzyk nie wymaga deklarowania nieprzechwytywanych wyjtkw, jakie moe
zgasza metoda, warto udokumentowa je tak samo dokadnie, jak wyjtki przechwytywane. Wyjtki nieprzechwytywane z reguy reprezentuj bdy programowe
(temat 58.) i zapoznanie programistw z nimi pomaga im unika tych bdw.

TEMAT 62. DOKUMENTOWANIE WYJTKW ZGASZANYCH PRZEZ METOD

Dobrze opisana lista wyjtkw nieprzechwytywanych, jakie mog by zgaszane


przez metod, efektywnie opisuje warunki wstpne jej prawidowego wykonania.
Niezmiernie wane jest, aby kada dokumentacja metody zawieraa warunki wstpne,
a dokumentowanie wyjtkw nieprzechwytywanych jest najlepszym sposobem
spenienia tego wymagania.
Szczeglnie wane jest udokumentowanie nieprzechwytywanych wyjtkw, jakie
mog zgasza metody zadeklarowane w interfejsie. Dokumentacja ta staje si
fragmentem oglnych zaoe interfejsu i pozwala na jednakowe dziaanie rnych
implementacji tego samego interfejsu.
Naley skorzysta ze znacznika Javadoc @throws do udokumentowania wszystkich
nieprzechwytywanych wyjtkw zgaszanych przez metod, ale nie naley
umieszcza tych wyjtkw w deklaracji metody po sowie kluczowym throws.
Wane jest, aby programista korzystajcy z API wiedzia, ktre wyjtki s przechwytywane, a ktre nie, poniewa jego dziaania rni si w tych dwch przypadkach. Dokumentacja generowana przez znacznik Javadoc @throws w przypadku
braku nagwka metody wygenerowanego przez deklaracj throws zapewnia silny
wizualny sygna pomagajcy programicie odrni przechwytywane wyjtki od
nieprzechwytywanych.
Trzeba zauway, e dokumentowanie wszystkich nieprzechwytywanych wyjtkw
zgaszanych przez metod jest sytuacj idealn, ale nie zawsze osigaln w praktyce.
Gdy klasa jest modyfikowana, dodanie kolejnych nieprzechwytywanych wyjtkw
nie jest naruszeniem zgodnoci rdowej ani binarnej. Zamy, e klasa wywouje
metod z innej, niezalenie tworzonej klasy. Autorzy pierwszej klasy mog dokadnie
udokumentowa wszystkie nieprzechwytywane wyjtki zgaszane w swojej klasie,
ale jeeli druga klasa zostaa zmodyfikowana i zgasza nowe wyjtki, najprawdopodobniej pierwsza klasa (niepodlegajca modyfikacji) dokona propagacji nowego
nieprzechwytywanego wyjtku, cho nie zosta on zadeklarowany.
Jeeli wyjtek jest zgaszany przez wiele metod klasy przy tych samych warunkach, dopuszczalne jest udokumentowanie takiego wyjtku w komentarzach do
klasy zamiast umieszczania go przy kadej metodzie. Przykadem takiego wyjtku
jest NullPointerException. Mona w komentarzu dokumentujcym klas umieci
zdanie podobne do: wszystkie metody tej klasy zgaszaj wyjtek NullPointer
Exception, jeeli do dowolnego parametru przekazana jest warto null.
Naley zatem dokumentowa kady wyjtek, ktry moe by zgaszany przez pisan
metod. Odnosi si to zarwno do przechwytywanych, jak i nieprzechwytywanych
wyjtkw oraz zarwno do metod abstrakcyjnych, jak i konkretnych. Naley napisa
osobne klauzule throws dla kadego przechwytywanego wyjtku i nie pisa klauzul
throws dla wyjtkw nieprzechwytywanych. Jeeli nie udokumentujemy wyjtkw
zgaszanych przez nasz metod, dla innych programistw bdzie trudno uywa
efektywnie naszych klas i interfejsw lub bdzie to nawet niemoliwe.

269

270

ROZDZIA 9. WYJTKI

Temat 63. Udostpnianie danych o bdzie


Gdy program zostanie zatrzymany przez nieprzechwycony wyjtek, system automatycznie drukuje informacje ze stosu wyjtkw. Stos ten zawiera reprezentacj
wyjtku w postaci cigu znakw wynik metody toString. Dane te zwykle skadaj si z nazwy klasy, po ktrej nastpuj informacje szczegowe. Czsto jest to jedyny
fragment informacji, jakie otrzyma programista lub serwis oprogramowania
w przypadku wystpienia bdu w programie. Jeeli trudno powtrzy ten bd,
bardzo trudne moe by uzyskanie dodatkowych danych. Dlatego niezwykle wane
jest, aby metoda toString dla wyjtku zwracaa moliwie duo informacji o przyczynie bdu. Inaczej mwic, cig reprezentujcy wyjtek powinien umoliwia
dalsz analiz bdu.
Aby odszuka bd, cig reprezentujcy wyjtek powinien zawiera wartoci
wszystkich parametrw i pl majcych udzia w przyczynie wyjtku. Na
przykad, w przypadku wyjtku IndexOutOfBounds cig reprezentujcy wyjtek
powinien zawiera warto dolnego i grnego zakresu oraz warto indeksu, ktry
nie zmieci si w tych granicach. Informacje te nios wiele informacji na temat
przyczyny bdu. Kada z tych wartoci moe by nieprawidowa. Aktualna warto
indeksu moe by o jeden mniejsza od dolnego zakresu lub rwna grnemu zakresowi lub moe by to warto cakowicie spoza zakresu w jedn lub drug stron.
Dolna granica moe by wiksza od grnej (powane naruszenie niezmiennikw).
Kada z tych sytuacji wskazuje na inny problem i informacja o tym niezmiernie
uatwia programicie poszukiwanie waciwego bdu.
Cho niezmiernie wane jest doczanie stosownych twardych danych do cigu
reprezentujcego wyjtek, zwykle nie warto jest dodawa zbyt wielu informacji. Zrzut
stosu przeznaczony jest do analizy kodu rdowego i zazwyczaj zawiera dokadne
nazwy plikw i numery wierszy, w ktrych rozpoczynaj si wszystkie metody
umieszczone na stosie. Dugie opisy bdw s zwykle nadmiarowe wikszo
danych moe by zebrana na podstawie analizy kodu rdowego.
Cig reprezentujcy wyjtek nie powinien zawiera komunikatw bdw zrozumiaych dla uytkownika. W przeciwiestwie do komunikatw dla uytkownikw cigi te s przeznaczone dla programistw lub serwisu przy analizie awarii.
Dlatego doczone dane s waniejsze ni czytelno komunikatu.
Jednym ze sposobw upewnienia si, e cig reprezentujcy wyjtek zawiera odpowiednie informacje pomagajce odszuka bd, jest wymaganie tych danych zamiast cigu reprezentujcego wyjtek. Na podstawie podanych informacji komunikat

TEMAT 63. UDOSTPNIANIE DANYCH O BDZIE

moe by generowany automatycznie. Na przykad, zamiast konstruktora z parametrem String, wyjtek IndexOutOfBoundsException mgby mie konstruktor,
wygldajcy nastpujco:
/**
* Tworzy IndexOutOfBoundsException.
*
* @param lowerBound - najmniejsza dopuszczalna warto indeksu.
* @param upperBound - najwiksza dopuszczalna warto indeksu plus jeden.
* @param index
- aktualna warto parametru.
*/
public IndexOutOfBoundException(int lowerBound, int upperBound,
int index) {
// Generowanie komunikatu opisujcego bd
super( "Dolny zakres: " + lowerBound +
", Grny zakres: " + upperBound +
", Indeks: "
+ index);
// Zapisanie informacji i bdzie dla programowego dostpu
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.index = index;
}

Niestety, biblioteki platformy Java nie korzystaj z tego idiomu zbyt czsto, ale
polecamy gorco jego stosowanie. Uatwia to programicie odszukanie bdu
w przypadku zgoszenia wyjtku, a waciwie trudno jest w takiej sytuacji nie znale
bdu. W efekcie stosowania tego idiomu generowanie wysokiej klasy cigu reprezentujcego wyjtek jest przeniesione do klasy wyjtku, nie powodujc nadmiarowego generowania takiego cigu przez wszystkich programistw, korzystajcych z klasy.
W temacie 58. sugerowalimy, e waciwe jest udostpnienie metod w klasie wyjtku,
pozwalajcych na odczytanie danych zapamitanych w czasie generowania wyjtku
(lowerBound, upperBound i index z powyszego przykadu). Metody takie maj wikszy
sens w przypadku wyjtkw przechwytywanych ni w przypadku nieprzechwytywanych, poniewa w trakcie obsugi wyjtku przechwytywanego dane te mog
zosta wykorzystane do przywrcenia normalnego dziaania. Rzadko zdarza si (cho
nie jest to niemoliwe), e programista bdzie potrzebowa dostpu do szczegw
wyjtku nieprzechwytywanego. Nawet w przypadku wyjtkw nieprzechwytywanych jako ogln zasad mona jednak zaleci tworzenie takich metod (temat 10.).

271

272

ROZDZIA 9. WYJTKI

Temat 64. Zachowanie atomowoci


w przypadku bdu
Po zgoszeniu wyjtku przez obiekt zwykle podane jest, aby obiekt cay czas by
w dobrze zdefiniowanym stanie, nawet w przypadku wystpienia bdu w czasie
wykonywania operacji. Jest to szczeglnie wane w przypadku wyjtkw przechwytywanych, ktre wywoujcy moe obsuy. Nieudane wykonanie metody nie
powinno powodowa zmiany stanu obiektu sprzed wywoania tej metody.
Metoda o tej wasnoci nazywana jest atomow w przypadku bdu.
Istnieje kilka sposobw na osignicie tego efektu. Najprostszym jest projektowanie
obiektw niezmiennych (temat 15.). Jeeli obiekt jest niezmienny, atomowo
w przypadku bdu jest zachowana bez adnych nakadw. Jeeli wykonanie operacji
si nie powiedzie, moe to spowodowa, e nie zostanie utworzony wynikowy obiekt,
lecz nigdy nie spowoduje powstania nieustalonego stanu istniejcego obiektu, poniewa stan kadego z obiektw jest ustalany w trakcie tworzenia i nie moe by
pniej modyfikowany.
W przypadku metod operujcych na obiektach modyfikowalnych najczstsz metod osignicia atomowoci w przypadku bdu jest sprawdzanie poprawnoci
parametrw przed wykonaniem operacji (temat 38.). Dziki temu wyjtki s zgaszane przed rozpoczciem modyfikacji obiektu. Dla przykadu przeanalizujmy metod Stack.pop z tematu 6.
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Usuwanie zbdnych referencji
return result;
}

Jeeli usuniemy pocztkowe sprawdzanie rozmiaru stosu, metoda nadal bdzie zgaszaa wyjtek w przypadku prby pobrania elementu z pustego stosu. Jednak bdzie
pozostawiaa pole size w niespjnym (ujemnym) stanie, powodujc, e wszystkie
kolejne wywoania metody zakocz si bdem. Dodatkowo, wyjtek zgaszany
przez metod pop moe by niezgodny z abstrakcj (temat 61.).
Podobnym podejciem przy uzyskiwaniu atomowoci w przypadku bdu jest
porzdkowanie oblicze w taki sposb, aby wszystkie fragmenty, w ktrych mog
wystpi bdy, byy wykonywane przed zmian stanu obiektu. Podejcie to jest

TEMAT 64. ZACHOWANIE ATOMOWOCI W PRZYPADKU BDU

naturalnym rozszerzeniem poprzedniego, gdy argumenty nie mog by sprawdzone


bez wykonania fragmentw operacji. Na przykad, w przypadku TreeMap elementy
s sortowane wedug okrelonego porzdku. Aby doda element do TreeMap, element ten musi mie typ porwnywalny przy uyciu porzdkowania, wykorzystanego
w TreeMap. Prba dodania elementu niewaciwego typu spowoduje zgoszenie wyjtku ClassCastException w wyniku operacji wyszukiwania elementu w drzewie,
zanim drzewo zostanie w jakikolwiek sposb zmienione.
Trzecim, najrzadszym podejciem jest tworzenie kodu odtwarzajcego, ktry wykrywa bdy w czasie operacji i powoduje wycofanie obiektu do stanu z przed rozpoczcia operacji. Podejcie to jest najczciej uywane w przypadku trwaych struktur
danych.
Ostatnim podejciem przy uzyskiwaniu atomowoci w przypadku bdu jest wykonywanie operacji na kopii obiektu i zamiana zawartoci obiektu rdowego
zawartoci kopii po udanym zakoczeniu operacji. Podejcie to jest naturalne
w przypadku, gdy obliczenia na tymczasowych strukturach danych mog by
przeprowadzone z du szybkoci. Dla przykadu, metoda Collections.sort kopiuje wejciow list do tablicy przed rozpoczciem sortowania w celu zmniejszenia
kosztu dostpu do elementw w wewntrznej ptli sortowania. Jest to zrobione
w celu poprawienia wydajnoci, ale dodatkowym zyskiem jest to, e w przypadku
wystpienia bdu wejciowa lista bdzie niezmieniona.
Cho atomowo w przypadku bdu jest korzystna, nie zawsze moe by osignita.
Na przykad, jeeli dwa wtki prbuj rwnolegle zmodyfikowa obiekt bez waciwej
synchronizacji, moe by on pozostawiony w niewaciwym stanie. Dlatego niewaciwe moe by zaoenie, e obiekt bdzie nadawa si do uytku po przechwyceniu wyjtku ConcurrentModificationException. W przypadku bdw
(w przeciwiestwie do wyjtkw) nie jest moliwe odtworzenie stanu obiektu, wic
metody nie musz prbowa osignicia atomowoci w przypadku zgoszenia bdu.
Nawet, gdy atomowo w przypadku bdu jest moliwa do osignicia, nie zawsze
jest podana. W przypadku niektrych operacji bdzie ona znacznie zwikszaa
koszt i stopie skomplikowania kodu. Jednak zwracajc uwag na to zagadnienie,
przekonasz si, e najczciej jest ona atwa do osignicia.
Jako zasad naley przyj, e wyjtki bdce czci specyfikacji metody nie powinny
zmienia stanu obiektu sprzed wywoania tej metody. Gdy zasada ta nie jest speniona,
dokumentacja API powinna jasno wskazywa, w jakim stanie s pozostawiane
obiekty. Niestety, wiele istniejcych dokumentacji API nie spenia tego warunku.

273

274

ROZDZIA 9. WYJTKI

Temat 65. Nie ignoruj wyjtkw


Uwaga ta moe si wydawa oczywista, jednak jest tak czsto naruszana, e wymaga
cigego powtarzania. Gdy projektanci API deklaruj, e metoda zgasza wyjtek, chc
przez to co przekaza. Nie naley tego ignorowa! atwo zignorowa wyjtek,
umieszczajc wywoanie metody wewntrz instrukcji try z pustym blokiem catch:
// Pusty blok catch powoduje zignorowanie wyjtku - wysoce podejrzane!
try {
// ...
} catch (SomeException e) {
}

Pusty blok catch powoduje zignorowanie przeznaczenia wyjtkw, ktrym jest


wymuszenie obsugi sytuacji wyjtkowej. Zignorowanie wyjtku jest analogiczne
do zignorowania alarmu przeciwpoarowego i jego wyczanie, przez co nikt nie
bdzie mia szansy na zaalarmowanie o prawdziwym zagroeniu. Gdy zobaczysz
pusty blok catch, powiniene natychmiast podj rodki zaradcze. Blok catch powinien zawiera co najmniej komentarz, wyjaniajcy, dlaczego wyjtek ten jest
ignorowany.
Przykadem sytuacji, w ktrej mona zignorowa wyjtek, jest renderowanie rysunku
do animacji. Jeeli ekran jest uaktualniany w regularnych odstpach czasu, najlepsz
metod obsugi nietrwaego bdu jest jego zignorowanie i poczekanie na nastpne
uaktualnienie ekranu.
Porada z tego tematu odnosi si zarwno do wyjtkw przechwytywanych, jak i nieprzechwytywanych. Poniewa wyjtki reprezentuj przewidywalne sytuacje wyjtkowe lub bdy w programie, ich zignorowanie spowoduje, e program bdzie
kontynuowa dziaanie bez adnego komunikatu. Program ten moe zatrzyma
si w dowolnym czasie w przyszoci w takim miejscu kodu, ktre nie jest w aden
sposb zwizane ze rdem problemu. Waciwa obsuga bdw moe cakowicie
zapobiec awarii. Zwyke pozostawienie nieprzechwytywanego wyjtku powoduje jego
propagacj i zatrzymanie jego dziaania, zachowujc dane potrzebne do odszukania
rda bdu.

You might also like