Professional Documents
Culture Documents
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?
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
SPIS TRECI
SPIS TRECI
10
SPIS TRECI
Dodatek A
Dodatek B
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.
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
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) {
}
259
260
ROZDZIA 9. WYJTKI
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.
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.
I drugi przypadek.
} catch (TheCheckedException e) {
e.printStackTrace();
System.exit(1);
}
// To ju koniec.
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.
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
IllegalStateException
NullPointerException
IndexOutOfBoundsException
ConcurrentModificationException
UnsupportedOperationException
265
266
ROZDZIA 9. WYJTKI
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.).
269
270
ROZDZIA 9. WYJTKI
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
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
273
274
ROZDZIA 9. WYJTKI