Professional Documents
Culture Documents
ISBN: 978-83-246-7761-0
Authorized translation from the English language edition, entitled CORE JAVA
VOLUME I FUNDAMENTALS, 9TH EDITION; ISBN 0137081898; by Cay S. Horstmann;
and Gary Cornell; published by Pearson Education, Inc, publishing as Prentice Hall.
Copyright 2013 by Oracle and/or its affiliates, 500 Oracle Parkway, Redwood Shores, CA 94065.
All rights reserved. No part of this book may be reproduced or transmitted in any form
or by any means, electronic or mechanical, including photocopying, recording or by any
information storage retrieval system, without permission from Pearson Education Inc.
Wydawnictwo HELION
ul. Kociuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (ksigarnia internetowa, katalog ksiek)
Drogi Czytelniku!
Jeeli chcesz oceni t ksik, zajrzyj pod adres
http://helion.pl/user/opinie/javpd9_ebook
Moesz tam wpisa swoje uwagi, spostrzeenia, recenzj.
Podzikowania .........................................................................................................................................19
Skorowidz ..............................................................................................................................................831
12 Java. Podstawy
Wstp
Do Czytelnika
Jzyk programowania Java pojawi si na scenie pod koniec 1995 roku i od razu zyska sobie
reputacj gwiazdy. Ta nowa technologia miaa si sta uniwersalnym cznikiem pomidzy
uytkownikami a informacj, bez wzgldu na to, czy informacje te pochodziy z serwera sie-
ciowego, bazy danych, serwisu informacyjnego, czy jakiegokolwiek innego rda. I rze-
czywicie, Java ma niepowtarzaln okazj spenienia tych wymaga. Ten zaprojektowany
z niezwyk starannoci jzyk zyska akceptacj wszystkich najwikszych firm z wyjtkiem
Microsoftu. Wbudowane w jzyk zabezpieczenia dziaaj uspokajajco zarwno na progra-
mistw, jak i uytkownikw programw napisanych w Javie. Dziki wbudowanym funkcjom
zaawansowane zadania programistyczne, takie jak programowanie sieciowe, czno pomi-
dzy bazami danych i wielowtkowo, s znacznie prostsze.
Do tej pory pojawio si ju osiem wersji pakietu Java Development Kit. Przez ostatnich osiem-
nacie lat interfejs programowania aplikacji (ang. Application Programming Interface API)
rozrs si z okoo 200 do ponad 3000 klas. API to obejmuje obecnie tak rne aspekty two-
rzenia aplikacji, jak konstruowanie interfejsu uytkownika, zarzdzanie bazami danych, inter-
nacjonalizacja, bezpieczestwo i przetwarzanie XML.
Ksika, ktr trzymasz w rce, jest pierwszym tomem dziewitego wydania ksiki Java.
Podstawy. Kada edycja tej ksiki nastpuje najszybciej, jak to tylko moliwe, po wydaniu
kolejnej wersji pakietu Java Development Kit. Za kadym razem uaktualnialimy tekst ksiki
z uwzgldnieniem najnowszych narzdzi dostpnych w Javie. To wydanie opisuje Java Stan-
dard Edition (SE) 7.
Tak jak w przypadku poprzednich wyda tej ksiki, to rwnie przeznaczone jest dla powa-
nych programistw, ktrzy chc wykorzysta technologi Java w rzeczywistych projektach.
Zakadamy, e odbiorca naszego tekstu jest programist posiadajcym due dowiadczenie
w programowaniu w innym jzyku ni Java. Ponadto prno tu szuka dziecinnych przykadw
(jak tostery, zwierzta z zoo czy rozbiegany tekst). Nic z tych rzeczy tutaj nie znajdziesz.
14 Java. Podstawy
Naszym celem byo przedstawienie wiedzy w taki sposb, aby Czytelnik mg bez problemu
w peni zrozumie zasady rzdzce jzykiem Java i jego bibliotek, a nie tylko myla, e
wszystko rozumie.
Ksika ta zawiera mnstwo przykadw kodu, obrazujcych zasady dziaania niemal ka-
dej opisywanej przez nas funkcji i biblioteki. Przedstawiane przez nas przykadowe programy
s proste, poniewa chcielimy si w nich skoncentrowa na najwaniejszych zagadnieniach.
Niemniej znakomita wikszo z nich zawiera prawdziwy, nieskrcony kod. Powinny dobrze
suy jako punkt wyjcia do pisania wasnych programw.
Wychodzimy z zaoenia, e osoby czytajce t ksik chc (albo wrcz pragn) pozna
wszystkie zaawansowane cechy Javy. Oto kilka przykadowych zagadnie, ktre opisujemy
szczegowo:
programowanie obiektowe,
mechanizm refleksji (ang. reflections) i obiekty proxy,
interfejsy i klasy wewntrzne,
delegacyjny model obsugi zdarze,
projektowanie graficznego interfejsu uytkownika za pomoc pakietu narzdzi
Swing UI,
obsuga wyjtkw,
programowanie generyczne,
kolekcje,
wspbieno.
Ze wzgldu na niebyway wrcz rozwj biblioteki klas Javy opisanie w jednym tomie wszyst-
kich wasnoci tego jzyka, ktrych potrzebuje powany programista, graniczyoby z cudem.
Z tego powodu postanowilimy podzieli nasz ksik na dwa tomy. Pierwszy, ktry trzy-
masz w rku, opisuje podstawy jzyka Java oraz najwaniejsze zagadnienia zwizane z pro-
gramowaniem interfejsu uytkownika. Tom drugi (ktry niebawem si ukae) zawiera informa-
cje dotyczce bardziej zaawansowanych tematw oraz opisuje zoone zagadnienia zwizane
z programowaniem interfejsu uytkownika. Poruszane w nim tematy to:
pliki i strumienie,
obiekty rozproszone,
bazy danych,
zaawansowane komponenty GUI,
metody rodzime,
przetwarzanie dokumentw XML,
programowanie sieciowe,
zaawansowana obrbka grafiki,
Wstp 15
Podczas pisania ksiki nie da si unikn drobnych bdw i wpadek. Bardzo chcielibymy
by o nich informowani. Jednak kad tak informacj wolelibymy otrzyma tylko jeden raz.
W zwizku z tym na stronie http://horstmann.com/corejava zamiecilimy list najczciej
zadawanych pyta, obej i poprawek do bdw. Formularz sucy do wysyania informacji
o bdach i propozycji poprawek zosta celowo umieszczony na kocu strony z errat, aby
zachci potencjalnego nadawc do wczeniejszego zapoznania si z istniejcymi ju infor-
macjami. Nie naley zraa si, jeli nie odpowiemy na kade pytanie lub nie zrobimy tego
natychmiast. Naprawd czytamy wszystkie przychodzce do nas listy i doceniamy wysiki
wszystkich naszych Czytelnikw wkadane w to, aby przysze wydania naszej ksiki byy
jeszcze bardziej zrozumiae i zawieray jeszcze wicej poytecznych informacji.
O ksice
Rozdzia 1. stanowi przegld waciwoci jzyka Java, ktre wyrniaj go na tle innych
jzykw programowania. Wyjaniamy, co projektanci chcieli zrobi, a co si im udao. Nastp-
nie krtko opisujemy histori powstania Javy oraz sposb, w jaki ewoluowaa.
W rozdziale 2. opisujemy proces pobierania i instalacji pakietu JDK (ang. Java Development
Kit) oraz doczonych do tej ksiki przykadw kodu. Nastpnie opisujemy krok po kroku
kompilacj i uruchamianie trzech typowych programw w Javie: aplikacji konsolowej, apli-
kacji graficznej i apletu. Naszymi narzdziami s czyste rodowisko JDK, edytor tekstowy
obsugujcy Jav i zintegrowane rodowisko programowania (ang. Integrated Development
Environment IDE) dla Javy.
W rozdziale 8. szczegowo opisujemy model zdarze AWT (ang. Abstract Window Toolkit).
Nauczymy si pisa programy reagujce na zdarzenia, takie jak kliknicie przyciskiem myszy
albo nacinicie klawisza na klawiaturze. Dodatkowo opanujesz techniki pracy nad takimi
elementami GUI jak przyciski i panele.
Rozdzia 9. zawiera bardzo szczegowy opis pakietu Swing. Pakiet ten umoliwia tworzenie
niezalenych od platformy graficznych interfejsw uytkownika. Nauczysz si posugiwa
rnego rodzaju przyciskami, komponentami tekstowymi, obramowaniami, suwakami, polami
list, menu i oknami dialogowymi. Niektre zaawansowane komponenty zostay opisane dopiero
w drugim tomie.
Rozdzia 10. zawiera informacje na temat wdraania programw jako aplikacji lub apletw.
Nauczysz si pakowa programy do plikw JAR oraz udostpnia aplikacje poprzez internet
za pomoc mechanizmw Java Web Start i apletw. Na zakoczenie opisujemy, w jaki sposb
Java przechowuje i wyszukuje informacje na temat konfiguracji ju po ich wdroeniu.
Tematem rozdziau 13. s kolekcje. Chcc zebra wiele obiektw, aby mc ich pniej uy,
najlepiej posuy si kolekcj, zamiast po prostu wrzuca wszystkie obiekty do tablicy. W roz-
dziale tym nauczysz si korzysta ze standardowych kolekcji, ktre s wbudowane w jzyk
i gotowe do uytku.
Koczcy ksik rozdzia 14. zawiera opis wielowtkowoci, ktra umoliwia programo-
wanie w taki sposb, aby rne zadania byy wykonywane jednoczenie (wtek to przepyw
sterowania w programie). Nauczysz si ustawia wtki i panowa nad ich synchronizacj.
Jako e wielowtkowo w Java 5.0 ulega powanym zmianom, opisujemy wszystkie nowe
mechanizmy z ni zwizane.
Konwencje typograficzne
Podobnie jak w wielu ksikach komputerowych, przykady kodu programw pisane s
czcionk o staej szerokoci znakw.
Programy, ktrych kod rdowy mona znale w internecie, s oznaczane jako listingi, np.:
Przykady kodu
W witrynie towarzyszcej tej ksice, pod adresem www.helion.pl/ksiazki/javpd9.htm,
opublikowane s w postaci skompresowanego archiwum wszystkie pliki z przykadami kodu
rdowego. Mona je wypakowa za pomoc dowolnego programu otwierajcego paczki
ZIP albo przy uyciu narzdzia jar dostpnego w zestawie Java Development Kit. Wicej
informacji na temat tego pakietu i przykady kodu mona znale w rozdziale 2.
Podzikowania
Pisanie ksiki to zawsze ogromny wysiek, a pisanie kolejnego wydania nie wydaje si duo
atwiejsze, zwaszcza kiedy wemie si pod uwag cige zmiany zachodzce w technologii
Java. Aby ksika moga powsta, potrzeba zaangaowania wielu osb. Dlatego te z wielk
przyjemnoci chciabym podzikowa za wspprac caemu zespoowi Core Java.
Wiele cennych uwag pochodzi od osb z wydawnictwa Prentice Hall, ktrym udao si pozo-
sta w cieniu. Chciabym, aby wszystkie te osoby wiedziay, e bardzo doceniam ich prac.
Jak zawsze gorce podzikowania kieruj do mojego redaktora z wydawnictwa Prentice
Hall Grega Doencha za przeprowadzenie tej ksiki przez proces pisania i produkcji
oraz za to, e pozwoli mi pozosta w bogiej niewiadomoci istnienia wszystkich osb pra-
cujcych w zapleczu. Jestem wdziczny Julie Nahil za doskonae wsparcie w dziedzinie pro-
dukcji, oraz Dmitryemu i Alinie Kirsanovom za korekt i skad. Dzikuj rwnie mojemu
wspautorowi poprzednich wyda tej ksiki Garyemu Cornellowi, ktry podj inne
wyzwania.
Do recenzentw tego wydania i poprzednich edycji ksiki nale: Chuck Allison (Utah Valley
University), Lance Andersen (Oracle), Alec Beaton (IBM), Cliff Berg, Joshua Bloch, David
Brown, Corky Cartwright, Frank Cohen (PushToTest), Chris Crane (devXsolution), dr Nicholas
J. De Lillo (Manhattan College), Rakesh Dhoopar (Oracle), David Geary (Clarity Training),
Jim Gish (Oracle), Brian Goetz (Oracle), Angela Gordon, Dan Gordon (Electric Cloud), Rob
Gordon, John Gray (University of Hartford), Cameron Gregory (olabs.com), Marty Hall
(coreservlets.com, Inc.), Vincent Hardy (Adobe Systems), Dan Harkey (San Jose State Univer-
sity), William Higgins (IBM), Vladimir Ivanovic (PointBase), Jerry Jackson (CA Technolo-
gies), Tim Kimmet (Walmart), Chris Laffra, Charlie Lai (Apple), Angelika Langer, Doug
Langston, Hang Lau (McGill University), Mark Lawrence, Doug Lea (SUNY Oswego),
Gregory Longshore, Bob Lynch (Lynch Associates), Philip Milne (konsultant), Mark Mor-
rissey (The Oregon Graduate Institute), Mahesh Neelakanta (Florida Atlantic University),
20 Java. Podstawy
Hao Pham, Paul Philion, Blake Ragsdell, Stuart Reges (University of Arizona), Rich Rosen
(Interactive Data Corporation), Peter Sanders (ESSI University, Nicea, Francja), dr Paul Sang-
hera (San Jose State University, Brooks College), Paul Sevinc (Teamup AG), Devang Shah
(Sun Microsystems), Bradley A. Smith, Steven Stelting (Oracle), Christopher Taylor, Luke
Taylor (Valtech), George Thiruvathukal, Kim Topley (StreamingEdge), Janet Traub, Paul
Tyma (konsultant), Peter van der Linden (Motorola Mobile Devices), Burt Walsh, Dan Xu
(Oracle) i John Zavgren (Oracle).
Cay Horstmann
San Francisco, Kalifornia
wrzesie 2012
1
Wstp do Javy
W tym rozdziale:
Java jako platforma programistyczna
Sowa klucze biaej ksigi Javy
Aplety Javy i internet
Krtka historia Javy
Najczstsze nieporozumienia dotyczce Javy
Pierwsze wydanie Javy w 1996 roku wywoao ogromne emocje nie tylko w prasie kom-
puterowej, ale take w takich mediach nalecych do gwnego nurtu, jak The New York
Times, The Washington Post czy Business Week. Jzyk Java zosta jako pierwszy i do
tej pory jedyny jzyk programowania wyrniony krtk, bo trwajc 10 minut, wzmiank
w National Public Radio. Kapita wysokiego ryzyka w wysokoci 100 000 000 dolarw zosta
zebrany wycznie dla produktw powstaych przy zastosowaniu okrelonego jzyka kom-
puterowego. Wracanie dzisiaj do tych wietnych czasw jest bardzo zabawne, a wic w tym
rozdziale krtko opisujemy histori jzyka Java.
Ten cay szum wok Javy jako jzyka programowania jest przesadzony. Java to z pewnoci
dobry jzyk programowania. Nie ma wtpliwoci, e jest to jedno z najlepszych narzdzi
dostpnych dla powanych programistw. Naszym zdaniem mogaby by wspaniaym jzy-
kiem programowania, ale na to jest ju chyba zbyt pno. Kiedy przychodzi do rzeczywistych
zastosowa, swoj gow podnosi ohydna zmora zgodnoci z istniejcym ju kodem.
22 Java. Podstawy
Za ten akapit na naszego redaktora posypay si gromy ze strony kogo bardzo wysoko posta-
wionego w firmie Sun Microsystems, kogo nazwiska wolimy nie ujawnia. Jednak z perspek-
tywy czasu wydaje si, e nasze przewidywania byy suszne. Java ma mnstwo bardzo poy-
tecznych cech, ktre opisujemy w dalszej czci tego rozdziau. Ma te jednak pewne wady,
a najnowsze dodatki do jzyka ze wzgldu na zgodno nie s ju tak eleganckie jak kiedy.
Jak jednak napisalimy w pierwszym wydaniu tej ksiki, Java nigdy nie bya tylko jzykiem.
Istnieje bardzo duo jzykw programowania, a tylko kilka z nich zrobio furor. Java to
caa platforma z du bibliotek zawierajc ogromne iloci gotowego do wykorzystania kodu
oraz rodowisko wykonawcze, ktre zapewnia bezpieczestwo, przenono midzy rnymi
systemami operacyjnymi oraz automatyczne usuwanie nieuytkw (ang. garbage collecting).
Jako programici damy jzyka o przyjaznej skadni i zrozumiaej semantyce (a wic nie C++).
Do tego opisu pasuje Java, jak rwnie wiele innych dobrych jzykw programowania. Nie-
ktre z nich oferuj przenono, zbieranie nieuytkw itd., ale nie maj bogatych bibliotek,
przez co zmuszeni jestemy pisa wasne, kiedy chcemy wykona obrbk grafiki, stworzy
aplikacj sieciow bd czc si z baz danych. C, Java ma to wszystko jest to dobry
jzyk, ktry oddaje do dyspozycji programisty wysokiej jakoci rodowisko wykonawcze wraz
z ogromn bibliotek. To wanie to poczenie sprawia, e tak wielu programistw nie potrafi
oprze si urokowi Javy.
3. sieciowy,
4. niezawodny,
5. bezpieczny,
6. niezaleny od architektury,
7. przenony,
8. interpretowany,
9. wysokowydajny,
10. wielowtkowy,
11. dynamiczny.
Rozdzia 1. Wstp do Javy 23
W trakcie pisania tej ksiki biaa ksiga Javy bya dostpna pod adresem
http://www.oracle.com/technetwork/java/langenv-140151.html.
1.2.1. Prosty
Naszym celem byo zbudowanie takiego systemu, ktry mona zaprogramowa bez
ukoczenia tajemnych szkole, a ktry podtrzymywaby obecne standardowe praktyki.
W zwizku z tym mimo e w naszym przekonaniu jzyk C++ nie nadawa si do tego
celu Java pod wzgldem projektowym jest do niego podobna, jak to tylko moliwe.
Dziki temu nasz system jest bardziej zrozumiay. Java jest pozbawiona wielu rzadko
uywanych, sabo poznanych i wywoujcych zamieszanie funkcji, ktre zgodnie z naszymi
dowiadczeniami przynosz wicej zego ni dobrego.
Skadnia Javy rzeczywicie jest oczyszczon wersj skadni jzyka C++. Nie ma potrzeby
doczania plikw nagwkowych, posugiwania si arytmetyk wskanikow (a nawet skadni
wskanikow), strukturami, uniami, przecianiem operatorw, wirtualnymi klasami bazo-
wymi itd. (wicej rnic pomidzy Jav a C++ mona znale w uwagach rozmieszczonych
na kartach tej ksiki). Nie jest jednak tak, e projektanci pozbyli si wszystkich waciwoci
jzyka C++, ktre nie byy eleganckie. Na przykad nie zrobiono nic ze skadni instrukcji
switch. Kady, kto zna jzyk C++, z atwoci przeczy si na skadni jzyka Java.
Osoby przyzwyczajone do rodowisk wizualnych (jak Visual Basic) przy nauce Javy bd
napotyka trudnoci. Trzeba poj mnstwo dziwnych elementw skadni (cho nie zabiera
to zbyt duo czasu). Wiksze znaczenie ma to, e w Javie trzeba znacznie wicej pisa.
Pikno jzyka Visual Basic polega na tym, e dua cz infrastruktury aplikacji jest automa-
tycznie dostarczana przez rodowisko programistyczne. Wszystko to w Javie trzeba napisa
wasnorcznie, a to z reguy wymaga do duej iloci kodu. Istniej jednak rodowiska
udostpniane przez niezalenych producentw, ktre umoliwiaj programowanie w stylu
przecignij i upu.
W tamtych czasach byo to niebywae wrcz osignicie. Oczywicie od tamtej pory biblioteka
Javy rozrosa si do nieprawdopodobnych rozmiarw. W zwizku z tym powstaa oddzielna
wersja Javy o nazwie Java Micro Edition z mniejsz bibliotek, ktra nadaje si do stoso-
wania na maych urzdzeniach.
24 Java. Podstawy
1.2.2. Obiektowy
Mwic krtko, projektowanie obiektowe to technika programowania, ktrej punktem
centralnym s dane (czyli obiekty) oraz interfejsy dajce dostp do tych obiektw.
Przez analogi obiektowy stolarz byby przede wszystkim zainteresowany krzesem,
ktre ma zrobi, a potrzebne do tego narzdzia stawiaby na drugim miejscu.
Nieobiektowy stolarz z kolei na pierwszym miejscu stawiaby swoje narzdzia. Narzdzia
zwizane z programowaniem obiektowym w Javie s w zasadzie takie jak w C++.
Obiektowa metoda programowania udowodnia swoj warto w cigu ostatnich 30 lat. Jest
nie do pomylenia, aby jakikolwiek nowoczesny jzyk z niej nie korzysta. Rzeczywicie
waciwoci Javy, dziki ktrym mona nazywa j jzykiem obiektowym, s podobne do
jzyka C++. Gwna rnica pomidzy tymi dwoma jzykami objawia si w wielodziedzi-
czeniu, ktre w Javie zostao zastpione prostszymi interfejsami, oraz w modelu metaklas
Javy (ktry jest opisany w rozdziale 5.).
1.2.3. Sieciowy
Java ma bogat bibliotek procedur wspomagajcych prac z takimi protokoami
TCP/IP jak HTTP i FTP. Aplikacje w tym jzyku mog uzyskiwa dostp poprzez sie
do obiektw z tak sam atwoci, jakby znajdoway si one w lokalnym systemie plikw.
W naszej ocenie funkcje sieciowe Javy s zarwno solidne, jak i atwe w uyciu. Kady, kto
kiedykolwiek sprbowa programowania sieciowego w innym jzyku programowania, bdzie
zachwycony tym, jak proste s tak niegdy uciliwe zadania jak poczenia na poziomie
gniazd (ang. socket connection); programowanie sieciowe opisalimy w drugim tomie. Mecha-
nizm zdalnych wywoa metod umoliwia komunikacj pomidzy obiektami rozproszonymi
(take opisany w drugim tomie).
1.2.4. Niezawodny
Java zostaa stworzona do pisania programw, ktre musz by niezawodne w rozmaitych
sytuacjach. Duo uwagi powicono wczesnemu sprawdzaniu moliwoci wystpienia
ewentualnych problemw, pniejszemu sprawdzaniu dynamicznemu (w trakcie
dziaania programu) oraz wyeliminowaniu sytuacji, w ktrych atwo popeni bd.
Najwiksza rnica pomidzy Jav a C/C++ polega na tym, e model wskanikowy
tego pierwszego jzyka jest tak zaprojektowany, aby nie byo moliwoci nadpisania
pamici i zniszczenia w ten sposb danych.
Rozdzia 1. Wstp do Javy 25
Jest to take bardzo przydatna funkcja. Kompilator Javy wykrywa wiele bdw, ktre w innych
jzykach ujawniyby si dopiero po uruchomieniu programu. Wracajc do wskanikw, kady,
kto spdzi wiele godzin na poszukiwaniu uszkodzenia w pamici spowodowanego bdnym
wskanikiem, bdzie bardzo zadowolony z Javy.
Osoby programujce do tej pory w jzyku takim jak Visual Basic, w ktrym nie stosuje si jaw-
nie wskanikw, pewnie zastanawiaj si, czemu s one takie wane. Programici jzyka C
nie maj ju tyle szczcia. Im wskaniki potrzebne s do uzyskiwania dostpu do acuchw,
tablic, obiektw, a nawet plikw. W jzyku Visual Basic w adnej z wymienionych sytuacji
nie stosuje si wskanikw ani nie trzeba zajmowa si przydzielaniem dla nich pamici.
Z drugiej jednak strony w jzykach pozbawionych wskanikw trudniej jest zaimplemen-
towa wiele rnych struktur danych. Java czy w sobie to, co najlepsze w obu tych podej-
ciach. Wskaniki nie s potrzebne do najczciej uywanych struktur, jak acuchy czy tablice.
Moliwoci stwarzane przez wskaniki s jednak cay czas w zasigu rki przydaj si na
przykad w przypadku list dwukierunkowych (ang. linked lists). Ponadto cay czas jestemy
w peni bezpieczni, poniewa nie ma moliwoci uzyskania dostpu do niewaciwego wska-
nika, popenienia bdu przydzielania pamici ani koniecznoci wystrzegania si przed wycie-
kami pamici.
1.2.5. Bezpieczny
Java jest przystosowana do zastosowa w rodowiskach sieciowych i rozproszonych.
W tej dziedzinie pooono duy nacisk na bezpieczestwo. Java umoliwia tworzenie
systemw odpornych na wirusy i ingerencj.
W pierwszym wydaniu naszej ksiki napisalimy: Nigdy nie mw nigdy i okazao si, e
mielimy racj. Niedugo po wydaniu pakietu JDK zesp ekspertw z Princeton University
znalaz ukryte bdy w zabezpieczeniach Java 1.0. Firma Sun Microsystems zaprosia pro-
gramistw do zbadania zabezpiecze Javy, udostpniajc publicznie specyfikacj i imple-
mentacj wirtualnej maszyny Javy oraz biblioteki zabezpiecze. Wszystkie znane bdy zabez-
piecze zostay szybko naprawione. Dziki temu przechytrzenie zabezpiecze Javy jest nie
lada sztuk. Znalezione do tej pory bdy byy cile zwizane z techniczn stron jzyka
i byo ich niewiele.
Pewna liczba zabezpiecze zostaa dodana do Javy z biegiem czasu. Od wersji 1.1 istnieje poj-
cie klasy podpisanej cyfrowo (ang. digitally signed class), ktre opisane jest w drugim tomie.
Dziki temu zawsze wiadomo, kto napisa dan klas. Jeli ufamy klasie napisanej przez
kogo innego, mona da jej wiksze przywileje.
26 Java. Podstawy
Nie jest to adna nowo. Ju ponad 30 lat temu Niklaus Wirth zastosowa t technik
w swoich oryginalnych implementacjach systemw Pascal i UCSD.
Maszyna wirtualna Javy ma take inne zalety. Zwiksza bezpieczestwo, poniewa moe
kontrolowa dziaanie sekwencji instrukcji. Niektre programy tworz nawet kod bajtowy
w locie, tym samym dynamicznie zwikszajc moliwoci dziaajcego programu.
1.2.7. Przenony
W przeciwiestwie do jzykw C i C++ Java nie jest w aden sposb uzaleniona
od implementacji. Rozmiary podstawowych typw danych s okrelone, podobnie
jak wykonywane na nich dziaania arytmetyczne.
Na przykad typ int w Javie zawsze oznacza 32-bitow liczb cakowit. W C i C++ typ int
moe przechowywa liczb cakowit 16-, 32-bitow lub o dowolnym innym rozmiarze,
jaki wymyli sobie twrca kompilatora. Jedyne ograniczenie polega na tym, e typ int nie
moe by mniejszy ni typ short int i wikszy ni long int. Ustalenie rozmiarw typw
liczbowych spowodowao zniknicie gwnego problemu z przenoszeniem programw. Dane
binarne s przechowywane i przesyane w ustalonym formacie, dziki czemu unika si nie-
porozumie zwizanych z kolejnoci bajtw. acuchy s przechowywane w standardo-
wym formacie Unicode.
Rozdzia 1. Wstp do Javy 27
Kady, kto kiedykolwiek prbowa napisa program, ktry mia wyglda dobrze w syste-
mie Windows, na komputerach Macintosh i w dziesiciu rnych odmianach Uniksa, wie,
jak ogromny jest to wysiek. Java 1.0 wykonaa to heroiczne zadanie i udostpnia prosty
zestaw narzdzi, ktre odwzorowyway elementy interfejsu uytkownika na kilku rnych
platformach. Niestety, w wyniku tego powstaa biblioteka, ktra przy duym nakadzie pracy
dawaa ledwie akceptowalne w rnych systemach rezultaty (dodatkowo na rnych plat-
formach wystpoway rne bdy). Ale to byy dopiero pocztki. W wielu aplikacjach od
piknego interfejsu uytkownika waniejsze s inne rzeczy wanie takie aplikacje korzy-
stay na pierwszych wersjach Javy. Obecny zestaw narzdzi do tworzenia interfejsu uyt-
kownika jest napisany od nowa i nie jest uzaleniony od interfejsu uytkownika hosta. Wyni-
kiem jest znacznie spjniejszy i w naszym odczuciu atrakcyjniejszy interfejs ni ten, ktry
by dostpny we wczesnych wersjach Javy.
1.2.8. Interpretowany
Interpreter Javy moe wykona kady kod bajtowy Javy bezporednio na urzdzeniu,
na ktrym interpreter ten zainstalowano. Jako e czenie jest bardziej inkrementalnym
i lekkim procesem, proces rozwoju moe by znacznie szybszy i bardziej odkrywczy.
czenie narastajce ma swoje zalety, ale opowieci o korzyciach pyncych z jego stoso-
wania w procesie rozwoju aplikacji s przesadzone. Pierwsze narzdzia Javy rzeczywicie
byy powolne. Obecnie kod bajtowy jest tumaczony przez kompilator JIT (ang. just-in-time
compiler) na kod maszynowy.
1.2.9. Wysokowydajny
Mimo e wydajno interpretowanego kodu bajtowego jest zazwyczaj wicej ni
wystarczajca, zdarzaj si sytuacje, w ktrych potrzebna jest wiksza wydajno.
Kod bajtowy moe by tumaczony w locie (w trakcie dziaania programu) na kod
maszynowy przeznaczony dla okrelonego procesora, na ktrym dziaa aplikacja.
1.2.10. Wielowtkowy
Korzyci pynce z wielowtkowoci to lepsza interaktywno i dziaanie w czasie
rzeczywistym.
Kady, kto prbowa programowania wielowtkowego w innym jzyku ni Java, bdzie mile
zaskoczony tym, jak atwe jest to w Javie. Wtki w Javie mog korzysta z systemw wie-
loprocesorowych, jeli podstawowy system operacyjny to umoliwia. Problem w tym, e
implementacje wtkw na rnych platformach znacznie si rni, a Java nic nie robi pod
tym wzgldem, aby zapewni niezaleno od platformy. Taki sam w rnych urzdzeniach
pozostaje tylko kod sucy do wywoywania wielowtkowoci. Implementacja wielowt-
kowoci jest w Javie zrzucana na system operacyjny lub bibliotek wtkw. Niemniej atwo,
z jak przychodzi korzystanie z niej w Javie, sprawia, e jest ona bardzo kuszc propozycj,
jeli chodzi o programowanie po stronie serwera.
1.2.11. Dynamiczny
Java jest bardziej dynamicznym jzykiem ni C i C++ pod wieloma wzgldami. Zostaa
zaprojektowana tak, aby dostosowywa si do ewoluujcego rodowiska. Do bibliotek
mona bez przeszkd dodawa nowe metody i zmienne egzemplarzy, nie wywierajc
adnego wpywu na klienty. Sprawdzanie informacji o typach w Javie nie sprawia
trudnoci.
Cecha ta jest wana w sytuacjach, kiedy trzeba doda kod do dziaajcego programu.
Najwaniejszy przykad takiej sytuacji to pobieranie kodu z internetu w celu uruchomienia
w przegldarce. W Javie 1.0 sprawdzenie typu w czasie dziaania programu byo proste, ale
w aktualnej wersji Javy programista ma moliwo penego wgldu zarwno w struktur, jak
i dziaanie obiektw. Jest to niezwykle wane, zwaszcza dla systemw, w ktrych konieczne
jest analizowanie obiektw w czasie pracy, takich jak kreatory GUI Javy, inteligentne debugery,
komponenty zdolne do podczania si w czasie rzeczywistym oraz obiektowe bazy danych.
e firma Sun udziela licencji na kod rdowy Javy i nie zezwala na wprowadzanie adnych
zmian w jzyku i bibliotece standardowej, kady aplet powinien dziaa w kadej przegl-
darce reklamowanej jako obsugujca Jav. Najnowsz wersj oprogramowania pobiera si
w trakcie odwiedzin strony internetowej zawierajcej aplet. Najwaniejsze jest jednak to, e
dziki zabezpieczeniom maszyny wirtualnej nie trzeba si obawia atakw ze strony zoli-
wego kodu.
Pobieranie apletu odbywa si w podobny sposb jak wstawianie obrazu na stron internetow.
Aplet integruje si ze stron, a tekst otacza go ze wszystkich stron jak obraz. Rnica polega
na tym, e ten obraz jest ywy. Reaguje na polecenia uytkownika, zmienia wygld oraz prze-
sya dane pomidzy komputerem, na ktrym zosta uruchomiony, a komputerem, z ktrego
pochodzi.
Rysunek 1.1 przedstawia dobry przykad dynamicznej strony internetowej, na ktrej wykony-
wane s skomplikowane obliczenia. Aplet Jmol wywietla budow czsteczek. Wywietlon
czsteczk mona za pomoc myszy obraca w rne strony, co pozwala lepiej zrozumie jej
budow. Tego typu bezporednia manipulacja obiektami nie jest moliwa na statycznych stro-
nach WWW, ale w apletach tak (aplet ten mona znale na stronie http://jmol.sourceforge.net).
Kiedy aplety pojawiy si na scenie, wywoay niemae poruszenie. Wielu ludzi uwaa, e to
wanie dziki zaletom apletw Java zyskaa tak du popularno. Jednak pocztkowe zauro-
czenie przemienio si szybko w rozczarowanie. Rne wersje przegldarek Netscape i Internet
Explorer dziaay z rnymi wersjami Javy. Niektre z nich byy przestarzae. Ze wzgldu na
t przykr sytuacj tworzenie apletw przy wykorzystaniu najnowszych wersji Javy byo
coraz trudniejsze. Obecnie wikszo dynamicznych efektw na stronach internetowych jest
realizowana za pomoc JavaScriptu i technologii Flash. Java natomiast staa si najpopu-
larniejszym jzykiem do tworzenia aplikacji dziaajcych po stronie serwera, ktre generuj
strony internetowe i stanowi ich zaplecze logiczne.
30 Java. Podstawy
Historia Javy siga 1991 roku, kiedy zesp inynierw z firmy Sun, ktrego przewodnicz-
cymi byli Patrick Naughton i (wszdobylski geniusz komputerowy) James Gosling, piastujcy
jedno z najwyszych stanowisk w firmie o nazwie Sun Fellow, postanowi zaprojektowa
niewielki jzyk programowania nadajcy si do uytku w takich urzdzeniach konsumenc-
kich jak tunery telewizji kablowej. Jako e urzdzenia te nie dysponuj du moc ani pami-
ci, zaoono, e jzyk musi by bardzo niewielki i powinien generowa zwizy kod. Ponadto
ze wzgldu na fakt, e producenci mog w swoich urzdzeniach stosowa rne procesory,
jzyk ten nie mg by zwizany tylko z jedn architektur. Projekt otrzyma kryptonim Green.
Jako e pracownicy firmy Sun obracali si w rodowisku uniksowym, swj jzyk oparli na
C++, a nie na Pascalu. Stworzony przez nich jzyk by obiektowy, a nie proceduralny. Jak
jednak mwi w wywiadzie Gosling: Przez cay czas jzyk by tylko narzdziem, a nie celem.
Gosling zdecydowa si nazwa swj jzyk Oak (db), prawdopodobnie dlatego e lubi
widok dbu stojcego za oknem jego biura w Sun. Pniej odkryto, e jzyk programowania
o tej nazwie ju istnia, i zmieniono nazw na Java. Okazao si to strzaem w dziesitk.
W 1992 roku inynierowie skupieni wok projektu Green przedstawili swoje pierwsze dzieo
o nazwie *7. By to niezwykle inteligentny pilot zdalnego sterowania (mia moc stacji SPARC
zamknit w pudeku o wymiarach 151010 centymetrw). Niestety, nikt w firmie Sun nie
by nim zainteresowany, przez co inynierowie musieli znale inny sposb na wypromo-
wanie swojej technologii. Jednak adna z typowych firm produkujcych elektronik uyt-
kow nie wykazaa zainteresowania. Nastpnym krokiem zespou by udzia w przetargu na
utworzenie urzdzenia TV Box obsugujcego takie nowe usugi telewizji kablowej jak filmy
na danie. Nie dostali jednak kontraktu (co zabawne, umow podpisa ten sam Jim Clark,
ktry zaoy firm Netscape firma ta miaa duy wkad w sukces Javy).
Inynierowie pracujcy nad projektem Green (przechrzczonym na First Person, Inc.) sp-
dzili cay rok 1993 i poow 1994 na poszukiwaniu kupca dla ich technologii nie znaleli
nikogo (Patrick Naughton, ktry by jednym z zaoycieli zespou i zajmowa si promocj
jego produktw, twierdzi, e uzbiera 300 000 punktw Air Miles, prbujc sprzeda ich
technologi). Projekt First Person przesta istnie w 1994 roku.
Rozdzia 1. Wstp do Javy 31
Podczas gdy w firmie Sun miay miejsce te wszystkie wydarzenia, sie oglnowiatowa bdca
czci internetu cay czas si rozrastaa. Kluczem do sieci jest przegldarka, ktra inter-
pretuje hipertekst i wywietla wynik na ekranie monitora. W 1994 roku wikszo uytkow-
nikw internetu korzystaa z niekomercyjnej przegldarki o nazwie Mosaic, ktra powstaa
w 1993 roku w centrum komputerowym uniwersytetu Illinois (pracowa nad ni midzy
innymi Marc Andreessen, ktry by wtedy studentem tego uniwersytetu i dostawa 6,85 dolara
za godzin. Andreessen zdoby saw i pienidze jako jeden ze wspzaoycieli i szef dziau
technologii firmy Netscape).
W wywiadzie dla SunWorld Gosling przyzna, e w poowie 1994 roku projektanci jzyka
zdali sobie spraw, i mogli stworzy naprawd dobr przegldark. Bya to jedna z nie-
wielu aplikacji klient-serwer nalecych do gwnego nurtu, wymagajca tych dziwnych rze-
czy, ktre zrobilimy, czyli niezalenoci od architektury, pracy w czasie rzeczywistym, nie-
zawodnoci i bezpieczestwa. W wiecie stacji roboczych pojcia te nie miay wielkiego
znaczenia. Postanowilimy wic napisa przegldark internetow.
Pierwsze wydanie Javy firma Sun opublikowaa na pocztku 1996 roku. Szybko zorientowano
si, e Java 1.0 nie stanie si narzdziem wykorzystywanym do tworzenia powanych apli-
kacji. Oczywicie mona byo za jej pomoc stworzy nerwowo poruszajcy si tekst w obsza-
rze roboczym przegldarki, ale nie byo ju na przykad moliwoci drukowania. Mwic
szczerze, Java 1.0 nie bya gotowa na wielkie rzeczy. W kolejnej wersji, Java 1.1, uzupe-
niono najbardziej oczywiste braki, znacznie ulepszono refleksj i dodano model zdarze dla
programowania GUI. Jednak nadal moliwoci byy raczej ograniczone.
Poza wydaniem standardowym opracowano jeszcze dwa inne: Micro Edition dla urzdze
takich jak telefony komrkowe oraz Enterprise Edition do przetwarzania po stronie serwera.
Ta ksika koncentruje si na wersji standardowej.
Kolejne wersje Java 1.3 i Java 1.4 to stopniowe ulepszenia w stosunku do pocztkowej wersji
Java 2. Jednoczenie rozrastaa si biblioteka standardowa, zwikszaa si wydajno i oczy-
wicie poprawiono wiele bdw. W tym samym czasie ucicha wrzawa wok apletw i apli-
kacji dziaajcych po stronie klienta, a Java staa si najczciej wybieran platform do two-
rzenia aplikacji dziaajcych po stronie serwera.
32 Java. Podstawy
Wersja 6 (bez przyrostka .0) ujrzaa wiat pod koniec 2006 roku. Tym razem rwnie nie
wprowadzono adnych zmian w jzyku, ale zastosowano wiele usprawnie zwizanych
z wydajnoci i rozszerzono bibliotek.
Tabela 1.1 przedstawia ewolucj jzyka Java i jego biblioteki. Jak wida, rozmiar interfejsu
programistycznego (API) rs w rekordowym tempie.
Java jest jzykiem programowania, a HTML to sposb opisu struktury stron internetowych.
Nie maj ze sob nic wsplnego z wyjtkiem tego, e w HTML s dostpne rozszerzania
umoliwiajce wstawianie apletw Javy na strony HTML.
Java to jzyk programowania, a XML jest sposobem opisu danych. Dane w formacie XML
mona przetwarza za pomoc wielu jzykw programowania, ale API Javy ma doskonae
narzdzia do przetwarzania XML. Ponadto wiele znaczcych narzdzi XML jest zaimplemen-
towanych w Javie. Wicej informacji na ten temat znajduje si w drugiej czci tej ksiki.
aden jzyk programowania o tak duych moliwociach jak Java nie jest atwy do nauczenia
si. Trzeba odrnia, jak atwo napisa program do zabawy i jak trudno napisa powan
aplikacj. Warto zauway, e opisowi jzyka Java w tej ksice powicone zostay tylko
cztery rozdziay. Pozostae rozdziay w obu czciach opisuj sposoby wykorzystania tego
jzyka przy uyciu bibliotek Javy. Biblioteki te zawieraj tysice klas i interfejsw oraz
dziesitki tysicy funkcji. Na szczcie nie trzeba ich wszystkich zna, ale trzeba zapozna
si z zaskakujco du ich liczb, aby mc zrobi cokolwiek dobrego w Javie.
Teoretycznie jest to moliwe i praktycznie wszyscy poza firm Microsoft chcieliby, aby tak
si stao. Jednak wiele aplikacji, ktre bardzo dobrze dziaaj na komputerach biurkowych,
nie dziaaoby prawidowo na innych urzdzeniach lub w przegldarkach. Ponadto aplikacje te
zostay napisane w taki sposb, aby maksymalnie wykorzysta moliwoci procesora i na-
tywnej biblioteki interfejsowej, oraz zostay ju przeniesione na wszystkie najwaniejsze
platformy. Do tego typu aplikacji nale procesory tekstu, edytory zdj i przegldarki inter-
netowe. Wikszo z nich zostaa napisana w jzykach C i C++, a ponowne napisanie ich
w Javie nie przyniosoby uytkownikom adnych korzyci.
Jzyk C# przej wiele dobrych pomysw od Javy, jak czysto jzyka programowania,
maszyna wirtualna czy automatyczne usuwanie nieuytkw. Jednak z niewiadomych przy-
czyn wielu dobrych rzeczy w tym jzyku brakuje, zwaszcza zabezpiecze i niezalenoci
od platformy. Dla tych, ktrzy s zwizani z systemem Windows, jzyk C# wydaje si dobrym
wyborem. Sdzc jednak po ogoszeniach dotyczcych oferowanej pracy, Java nadal stanowi
wybr wikszoci deweloperw.
Po utworzeniu Javy firma Sun Microsystems udzielaa darmowych licencji na Jav dystry-
butorom i uytkownikom kocowym. Mimo e firma ta sprawowaa pen kontrol nad Jav,
w proces tworzenia nowych wersji jzyka i projektowania nowych bibliotek zostao zaan-
gaowanych wiele firm. Kod rdowy maszyny wirtualnej i bibliotek by zawsze oglno-
dostpny, ale tylko do wgldu. Nie mona go byo modyfikowa ani ponownie rozdziela.
Do tej pory Java bya zamknita, ale dobrze si sprawowaa.
Sytuacja ulega radykalnej zmianie w 2007 roku, kiedy firma Sun ogosia, e przysze wersje
Javy bd dostpne na licencji GPL, tej samej otwartej licencji, na ktrej dostpny jest system
Linux. Firma Oracle zobowizaa si pozostawi Jav otwart. Jest tylko jedna rysa na tej
powierzchni patenty. Na mocy licencji GPL kady moe uywa Javy i j modyfikowa, ale
dotyczy to tylko zastosowa desktopowych i serwerowych. Jeli kto chce uywa Javy w uka-
dach wbudowanych, musi mie inn licencj, za ktr najpewniej bdzie musia zapaci. Jednak
patenty te w cigu najbliszych kilku lat wygasn i wwczas Java bdzie cakowicie darmowa.
Java jest jzykiem interpretowanym, a wic jest zbyt powolna do powanych zastosowa.
Na pocztku Java bya interpretowana. Obecnie poza platformami skali mikro (jak telefony
komrkowe) maszyna wirtualna Javy wykorzystuje kompilator czasu rzeczywistego. Naj-
czciej uywane czci kodu dziaaj tak szybko, jakby byy napisane w C++, a w niekt-
rych przypadkach nawet szybciej.
Java ma pewien narzut w stosunku do C++. Uruchamianie maszyny wirtualnej zajmuje sporo
czasu, poza tym GUI w Javie s wolniejsze od ich natywnych odpowiednikw, poniewa
zostay przystosowane do pracy na rnych platformach.
Przez wiele lat ludzie skaryli si, e Java jest powolna. Jednak dzisiejsze komputery s duo
szybsze od tych, ktre byy dostpne w czasach, gdy zaczto si na to skary. Powolny
program w Javie i tak dziaa nieco szybciej ni niewiarygodnie szybkie programy napisane
kilka lat temu w C++. Obecnie te skargi brzmi jak echo dawnych czasw, a niektrzy zaczli
dla odmiany narzeka na to, e interfejsy uytkownika w Javie s brzydsze ni wolniejsze.
Na pocztku istnienia Javy opublikowano kilka raportw opisujcych bdy w systemie zabez-
piecze Javy. Wikszo z nich dotyczyo implementacji Javy w okrelonej przegldarce.
Badacze potraktowali zadanie znalezienia wyrw w murze obronnym Javy i zamania siy
oraz wyrafinowania modelu zabezpiecze apletw jako wyzwanie. Znalezione przez nich
techniczne usterki zostay szybko naprawione i wedug naszej wiedzy adne rzeczywiste
systemy nie zostay jeszcze zamane. Spjrzmy na to z innej perspektywy w systemie
Windows miliony wirusw atakujcych pliki wykonywalne i makra programu Word spo-
wodoway bardzo duo szkd, ale wywoay niewiele krytyki na temat saboci atakowanej
platformy. Take mechanizm ActiveX w przegldarce Internet Explorer moe by dobr
poywk dla naduy, ale jest to tak oczywiste, e z nudw niewielu badaczy publikuje
swoje odkrycia na ten temat.
Po pierwszym wydaniu Javy niektrzy ludzie gotowi byliby postawi due pienidze, e tak
si stanie. Od pierwszego wydania tej ksiki utrzymujemy, e twierdzenie, i uytkownicy
domowi zechc zastpi wszechstronne komputery ograniczonymi urzdzeniami pozbawio-
nymi pamici, jest absurdalne. Wyposaony w Jav komputer sieciowy mgby by praw-
dopodobnym rozwizaniem umoliwiajcym wdroenie strategii jednokrotnego ustawienia
opcji konfiguracyjnych bez potrzeby pniejszego wracania do nich (ang. zero administra-
tion initiative). Umoliwioby to zmniejszenie kosztw ponoszonych na utrzymanie kom-
puterw w firmach, ale jak na razie nie wida wielkiego ruchu w tym kierunku. W aktualnie
dostpnych tabletach Java nie jest wykorzystywana.
36 Java. Podstawy
2
rodowisko
programistyczne Javy
W tym rozdziale:
Instalacja oprogramowania Java Development Kit
Wybr rodowiska programistycznego
Korzystanie z narzdzi wiersza polece
Praca w zintegrowanym rodowisku programistycznym
Uruchamianie aplikacji graficznej
Budowa i uruchamianie apletw
W tym rozdziale nauczysz si instalowa oprogramowanie Java Development Kit (JDK) oraz
kompilowa i uruchamia rne typy programw: programy konsolowe, aplikacje graficzne
i aplety. Narzdzia JDK s uruchamiane za pomoc polece wpisywanych w oknie interpretera
polece. Wielu programistw woli jednak wygod pracy w zintegrowanym rodowisku pro-
gramistycznym. Opisalimy jedno dostpne bezpatnie rodowisko, w ktrym mona kompi-
lowa i uruchamia programy napisane w Javie. Mimo niewtpliwych zalet, takich jak
atwo nauki, takie rodowiska pochaniaj bardzo duo zasobw i bywaj nieporczne przy
pisaniu niewielkich aplikacji. Prezentujemy zatem kompromisowe rozwizanie w postaci
edytora tekstowego, ktry umoliwia uruchamianie kompilatora Javy i programw napisa-
nych w tym jzyku. Jeli opanujesz techniki opisywane w tym rozdziale i wybierzesz odpo-
wiednie dla siebie narzdzia programistyczne, moesz przej do rozdziau 3., od ktrego
zaczyna si opis jzyka programowania Java.
38 Java. Podstawy
Znamy ju skrt JDK oznaczajcy Java Development Kit. eby nie byo za atwo, informu-
jemy, e wersje od 1.2 do 1.4 tego pakietu miay nazw Java SDK (ang. Software Deve-
lopment Kit). Wci mona znale odwoania do tej starej nazwy. Jest te Java Runtime
Environment (JRE), czyli oprogramowanie zawierajce maszyn wirtualn bez kompilatora.
Jako programici nie jestemy tym zainteresowani. Ten program jest przeznaczony dla uyt-
kownikw kocowych, ktrym kompilator nie jest potrzebny.
Kolej na wszdobylski termin Java SE. Jest to Java Standard Edition, w odrnieniu od
Java EE (ang. Enterprise Edition) i Java ME (ang. Micro Edition).
Rozdzia 2. rodowisko programistyczne Javy 39
Czasami mona te spotka termin Java 2, ktry zosta ukuty w 1998 roku przez dzia mar-
ketingu w firmie Sun. Uwaano, e zwikszenie numeru wersji o uamek nie oddaje w peni
postpu, jakiego dokonano w JDK 1.2. Jednak jako e pniej zmieniono zdanie zde-
cydowano si zachowa numer 1.2. Kolejne wydania miay numery 1.3, 1.4 i 5.0. Zmieniono
jednak nazw platformy z Java na Java 2. W ten sposb powsta pakiet Java 2 Standard
Edition Software Development Kit Version 5.0, czyli J2SE SDK 5.0.
Inynierowie mieli problemy z poapaniem si w tych nazwach, ale na szczcie w 2006 roku
zwyciy rozsdek. Bezuyteczny czon Java 2 zosta usunity, a aktualna wersja Java Stan-
dard Edition zostaa nazwana Java SE 6. Nadal mona sporadycznie spotka odwoania do
wersji 1.5 i 1.6, ale s one synonimami wersji 5 i 6.
Czasami firma Oracle udostpnia paczki zawierajce zarwno pakiet Java Development Kit,
jak i zintegrowane rodowisko programistyczne. Jego nazwy kilkakrotnie si zmieniay; do tej
pory mona si byo spotka z Forte, Sun ONE Studio, Sun Java Studio i NetBeans. Trudno
zgadn, jak nazw nadadz mu nastpnym razem nadgorliwcy z dziau marketingu. Na razie
zalecamy wic zainstalowanie jedynie pakietu JDK. Jeli zdecydujesz si pniej na uywanie
rodowiska firmy Sun, pobierz je ze strony http://netbeans.org.
Sowo jdk naley zastpi ciek do katalogu instalacyjnego Javy, np. c:\jdk1.7.0_02.
Jeli zainstalowae Jav w folderze Program Files, ca ciek wpisz
w cudzysowie: "c:\Program Files\jdk1.7.0_02\bin";inne wpisy.
Zapisz ustawienia. Kade nowe okno konsoli bdzie wykorzystywa prawidow
ciek.
Oto jak mona sprawdzi, czy powysze czynnoci zostay wykonane prawidowo: otwrz
okno konsoli i wpisz ponisze polecenie:
javac -version
Jeli zamiast tego ukae si komunikat typu javac: polecenie nie zostao znalezione lub
Nazwa nie jest rozpoznawana jako polecenie wewntrzne lub zewntrzne, program wyko-
nywalny lub plik wsadowy, trzeba wrci do pocztku i dokadnie sprawdzi swoj instalacj.
Aby otworzy okno konsoli w systemie Windows, naley postpowa zgodnie z na-
stpujcymi wskazwkami: w systemie Windows XP kliknij opcj Uruchom w menu
Start i wpisz polecenie cmd. W systemach Windows Vista i 7 wystarczy wpisa cmd w polu
Rozpocznij wyszukiwanie w menu Start. Nastpnie nacinij klawisz Enter.
Osobom, ktre nigdy nie miay do czynienia z oknem konsoli, zalecamy zapoznanie si
z kursem objaniajcym podstawy korzystania z tego narzdzia dostpnym pod adresem
http://www.horstmann.com/bigj/help/windows/tutorial.html.
5. Wykonaj polecenie:
jar xvf ../src.zip
Plik src.zip zawiera kod rdowy wszystkich bibliotek publicznych. Wicej rde
(dla kompilatora, maszyny wirtualnej, metod rodzimych i prywatnych klas pomocni-
czych) mona znale na stronie http://jdk7.java.net.
Dla uczcych si Javy najwaniejsze s katalogi docs i src. Katalog docs zawiera dokumen-
tacj biblioteki Javy w formacie HTML. Mona j przeglda za pomoc dowolnej przegl-
darki internetowej, jak chociaby Firefox.
Katalog src zawiera kod rdowy publicznych bibliotek Javy. W miar zdobywania wiedzy
na temat Javy by moe bdziesz chcia uzyska wicej informacji, ni dostarcza niniejsza
ksika i dokumentacja. W takiej sytuacji najlepszym miejscem do rozpoczcia poszukiwa
jest kod rdowy Javy. wiadomo, e zawsze mona zajrze do kodu rdowego, aby
sprawdzi, jak faktycznie dziaa dana funkcja biblioteczna, ma w duym stopniu dziaanie
uspokajajce. Jeli chcemy na przykad zbada wntrze klasy System, moemy zajrze do
pliku src/java/Lang/System.java.
ostatnich lat rodowiska te stay si tak wygodne i wszechstronne, e nie ma sensu mczy
si bez nich. Dwa z nich zasuguj na wyrnienie: Eclipse i NetBeans. Oba s dostpne bez-
patnie. W tym rozdziale opisujemy, jak rozpocz prac w rodowisku Eclipse, jako e jest
ono nieco lepsze od NetBeans, cho to drugie szybko dogania swojego konkurenta. Oczy-
wicie do pracy z t ksik mona uy take dowolnego innego rodowiska.
Kiedy do pisania prostych programw polecalimy edytory tekstowe, takie jak Emacs, JEdit
czy TextPad. Ze wzgldu na fakt, e zintegrowane rodowiska s ju bardzo szybkie i wy-
godne, teraz zalecamy uywanie wanie nich.
Podsumowujc, naszym zdaniem kady powinien zna podstawy obsugi narzdzi JDK, a po
ich opanowaniu przej na zintegrowane rodowisko programistyczne.
Rysunek 2.4.
Kompilacja
i uruchamianie
programu
Welcome.java
Program Welcome jest niezwykle prosty. Wywietla tylko wiadomo w konsoli. Jego kod r-
dowy przedstawia listing 2.1 (sposb dziaania tego kodu opisujemy w nastpnym rozdziale).
Dostpnych jest jeszcze kilka innych IDE, ale Eclipse cieszy si obecnie najwiksz popu-
larnoci. Oto podstawowe kroki pocztkujcego:
1. Po uruchomieniu programu Eclipse kliknij opcj File/New Project.
2. W oknie kreatora wybierz pozycj Java Project (zobacz rysunek 2.5). Te zrzuty
zostay zrobione w wersji 3.3 Eclipse. Nie jest to jednak wymg i moesz uywa
innej wersji tego rodowiska.
Rysunek 2.5.
Okno dialogowe
New Project
w Eclipse
3. Kliknij przycisk Next. Wprowad nazw projektu Welcome i wpisz pen ciek
katalogu, ktry zawiera plik Welcome.java (zobacz rysunek 2.6).
4. Zaznacz opcj Create project from existing source (utwrz projekt z istniejcego
rda).
5. Kliknij przycisk Finish (zakocz), aby utworzy projekt.
6. Aby otworzy projekt, kliknij znajdujcy si w lewym panelu obok okna projektu
symbol trjkta. Nastpnie kliknij symbol trjkta znajdujcy si obok napisu
Default package (domylny pakiet). Kliknij dwukrotnie plik o nazwie Welcome.java.
Powinno si pojawi okno z kodem rdowym programu (zobacz rysunek 2.7).
48 Java. Podstawy
Rysunek 2.6.
Konfiguracja
projektu
w Eclipse
Rysunek 2.7.
Edycja kodu
rdowego
w Eclipse
Rysunek 2.8.
Uruchamianie
programu
w rodowisku
Eclipse
Bdy w Eclipse s czsto oznaczane ikon arwki. Aby przejrze list sugero-
wanych rozwiza problemu, naley t ikon klikn.
Rysunek 2.9.
Komunikaty
o bdach
w Eclipse
Nastpnie kliknij opcj Plik/Otwrz, aby otworzy plik (kilka plikw do otwarcia znajduje
si w katalogu z klas). Aby zamkn program, naley klikn pozycj Zakocz w menu
Plik albo krzyyk w prawym grnym rogu okna przegldarki.
Rzumy okiem na kod rdowy tego programu. Jest on znacznie duszy od poprzedniego,
ale biorc pod uwag to, ile wierszy kodu trzeba by byo napisa w jzykach C i C++, aby
Rozdzia 2. rodowisko programistyczne Javy 51
Rysunek 2.10.
Dziaanie
aplikacji
ImageViewer
stworzy podobn aplikacj, trzeba przyzna, e nie jest zbyt skomplikowany. Oczywicie
atwo taki program napisa (a raczej przecign i upuci) w Visual Basicu. JDK nie umoli-
wia wizualnego budowania interfejsw, a wic cay kod widoczny na listingu 2.2 trzeba napi-
sa rcznie. Pisaniem takich programw graficznych zajmiemy si w rozdziaach od 7. do 9.
/**
* Program do przegldania obrazw.
* @version 1.22 2007-05-21
* @author Cay Horstmann
*/
public class ImageViewer
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ImageViewerFrame();
frame.setTitle("ImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka z etykiet wywietlajca obraz.
*/
class ImageViewerFrame extends JFrame
{
52 Java. Podstawy
// Pasek menu.
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
}
Rozdzia 2. rodowisko programistyczne Javy 53
Rysunek 2.11.
Aplet
WelcomeApplet
w oknie
przegldarki
apletw
Jednak tym razem nie uruchamiamy programu java, tylko program appletviewer. Jest to spe-
cjalne narzdzie dostpne w pakiecie JDK, ktre umoliwia szybkie przetestowanie apletu.
Program ten przyjmuje na wejciu pliki HTML, a nie pliki klas Javy. Zawarto pliku Wel-
comeApplet.html przedstawia listing 2.3.
</applet>
<hr/>
<p><a href="WelcomeApplet.java">rdo</a></p>
</body>
</html>
Osoby znajce HTML rozpoznaj kilka standardowych elementw tego jzyka oraz znacznik
applet, nakazujcy przegldarce apletw, aby zaadowaa aplet, ktrego kod znajduje si w pliku
WelcomeApplet.class. Przegldarka apletw bierze pod uwag tylko znacznik applet.
Rysunek 2.12.
Dziaanie apletu
WelcomeApplet
w przegldarce
internetowej
Jak wida, aplikacja ta jest zdolna do interakcji z internetem. Kliknicie przycisku Cay Horst-
mann powoduje przejcie do strony internetowej Caya Horstmanna. Kliknicie przycisku Gary
Cornell powoduje wywietlenie okna wysyania poczty e-mail z adresem Garyego Cornella
wstawionym w polu adresata.
Zauwa, e aden z tych przyciskw nie dziaa w przegldarce apletw. Nie ma ona mo-
liwoci wysyania poczty e-mail ani wywietlania stron internetowych, wic ignoruje nasze
Rozdzia 2. rodowisko programistyczne Javy 55
Kod apletu przedstawia listing 2.4. Na razie wystarczy rzuci tylko na niego okiem. Do pisania
apletw wrcimy w rozdziale 10.
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import javax.swing.*;
/**
* Aplet ten wywietla powitanie autorw.
* @version 1.22 2007-04-08
* @author Cay Horstmann
*/
public class WelcomeApplet extends JApplet
{
public void init()
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
setLayout(new BorderLayout());
add(panel, BorderLayout.SOUTH);
}
});
}
56 Java. Podstawy
W tym rozdziale:
Prosty program w Javie
Komentarze
Typy danych
Zmienne
Operatory
acuchy
Wejcie i wyjcie
Kontrola przepywu sterowania
Wielkie liczby
Tablice
Do tego rozdziau naley przej dopiero wtedy, gdy z powodzeniem zainstalowao si pakiet
JDK i uruchomio przykadowe programy z rozdziau 2. Poniewa czas zacz programo-
wanie, w rozdziale tym zapoznasz si z podstawowymi pojciami programistycznymi Javy,
takimi jak typy danych, instrukcje warunkowe i ptle.
Niestety, napisanie w Javie programu z graficznym interfejsem uytkownika nie jest atwe
wymaga duej wiedzy na temat sposobw tworzenia okien, dodawania do nich pl tekstowych,
przyciskw, ktre reaguj na zawarto tych pl itd. Jako e opis technik pisania programw
GUI w Javie znacznie wykracza poza nasz cel przedstawienia podstaw programowania w tym
jzyku, przykadowe programy w tym rozdziale s bardzo proste. Komunikuj si za pored-
nictwem okna konsoli, a ich przeznaczeniem jest tylko ilustracja omawianych poj.
58 Java. Podstawy
Dowiadczeni programici jzyka C++ mog tylko przejrze ten rozdzia, koncentrujc si na
ramkach opisujcych rnice pomidzy Jav a C++. Programici innych jzykw, jak Visual
Basic, bd zna wikszo omawianych poj, ale odkryj, e skadnia Javy jest cakiem inna
od znanych im jzykw. Te osoby powinny bardzo uwanie przeczyta ten rozdzia.
Warto powici troch czasu i nauczy si tego fragmentu na pami, poniewa wszystkie
aplikacje s oparte na tym schemacie. Przede wszystkim w Javie wielko liter ma znaczenie.
Jeli w programie bdzie literwka (jak np. sowo Main zamiast main), to program nie zadziaa.
Przestudiujemy powyszy kod wiersz po wierszu. Sowo kluczowe public nosi nazw mody-
fikatora dostpu (ang. access modifier). Okrela ono, jaki rodzaj dostpu do tego kodu maj
inne czci programu. Wicej informacji na temat modyfikatorw dostpu zawarlimy w roz-
dziale 5. Sowo kluczowe class przypomina, e wszystko w Javie naley do jakiej klasy.
Poniewa klasami bardziej szczegowo zajmujemy si w kolejnym rozdziale, na razie
bdziemy je traktowa jako zbiory mechanizmw programu, ktre s odpowiedzialne za
jego dziaanie. Jak pisalimy w rozdziale 1., klasy to bloki, z ktrych skadaj si wszystkie
aplikacje i aplety Javy. Wszystko w programie w Javie musi si znajdowa wewntrz
jakiej klasy.
Po sowie kluczowym class znajduje si nazwa klasy. Reguy dotyczce tworzenia nazw
klas w Javie s dosy liberalne. Nazwa klasy musi si zaczyna od litery, po ktrej moe
znajdowa si kombinacja dowolnych znakw i cyfr. Nie ma w zasadzie ogranicze, jeli
chodzi o dugo. Nie mona stosowa sw zarezerwowanych Javy (np. public lub class)
lista wszystkich sw zarezerwowanych znajduje si w dodatku.
Plik zawierajcy kod rdowy musi mie tak sam nazw jak klasa publiczna oraz rozszerze-
nie .java. W zwizku z tym nasz przykadowy kod powinien zosta zapisany w pliku o nazwie
FirstSample.java (przypominam, e wielko liter ma znaczenie take tutaj nie mona
napisa firstsample.java).
Rozdzia 3. Podstawowe elementy jzyka Java 59
Jeli plik ma prawidow nazw i nie ma adnych literwek w kodzie rdowym, w wyniku
jego kompilacji powstanie plik zawierajcy kod bajtowy tej klasy. Kompilator automatycznie
nada skompilowanemu plikowi nazw FirstSample.class i zapisze go w tym samym katalogu,
w ktrym znajduje si plik rdowy. Program uruchamiamy za pomoc nastpujcego pole-
cenia (nie zapomnij o pominiciu rozszerzenia .class):
java FirstSample
Po uruchomieniu program ten wywietla w konsoli acuch Nie powiemy Witaj, wiecie!.
Polecenie:
java NazwaKlasy
Ta historia pozwala zwrci uwag na kilka rzeczy. Z jednej strony rozczarowuje nas sytuacja, e
osoby odpowiadajce za jako s przepracowane i nie zawsze dysponuj wystarczajc wie-
dz specjalistyczn z zakresu najbardziej zaawansowanych zagadnie zwizanych z Jav.
Przez to nie zawsze podejmuj trafne decyzje. Z drugiej strony trzeba zauway, e firma Sun
zamieszcza raporty o bdach na stronie internetowej, aby kady mg je zweryfikowa. Taki
spis bdw jest bardzo wartociowym rdem wiedzy dla programistw. Mona nawet go-
sowa na swj ulubiony bd. Bdy o najwikszej liczbie gosw maj najwiksz szans
poprawienia w kolejnej wersji pakietu JDK.
Na razie nie bdziemy si zajmowa znaczeniem sw static void traktuj je jako co, czego
potrzebujesz do kompilacji programu w Javie. Po rozdziale czwartym przestanie to by tajem-
nic. Teraz trzeba tylko zapamita, e kady program napisany w Javie musi zawiera metod
main zadeklarowan w nastpujcy sposb:
public class NazwaKlasy
{
public static void main(String[] args)
{
instrukcje programu
}
}
Programici jzyka C++ doskonale znaj pojcie klasa. Klasy w Javie s pod
wieloma wzgldami podobne do tych w C++, ale jest te kilka rnic, o ktrych
nie mona zapomina. Na przykad w Javie wszystkie funkcje s metodami jakiej klasy
(w standardowej terminologii s one nazywane metodami, a nie funkcjami skadowymi).
W zwizku z tym w Javie konieczna jest obecno klasy zawierajcej metod main. Pro-
gramici C++ pewnie znaj te statyczne funkcje skadowe. S to funkcje zdefiniowane
wewntrz klasy, ktre nie wykonuj adnych dziaa na obiektach. Metoda main w Javie
jest zawsze statyczna. W kocu sowo kluczowe void, podobnie jak w C i C++, oznacza,
e metoda nie zwraca wartoci. W przeciwiestwie do jzyka C i C++ metoda main w Javie
nie zwraca adnego kodu wyjcia (ang. exit code) do systemu operacyjnego. Jeli metoda
main zakoczy dziaanie w normalny sposb, program ma kod wyjcia 0, ktry oznacza
pomylne zakoczenie. Aby zakoczy dziaanie programu innym kodem wyjcia, naley
uy metody System.exit.
Klamry oznaczaj pocztek i koniec ciaa metody. Ta metoda zawiera tylko jedn instrukcj.
Podobnie jak w wikszoci jzykw programowania, instrukcje Javy mona traktowa jako
zdania tego jzyka. Kada instrukcja musi by zakoczona rednikiem. Przede wszystkim
naley pamita, e znak powrotu karetki nie oznacza koca instrukcji, dziki czemu mog
one obejmowa nawet kilka wierszy.
W treci metody main znajduje si instrukcja wysyajca jeden wiersz tekstu do konsoli.
W tym przypadku uylimy obiektu System.out i wywoalimy na jego rzecz metod println.
Zwr uwag na kropki zastosowane w wywoaniu metody. Oglna skadnia stosowana
w Javie do wywoania jej odpowiednikw funkcji jest nastpujca:
obiekt.metoda(parametry)
Rozdzia 3. Podstawowe elementy jzyka Java 61
Metody w Javie, podobnie jak funkcje w innych jzykach programowania, przyjmuj zero,
jeden lub wicej parametrw (czsto nazywanych argumentami). Nawet jeli metoda nie
przyjmuje adnych parametrw, nie mona pomin stojcych po jej nazwie nawiasw. Na
przykad metoda println bez adnych argumentw drukuje pusty wiersz. Wywouje si j
nastpujco:
System.out.println();
Na rzecz obiektu System.out mona take wywoywa metod print, ktra nie
dodaje do danych wyjciowych znaku nowego wiersza. Na przykad wywoanie
System.out.print("Witaj") drukuje napis Witaj bez znaku nowego wiersza. Kolejne dane
zostan umieszczone bezporednio po sowie Witaj.
3.2. Komentarze
Komentarze w Javie, podobnie jak w wikszoci jzykw programowania, nie s uwzgld-
niane w programie wykonywalnym. Mona zatem stosowa je w dowolnej iloci bez obawy,
e nadmiernie zwiksz rozmiary kodu. W Javie s trzy rodzaje komentarzy. Najczciej
stosowana metoda polega na uyciu znakw //. Ten rodzaj komentarza obejmuje obszar od
znakw // do koca wiersza, w ktrym si znajduj.
System.out.println("Nie powiemy Witaj, wiecie!"); // Czy to nie sodkie?
Dusze komentarze mona tworzy poprzez zastosowanie znakw // w wielu wierszach lub
uycie komentarza w stylu /* */. W ten sposb w komentarzu mona uj cay blok treci
programu.
Do wikszoci zastosowa najlepiej nadaje si typ int. Aby zapisa liczb mieszkacw
naszej planety, trzeba uy typu long. Typy byte i short s uywane do specjalnych zada,
jak niskopoziomowa praca nad plikami lub due tablice, kiedy pami jest na wag zota.
Zakres wartoci typw cakowitych nie zaley od urzdzenia, na ktrym uruchamiany jest
kod Javy. Eliminuje to gwny problem programisty, ktry chce przenie swj program
Rozdzia 3. Podstawowe elementy jzyka Java 63
z jednej platformy na inn lub nawet z jednego systemu operacyjnego do innego na tej samej
platformie. W odrnieniu od Javy, jzyki C i C++ uywaj najbardziej efektywnego typu
cakowitego dla kadego procesora. W wyniku tego program prawidowo dziaajcy na pro-
cesorze 32-bitowym moe powodowa bd przekroczenia zakresu liczby cakowitej na pro-
cesorze 16-bitowym. Jako e programy w Javie musz dziaa prawidowo na wszystkich
urzdzeniach, zakresy wartoci rnych typw s stae.
Due liczby cakowite (typu long) s opatrzone modyfikatorem L lub l (na przykad
4000000000L). Liczby w formacie szesnastkowym maj przedrostek 0x (na przykad 0xCAFE).
Liczby w formacie semkowym poprzedza przedrostek 0. Na przykad liczba 010 w zapisie
semkowym to 8 w zapisie dziesitnym. Oczywicie zapis ten moe wprowadza w bd,
w zwizku z czym odradzamy jego stosowanie.
W jzykach C i C++ typ int to liczba cakowita, ktrej rozmiar zaley od urzdze-
nia docelowego. W procesorach 16-bitowych, jak 8086, typ int zajmuje 2 bajty
pamici. W procesorach 32-bitowych, jak Sun SPARC, s to wartoci czterobajtowe.
W przypadku procesorw Intel Pentium rozmiar typu int zaley od systemu operacyjnego:
w DOS-ie i Windows 3.1 typ int zajmuje 2 bajty pamici. W programach dla systemu Win-
dows dziaajcych w trybie 32-bitowym typ int zajmuje 4 bajty. W Javie wszystkie typy
numeryczne s niezalene od platformy.
Zauwa, e w Javie nie ma typu unsigned.
Nazwa double (podwjny) wynika z tego, e typ ten ma dwa razy wiksz precyzj ni typ
float (czasami liczby te nazywa si liczbami o podwjnej precyzji). W wikszoci przy-
padkw do reprezentacji liczb zmiennoprzecinkowych wybierany jest typ double. Ograniczona
precyzja typu float czsto okazuje si niewystarczajca. Siedem znaczcych (dziesitnych)
cyfr moe wystarczy do precyzyjnego przedstawienia naszej pensji w zotwkach i gro-
szach, ale moe by ju to za mao precyzyjne do przechowywania liczby okrelajcej zarobki
64 Java. Podstawy
naszego szefa. W zwizku z tym powodw do stosowania typu float jest niewiele; moe to
by sytuacja, w ktrej zaley nam na nieznacznym zwikszeniu szybkoci poprzez zastoso-
wanie liczb o pojedynczej precyzji lub kiedy chcemy przechowywa bardzo du ich ilo.
Liczby typu float maj przyrostek F lub f (na przykad 3.14F). Liczby zmiennoprzecinkowe
pozbawione tego przyrostka (na przykad 3.14) s zawsze traktowane jako typ double. Mona
te poda przyrostek D lub d (na przykad 3.14D).
Na przykad wynikiem dzielenia dodatniej liczby przez zero jest dodatnia nieskoczono.
Dziaanie dzielenia zero przez zero lub wycigania pierwiastka kwadratowego z liczby
ujemnej daje w wyniku NaN.
aby dowiedzie si, czy dany wynik jest rwny staej Double.NaN. Wszystkie tego typu
wartoci s rne. Mona za to uywa metody Double.isNaN:
if (Double.isNaN(x)) // Sprawdzenie, czy x jest nie liczb.
Poza symbolem zastpczym \u oznaczajcym zapis znaku w kodzie Unicode jest jeszcze
kilka innych symboli zastpczych umoliwiajcych zapisywanie rnych znakw specjal-
nych. Zestawienie tych znakw przedstawia tabela 3.3. Mona je stosowa zarwno w sta-
ych znakowych, jak i w acuchach, np. 'u\2122' albo "Witaj\n". Symbol zastpczy \u
jest jedynym symbolem zastpczym, ktrego mona uywa take poza cudzysowami ota-
czajcymi znaki i acuchy. Na przykad zapis:
public static void main(String\u005B\u005D args)
\t Tabulacja \u0009
Aby w peni zrozumie typ char, trzeba pozna system kodowania znakw Unicode. Uni-
code opracowano w celu pozbycia si ogranicze tradycyjnych systemw kodowania. Przed
powstaniem systemu Unicode istniao wiele rnych standardw: ASCII w USA, ISO 8859-1
dla jzykw krajw Europy Zachodniej, ISO-8859-2 dla jzykw rodkowo- i wschodnio-
europejskich (w tym polskiego), KOI-8 dla jzyka rosyjskiego, GB18030 i BIG-5 dla jzyka
chiskiego itd. Powoduje to dwa problemy: jeden kod moe oznacza rne znaki w r-
nych systemach kodowania, a poza tym kody znakw w jzykach o duej liczbie znakw
maj rne rozmiary niektre czsto uywane znaki zajmuj jeden bajt, a inne potrze-
buj dwch bajtw.
Niestety z czasem nastpio to, co byo nieuchronne. Unicode przekroczy liczb 65 536
znakw, gwnie z powodu dodania bardzo duych zbiorw ideogramw uywanych w jzy-
kach chiskim, japoskim i koreaskim. Obecnie 16-bitowy typ char nie wystarcza do opisu
wszystkich znakw Unicode.
Aby wyjani, jak ten problem zosta rozwizany w Javie, zaczynajc od Java SE 5.0, musimy
wprowadzi nieco nowej terminologii. Wsprzdna kodowa znaku (ang. code point) to
warto zwizana ze znakiem w systemie kodowania. W standardzie Unicode wsprzdne
kodowe znakw s zapisywane w notacji szesnastkowej i s poprzedzane acuchem U+, np.
wsprzdna kodowa litery A to U+0041. Wsprzdne kodowe znakw systemu Unicode s
pogrupowane w 17 przestrzeniach numeracyjnych (ang. code planes). Pierwsza z nich,
nazywana podstawow przestrzeni wielojzyczn (ang. Basic Multilingual Plane BMP),
zawiera klasyczne znaki Unicode o wsprzdnych kodowych z przedziau od U+0000 do
U+FFFF. Pozostae szesnacie przestrzeni o wsprzdnych kodowych znakw z przedziau
od U+10000 do U+10FFFF zawiera znaki dodatkowe (ang. supplementary characters).
3.4. Zmienne
W Javie kada zmienna musi mie okrelony typ. Deklaracja zmiennej polega na napisaniu
nazwy typu, a po nim nazwy zmiennej. Oto kilka przykadw deklaracji zmiennych:
Rozdzia 3. Podstawowe elementy jzyka Java 67
W jzyku C++ zamiast wartoci logicznych mona stosowa liczby, a nawet wska-
niki. Warto 0 jest odpowiednikiem wartoci logicznej false, a warto rna od
zera odpowiada wartoci true. W Javie tak nie jest. Dziki temu programici Javy maj
ochron przed popenieniem bdu:
if (x = 0) // ups miaem na myli x == 0
W C++ test ten przejdzie kompilacj i bdzie mona go uruchomi, a jego wartoci
zawsze bdzie false. W Javie testu tego nie bdzie mona skompilowa, poniewa wy-
raenia cakowitoliczbowego x = 0 nie mona przekonwertowa na warto logiczn.
double salary;
int vacationDays;
long earthPopulation;
boolean done;
Nazwa zmiennej musi si zaczyna liter oraz skada si z liter i cyfr. Zwrmy uwag, e
pojcia litera i cyfra w Javie maj znacznie szersze znaczenie ni w wikszoci innych
jzykw. Zgodnie z definicj litera to jeden ze znakw 'A' 'Z', 'a' 'z', '_' lub kady
znak Unicode bdcy liter jakiego jzyka. Na przykad polscy programici mog w nazwach
zmiennych uywa liter z ogonkami, takich jak . Grek moe uy litery . Podobnie cyfry
nale do zbioru '0' '9' oraz s nimi wszystkie znaki Unicode, ktre oznaczaj cyfr
w jakim jzyku. W nazwach zmiennych nie mona stosowa symboli typu '+' czy ani spacji.
Wszystkie znaki uyte w nazwie zmiennej oraz ich wielko maj znaczenie. Dugo na-
zwy zmiennej jest w zasadzie nieograniczona.
Mimo e znak $ jest w Javie traktowany jak zwyka litera, nie naley go uywa
w swoim kodzie. Jest stosowany w nazwach generowanych przez kompilator i inne
narzdzia Javy.
Dodatkowo nazwa zmiennej w Javie nie moe by taka sama jak sowo zarezerwowane (list
sw zarezerwowanych zawiera dodatek).
Nie polecamy jednak takiego stylu pisania kodu. Dziki deklarowaniu kadej zmiennej oddziel-
nie programy s atwiejsze do czytania.
68 Java. Podstawy
3.4.2. Stae
Stae oznaczamy sowem kluczowym final. Na przykad:
Rozdzia 3. Podstawowe elementy jzyka Java 69
Sowo kluczowe final oznacza, e mona tylko jeden raz przypisa warto i nie bdzie mona
ju jej zmieni w programie. Nazwy staych piszemy zwyczajowo samymi wielkimi literami.
W Javie chyba najczciej uywa si staych, ktre s dostpne dla wielu metod jednej klasy.
S to tak zwane stae klasowe. Tego typu stae definiujemy za pomoc sowa kluczowego
static final. Oto przykad uycia takiej staej:
public class Constants2
{
public static final double CM_PER_INCH = 2.54;
Sowo const jest sowem zarezerwowanym w Javie, ale obecnie nie jest do niczego
uywane. Do deklaracji staych trzeba uywa sowa kluczowego final.
3.5. Operatory
Znane wszystkim operatory arytmetyczne +, , * i / su w Javie odpowiednio do wykony-
wania operacji dodawania, odejmowania, mnoenia i dzielenia. Operator / oznacza dzielenie
cakowitoliczbowe, jeli obie liczby s typu cakowitoliczbowego, oraz dzielenie zmienno-
przecinkowe w przeciwnym przypadku. Operatorem reszty z dzielenia (dzielenia modulo)
jest symbol %. Na przykad wynikiem dziaania 15/2 jest 7, a 15%2 jest 1, podczas gdy
15.0/2 = 7.5.
70 Java. Podstawy
Pamitajmy, e dzielenie cakowitoliczbowe przez zero powoduje wyjtek, podczas gdy wyni-
kiem dzielenia zmiennoprzecinkowego przez zero jest nieskoczono lub warto NaN.
Oglna zasada jest taka, e operator powinien si znajdowa po lewej stronie znaku rwnoci,
np. *= czy %=.
Jednym z gwnych celw, ktre postawili sobie projektanci Javy, jest przenono.
Wyniki oblicze powinny by takie same bez wzgldu na to, ktrej maszyny wirtual-
nej uyto. Uzyskanie takiej przenonoci jest zaskakujco trudne w przypadku dziaa
na liczbach zmiennoprzecinkowych. Typ double przechowuje dane liczbowe w 64 bitach
pamici, ale niektre procesory maj 80-bitowe rejestry liczb zmiennoprzecinkowych.
Rejestry te w swoich obliczeniach porednich stosuj zwikszon precyzj. Przyjrzyjmy
si na przykad poniszemu dziaaniu:
double w = x * y/z;
(Jak powszechnie wiadomo, nazwa jzyka C++ pochodzi od operatora inkrementacji, ktry jest
te winowajc powstania pierwszego dowcipu o tym jzyku. Przeciwnicy C++ zauwaaj,
e nawet nazwa tego jzyka jest bdna: Powinna brzmie ++C, poniewa jzyka tego
chcielibymy uywa tylko po wprowadzeniu do niego poprawek.)
Dodatkowo dostpne s operatory wikszoci (>), mniejszoci (<), mniejszy lub rwny (<=)
oraz wikszy lub rwny (>=).
72 Java. Podstawy
Operatorem koniunkcji logicznej w Javie, podobnie jak w C++, jest &&, a alternatywy logicz-
nej ||. Jak nietrudno si domyli, znajc operator !=, znak wykrzyknika (!) jest operatorem
negacji. Wartoci wyrae z uyciem operatorw && i || s obliczane metod na skrty.
Warto drugiego argumentu nie jest obliczana, jeli ostateczny rezultat wynika ju z pierw-
szego. Jeeli midzy dwoma wyraeniami postawimy operator &&:
wyraenie1 && wyraenie2
i warto logiczna pierwszego z nich okae si false, to warto caego wyraenia nie moe
by inna ni false. W zwizku z tym warto drugiego wyraenia nie jest obliczana. Mona
to wykorzysta do unikania bdw. Jeli na przykad warto zmiennej x w wyraeniu:
x != 0 && 1/x > x + y // Unikamy dzielenia przez zero.
jest rwna zero, druga jego cz nie bdzie obliczana. Zatem dziaanie 1/x nie zostanie
wykonane, jeli x = 0, dziki czemu nie wystpi bd dzielenia przez zero.
W Javie dostpny jest te czasami przydatny operator trjargumentowy w postaci ?:. War-
toci wyraenia:
warunek ? wyraenie1 : wyraenie2
jest wyraenie1, jeli warunek ma warto true, lub wyraenie2, jeli warunek ma warto false.
Na przykad wynikiem wyraenia:
x < y ? x : y
Operatory te dziaaj na bitach. Jeli na przykad zmienna n jest typu int, to wyraenie:
int fourthBitFromRight = (n & 8) / 8;
da wynik 1, jeli czwarty bit od prawej w binarnej reprezentacji wartoci zmiennej n jest
jedynk, lub 0 w przeciwnym razie. Dziki uyciu odpowiedniej potgi liczby 2 mona
zamaskowa wszystkie bity poza jednym.
Mona te uywa tak zwanych operatorw przesunicia, w postaci >> i <<, ktre przesu-
waj liczb o jeden bit w prawo lub w lewo. Czsto przydatne s przy tworzeniu cigw
bitw uywanych przy maskowaniu:
int fourthBitFromRight = (n & (1 << 3)) >> 3;
Midzy metodami println i sqrt jest pewna rnica. Pierwsza dziaa na obiekcie
System.out, ktry jest zdefiniowany w klasie System. Druga natomiast nie dziaa na
adnym obiekcie. Tego typu metody nosz nazw metod statycznych. Wicej na ich
temat dowiesz si w rozdziale 4.
W Javie nie ma operatora podnoszcego liczb do potgi. Do tego celu trzeba uy metody
pow dostpnej w klasie Math. Wyraenie:
double y = Math.pow(x, a);
ustawia warto zmiennej y na liczb x podniesion do potgi a (xa). Metoda pow przyjmuje
parametry typu double i zwraca wynik tego samego typu.
Math.tan
Math.atan
Math.atan2
a take funkcj wykadnicz i jej odwrotno, czyli logarytm naturalny, oraz logarytm
dziesitny:
Math.exp
Math.log
Math.log10
Na przykad:
System.out.println("Pierwiastek kwadratowy z \u03C0 wynosi " + sqrt(PI));
Importy statyczne opisujemy w rozdziale 4.
Sze typw konwersji (rysunek 3.1) niepowodujcych strat danych oznaczono strzakami
cigymi. Konwersje, ktre mog spowodowa utrat czci danych, oznaczono strzakami
przerywanymi. Na przykad dua liczba cakowita, jak 123 456 789, skada si z wikszej
liczby cyfr, ni moe si zmieci w typie float. Po konwersji tej liczby cakowitej na liczb
typu float stracimy nieco na precyzji:
int n = 123456789;
float f = n; // f ma warto 1.23456792E8
Rysunek 3.1.
Dozwolone
konwersje
pomidzy typami
liczbowymi
3.5.6. Rzutowanie
W poprzednim podrozdziale dowiedzielimy si, e wartoci typu int s w razie potrzeby
automatycznie konwertowane na typ double. S jednak sytuacje, w ktrych chcemy prze-
konwertowa typ double na typ int. W Javie moliwe s takie konwersje, ale oczywicie
mog one pociga za sob utrat informacji. Konwersje, w ktrych istnieje ryzyko utraty
informacji, nazywaj si rzutowaniem (ang. casting). Aby wykona rzutowanie, naley przed
nazw rzutowanej zmiennej postawi nazw typu docelowego w okrgych nawiasach. Na
przykad:
double x = 9.997;
int nx = (int) x;
W wyniku tego dziaania zmienna nx bdzie miaa warto 9, poniewa rzutowanie liczby
zmiennoprzecinkowej na cakowit powoduje usunicie czci uamkowej.
Teraz zmienna nx ma warto 10. Przy zaokrglaniu za pomoc metody round nadal konieczne
jest zastosowanie rzutowania, tutaj (int). Jest to spowodowane tym, e metoda round zwraca
warto typu long, a tego typu warto mona przypisa zmiennej typu int wycznie na
drodze jawnego rzutowania, poniewa istnieje ryzyko utraty danych.
76 Java. Podstawy
Wynikiem rzutowania na okrelony typ liczby, ktra nie mieci si w jego zakresie,
jest obcicie tej liczby i powstanie cakiem nowej wartoci. Na przykad rzutowanie
(byte) 300 da w wyniku liczb 44.
Operator Wizanie
[] . () (wywoanie metody) lewe
! ~++ -- + (jednoargumentowy) () (rzutowanie) new prawe
* / % lewe
+ - lewe
<< >> >>> lewe
< <= > >= instanceof lewe
== != lewe
& lewe
^ lewe
| lewe
&& lewe
|| lewe
?: prawe
= += -= *= /= %= &= /= ^= <<= >>= >>>= prawe
Rozdzia 3. Podstawowe elementy jzyka Java 77
Mona te definiowa wasne typy wyliczeniowe (ang. enumerated type). Typ wyliczeniowy
zawiera skoczon liczb nazwanych wartoci. Na przykad:
enum Rozmiar { MAY, REDNI, DUY, EKSTRA_DUY };
Zmienna typu Rozmiar moe przechowywa tylko jedn z wartoci wymienionych w deklaracji
typu lub specjaln warto null, ktra oznacza, e zmienna nie ma w ogle adnej wartoci.
3.6. acuchy
W zasadzie acuchy w Javie skadaj si z szeregu znakw Unicode. Na przykad acuch
"Java\u2122" skada si z piciu znakw Unicode: J, a, v, a i . W Javie nie ma wbudowa-
nego typu String. Zamiast tego standardowa biblioteka Javy zawiera predefiniowan klas
o takiej wanie nazwie. Kady acuch w cudzysowach jest obiektem klasy String:
String e = ""; // pusty acuch
String greeting = "Cze!";
78 Java. Podstawy
3.6.1. Podacuchy
Aby wydoby z acucha podacuch, naley uy metody substring klasy String. Na
przykad:
String greeting = "Cze!";
String s = greeting.substring(0, 3);
Drugi parametr metody substring okrela pooenie pierwszego znaku, ktrego nie chcemy
skopiowa. W powyszym przykadzie chcielimy skopiowa znaki na pozycjach 0, 1 i 2
(od pozycji 0 do 2 wcznie). Z punktu widzenia metody substring nasz zapis oznacza: od
pozycji zero wcznie do pozycji 3 z wyczeniem.
Sposb dziaania metody substring ma jedn zalet: atwo mona obliczy dugo pod-
acucha. acuch s.substring(a, b) ma dugo b - a. Na przykad acuch "Cze" ma
dugo 3 - 0 = 3.
3.6.2. Konkatenacja
W Javie, podobnie jak w wikszoci innych jzykw programowania, mona czy (konka-
tenowa) acuchy za pomoc znaku +.
String expletive = "brzydkie sowo";
String PG13 = "usunito";
String message = expletive + PG13;
Jeli z acuchem zostanie poczona warto niebdca acuchem, zostanie ona przekon-
wertowana na acuch (w rozdziale 5. przekonamy si, e kady obiekt w Javie mona
przekonwertowa na acuch). Na przykad kod:
int age = 13;
String rating = "PG" + age;
jest w peni poprawny i wydrukowaby to, co potrzeba (przy zachowaniu odpowiednich odst-
pw, gdy po sowie brzmi znajduje si spacja).
Rozdzia 3. Podstawowe elementy jzyka Java 79
Jako e w acuchach nie mona zmienia znakw, obiekty klasy String w dokumentacji
jzyka Java s okrelane jako niezmienialne (ang. immutable). Podobnie jak liczba 3 jest
zawsze liczb 3, acuch "Cze!" zawsze bdzie szeregiem jednostek kodowych odpowia-
dajcych znakom C, z, e, , i !. Nie mona zmieni tych wartoci. Mona jednak, o czym
si przekonalimy, zmieni zawarto zmiennej greeting, sprawiajc, aby odwoywaa si
do innego acucha. Podobnie moemy zadecydowa, e zmienna liczbowa przechowujca
warto 3 zmieni odwoanie na warto 4.
Czy to nie odbija si na wydajnoci? Wydaje si, e zmiana jednostek kodowych byaby
prostsza ni tworzenie nowego acucha od pocztku. Odpowied brzmi: tak i nie. Rzeczywi-
cie generowanie nowego acucha zawierajcego poczone acuchy "Cze" i "kaj" jest
nieefektywne, ale niezmienialno acuchw ma jedn zalet: kompilator moe traktowa
acuchy jako wspdzielone.
zwrci warto true, jeli acuchy s i t s identyczne, lub false w przeciwnym przypadku.
Zauwamy, e s i t mog by zmiennymi acuchowymi lub staymi acuchowymi. Na
przykad wyraenie:
80 Java. Podstawy
"Cze!".equals(greeting")
jest poprawne. Aby sprawdzi, czy dwa acuchy s identyczne, z pominiciem wielkoci
liter, naley uy metody equalsIgnoreCase.
"Cze!".equalsIgnoreCase("cze!")
Do porwnywania acuchw nie naley uywa operatora ==! Za jego pomoc mona tylko
stwierdzi, czy dwa acuchy s przechowywane w tej samej lokalizacji. Oczywicie skoro
acuchy s przechowywane w tym samym miejscu, to musz by rwne. Moliwe jest
jednak te przechowywanie wielu kopii jednego acucha w wielu rnych miejscach.
String greeting = "Cze!"; // Inicjacja zmiennej greeting acuchem.
if (greeting == "Cze!") . . .
// prawdopodobnie true
if (greeting.substring(0, 3) == "Cze") . . .
// prawdopodobnie false
Gdyby maszyna wirtualna zawsze traktowaa rwne acuchy jako wspdzielone, mona by
byo je porwnywa za pomoc operatora ==. Wspdzielone s jednak tylko stae acu-
chowe. acuchy bdce na przykad wynikiem operacji wykonywanych za pomoc ope-
ratora + albo metody substring nie s wspdzielone. W zwizku z tym nigdy nie uywaj
Rozdzia 3. Podstawowe elementy jzyka Java 81
lub
if (str.equals(""))
Pusty acuch jest w Javie obiektem zawierajcym informacj o swojej dugoci (0) i pust
tre. Ponadto zmienna typu String moe te zawiera specjaln warto o nazwie null,
oznaczajc, e aktualnie ze zmienn nie jest powizany aden obiekt (wicej informacji na
temat wartoci null znajduje si w rozdziale 4.). Aby sprawdzi, czy wybrany acuch jest
null, mona uy nastpujcej instrukcji warunkowej:
if (str == null)
Czasami trzeba te sprawdzi, czy acuch nie jest ani pusty, ani null. Wwczas mona si
posuy ponisz instrukcj warunkow:
if (str != null && str.length() != 0)
Najpierw naley sprawdzi, czy acuch nie jest null, poniewa wywoanie metody na
wartoci null jest bdem, o czym szerzej napisano w rozdziale 4.
Metoda length zwraca liczb jednostek kodowych, z ktrych skada si podany acuch
w systemie UTF-16. Na przykad:
String greeting = "Cze!";
int n = greeting.length(); // wynik = 6
Dlaczego robimy tyle szumu wok jednostek kodowych? Rozwamy ponisze zdanie:
oznacza zbir liczb cakowitych
nie zwrci spacji, ale drug jednostk kodow znaku . Aby unikn tego problemu, nie
naleao uywa typu char. Dziaa on na zbyt niskim poziomie.
Jeli nasz kod przemierza acuch i chcemy zobaczy kad wsprzdn kodow po kolei,
naley uy poniszych instrukcji:
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;
Takie wycigi z API znajduj si w wielu miejscach ksiki. Ich celem jest przybli-
enie czytelnikowi API Javy. Kady wycig z API zaczyna si od nazwy klasy, np.
java.lang.String znaczenie nazwy pakietu java.lang jest wyjanione w rozdziale 4.
Po nazwie klasy znajduj si nazwy, objanienia i opis parametrw jednej lub wikszej
liczby metod.
Z reguy nie wymieniamy wszystkich metod nalecych do klasy, ale wybieramy te, ktre
s najczciej uywane, i zamieszczamy ich zwize opisy. Pen list metod mona
znale w dokumentacji dostpnej w internecie (zobacz podrozdzia 3.6.8, Dokumenta-
cja API w internecie).
Dodatkowo podajemy numer wersji Javy, w ktrej zostaa wprowadzona dana klasa.
Jeli jaka metoda zostaa do niej dodana pniej, ma wasny numer wersji.
java.lang.String 1.0
String trim()
Usuwa wszystkie biae znaki z pocztku i koca acucha. Zwraca wynik jako
nowy acuch.
Rysunek 3.2.
Trzyczciowe
okno
dokumentacji API
Ekran jest podzielony na trzy czci. W grnej ramce po lewej stronie okna znajduje si lista
wszystkich dostpnych pakietw. Pod ni jest nieco wiksza ramka, ktra zawiera listy wszyst-
kich klas. Kliknicie nazwy jednej z klas powoduje wywietlenie dokumentacji tej klasy
w duym oknie po prawej stronie (zobacz rysunek 3.3). Aby na przykad uzyska dodatkowe
informacje na temat metod dostpnych w klasie String, naley w drugiej ramce znale
odnonik String i go klikn.
86 Java. Podstawy
Rysunek 3.3.
Opis klasy String
Aby zoy acuch z wielu bardzo maych czci, naley wykona nastpujce czynnoci.
Najpierw tworzymy pusty obiekt builder klasy StringBuilder (szczegowy opis konstrukto-
rw i operatora new znajduje si w rozdziale 4.):
StringBuilder builder = new StringBuilder();
Rozdzia 3. Podstawowe elementy jzyka Java 87
Rysunek 3.4.
Zestawienie
metod klasy
String
Rysunek 3.5.
Szczegowy opis
metody klasy
String
Po zoeniu acucha wywoujemy metod toString. Zwrci ona obiekt klasy String zawie-
rajcy sekwencj znakw znajdujc si w obiekcie builder.
String completedString = builder.toString();
java.lang.StringBuilder 5.0
StringBuilder()
Tworzy pusty obiekt builder.
int length()
Zwraca liczb jednostek kodowych zawartych w obiekcie builder lub buffer.
StringBuilder append(String str)
Dodaje acuch c.
StringBuilder append(char c)
Dodaje jednostk kodow c.
StringBuilder appendCodePoint(int cp)
Dodaje wsprzdn kodow, konwertujc j na jedn lub dwie jednostki
kodowe.
void setCharAt(int i, char c)
Ustawia i-t jednostk kodow na c.
StringBuilder insert(int offset, String str)
Wstawia acuch, umieszczajc jego pocztek na pozycji offset.
StringBuilder insert(int offset, char c)
Wstawia jednostk kodow na pozycji offset.
StringBuilder delete(int startIndex, int endIndex)
Usuwa jednostki kodowe znajdujce si midzy pozycjami startIndex i endIndex - 1.
String toString()
Zwraca acuch zawierajcy sekwencj znakw znajdujc si w obiekcie
builder lub buffer.
Rozdzia 3. Podstawowe elementy jzyka Java 89
Nastpnie dane wejciowe odczytuje si za pomoc rnych metod klasy Scanner. Na przy-
kad metoda nextLine czyta jeden wiersz danych:
System.out.print("Jak si nazywasz? ");
String name = in.nextLine();
W tym przypadku zastosowanie metody nextLine zostao podyktowane tym, e dane na wej-
ciu mog zawiera spacje. Aby odczyta jedno sowo (ograniczone spacjami), naley wywo-
a ponisz metod:
String firstName = in.next();
/**
90 Java. Podstawy
java.util.Scanner 5.0
Scanner(InputStream in)
Tworzy obiekt klasy Scanner przy uyciu danych z podanego strumienia
wejciowego.
Rozdzia 3. Podstawowe elementy jzyka Java 91
String nextLine()
Wczytuje kolejny wiersz danych.
String text()
Wczytuje kolejne sowo (znakiem rozdzielajcym jest spacja).
int nextInt()
double nextDouble()
Wczytuje i konwertuje kolejn liczb cakowit lub zmiennoprzecinkow.
boolean hasNext()
Sprawdza, czy jest kolejne sowo.
boolean hasNextInt()
boolean hasNextDouble()
Sprawdza, czy dana sekwencja znakw jest liczb cakowit, czy liczb
zmiennoprzecinkow.
java.lang.System 1.0
java.io.Console 6
wydrukuje:
3333.3333333333335
Problemy zaczynaj si wtedy, gdy chcemy na przykad wywietli liczb dolarw i centw.
92 Java. Podstawy
wydrukuje:
3 333,33
Mona stosowa po kilka znacznikw naraz, na przykad zapis "%,(.2f" oznacza uycie
separatorw grup i ujcie liczb ujemnych w nawiasy.
Aby utworzy sformatowany acuch, ale go nie drukowa, naley uy statycznej metody
String.format:
String message = String.format("Witaj, %s. W przyszym roku bdziesz mie lat %d",
name, age);
Mimo e typ Date omawiamy szczegowo dopiero w rozdziale 4., przedstawiamy krtki opis
opcji metody printf do formatowania daty i godziny. Stosowany jest format dwuliterowy,
w ktrym pierwsza litera to t, a druga jest jedn z liter znajdujcych si w tabeli 3.7. Na
przykad:
System.out.printf("%tc", new Date());
1
Aby program zadziaa, na pocztku kodu rdowego naley wstawi wiersz import java.util.Date;
przyp. tum.
Rozdzia 3. Podstawowe elementy jzyka Java 93
s acuch Witaj
c Znak H
Jak wida w tabeli 3.7, niektre formaty zwracaj tylko okrelon cz daty, na przykad
tylko dzie albo tylko miesic. Formatowanie kadej czci daty oddzielnie byoby nieroz-
sdnym rozwizaniem. Dlatego w acuchu formatujcym mona poda indeks argumentu,
ktry ma by sformatowany. Indeks musi si znajdowa bezporednio po symbolu % i ko-
czy si symbolem $. Na przykad:
System.out.printf("%1$s %2$te %2$tB %2$tY", "Data:", new Date());
Ewentualnie mona uy flagi <. Oznacza ona, e ten sam argument co w poprzedniej spe-
cyfikacji formatu powinien zosta uyty ponownie. Ponisza instrukcja:
System.out.printf("%s %te %<tB %<tY", "Data:", new Date());
Przedstawione zostay wszystkie wasnoci metody printf. Rysunek 3.6 prezentuje sche-
mat opisujcy skadni specyfikatorw formatu.
Jeli nazwa pliku zawiera lewe ukoniki, naley pamita o zastosowaniu dla nich symboli
zastpczych: "c:\\mojkatalog\\mojplik.txt".
Po wykonaniu tych czynnoci mona odczyta zawarto pliku za pomoc metod klasy
Scanner, ktre byy opisywane wczeniej.
Aby zapisa dane do pliku, naley posuy si obiektem PrintWriter. Naley poda kon-
struktorowi nazw pliku:
PrintWriter out = new PrintWriter("mojplik.txt");
Jeli plik nie istnieje, mona uy metod print, println lub printf, podobnie jak w przy-
padku drukowania do wyjcia System.out.
Obiekt Scanner mona utworzy przy uyciu parametru acuchowego, ale parametr
ten zostanie zinterpretowany jako dane, a nie nazwa pliku. Jeli na przykad
napiszemy:
Scanner in = new Scanner("mojplik.txt"); // Bd?
obiekt klasy Scanner bdzie widzia dane skadajce si z jedenastu znakw: 'm', 'o',
'j' itd. Istnieje due prawdopodobiestwo, e autorowi kodu chodzio o co innego.
Jasne jest zatem, e dostp do plikw jest rwnie atwy jak uywanie wejcia System.in oraz
wyjcia System.out. Jest tylko jedno ale: jeli obiekt klasy Scanner zostanie utworzony
przy uyciu nazwy nieistniejcego pliku albo PrintWriter przy uyciu nazwy, ktrej nie
mona utworzy, wystpi wyjtek. Dla kompilatora Javy wyjtki te maj wiksze znaczenie
ni na przykad wyjtek dzielenia przez zero. Rozmaite techniki obsugi wyjtkw zostay
opisane w rozdziale 11. Na razie wystarczy, jeli poinformujemy kompilator, e wiemy, i
istnieje moliwo wystpienia wyjtku zwizanego z nieodnalezieniem pliku. Robimy to,
dodajc do metody main klauzul throws:
Rozdzia 3. Podstawowe elementy jzyka Java 97
Jeli nie moesz si poapa w lokalizacji plikw, moesz zastosowa cieki bezwzgldne,
takie jak "c:\\mojkatalog\\mojplik.txt" lub "/home/ja/mojkatalog/mojplik.txt".
Wiemy ju, jak odczytywa i zapisywa pliki zawierajce dane tekstowe. Bardziej zaawan-
sowane zagadnienia, jak obsuga rnych standardw kodowania znakw, przetwarzanie
danych binarnych, odczyt katalogw i zapis plikw archiwum zip, zostay opisane w roz-
dziale 1. drugiego tomu.
java.util.Scanner 5.0
Scanner(Path p)
Tworzy obiekt klasy Scanner, ktry wczytuje dane z podanej cieki.
Scanner(String data)
Tworzy obiekt klasy Scanner, ktry wczytuje dane z podanego acucha.
java.io.PrintWriter 1.1
PrintWriter(String fileName)
Tworzy obiekt PrintWriter, ktry zapisuje dane do pliku o podanej nazwie.
java.nio.file.Paths 7.0
Blok, czyli instrukcja zoona, to dowolna liczba instrukcji Javy ujtych w nawiasy klam-
rowe. Blok okrela zasig zmiennych. Bloki mona zagnieda w innych blokach. Poniej
znajduje si blok zagniedony w bloku metody main:
public static void main(String[] args)
{
int n;
. . .
{
int k;
. . .
} // Definicja zmiennej k jest dostpna tylko do tego miejsca.
}
Nie mona zdefiniowa dwch zmiennych o takiej samej nazwie w dwch zagniedonych
blokach. Na przykad poniszy kod jest bdny i nie mona go skompilowa:
public static void main(String[] args)
{
int n;
. . .
{
int k;
int n; // Bd nie mona ponownie zdefiniowa zmiennej n w bloku wewntrznym.
. . .
}
}
Rozdzia 3. Podstawowe elementy jzyka Java 99
Podobnie jak w wielu jzykach, w Javie czsto po spenieniu jednego warunku konieczne
jest wykonanie wielu instrukcji. W takim przypadku naley zastosowa blok instrukcji
w nastpujcej postaci:
{
instrukcja1;
instrukcja2;
}
Na przykad:
if (yourSales >= target)
{
performance = "rednio";
bonus = 100;
}
Bardziej oglna posta instrukcji warunkowej w Javie jest nastpujca (zobacz rysunek 3.8):
if (warunek) instrukcja1 else instrukcja2
Na przykad:
if (yourSales >= target)
{
performance = "rednio";
bonus = 100 + 0.01 * (yourSales - target);
}
else
{
performance = "Sabo";
bonus = 0;
}
100 Java. Podstawy
Rysunek 3.7.
Diagram
przepywu
sterowania
instrukcji if
Rysunek 3.8.
Diagram
przepywu
sterowania
instrukcji if-else
Stosowanie else jest opcjonalne. Dane else zawsze odpowiada najbliszemu poprzedzaj-
cemu je if. W zwizku z tym w instrukcji:
if (x <= 0) if (x == 0) sign = 0; else sign = -1;
Rozdzia 3. Podstawowe elementy jzyka Java 101
else odpowiada drugiemu if. Oczywicie dobrze by byo zastosowa klamry, aby kod by
bardziej czytelny:
if (x <= 0) { if (x == 0) sign = 0; else sign = -1; }
Czsto stosuje si kilka instrukcji else-if jedna po drugiej (zobacz rysunek 3.9). Na przykad:
if (yourSales >= 2 * target)
{
performance = "Znakomicie";
bonus = 1000;
}
else if (yourSales >= 1.5 * target)
{
performance = "Niele";
bonus = 500;
}
else if (yourSales >= target)
{
performance = "rednio";
bonus = 100;
}
else
{
System.out.println("Jeste zwolniony");
}
3.8.3. Ptle
Ptla while wykonuje instrukcj (albo blok instrukcji) tak dugo, jak dugo warunek ma
warto true. Oglna posta instrukcji while jest nastpujca:
while (warunek) instrukcja
Instrukcje ptli while nie zostan nigdy wykonane, jeli warunek ma warto false na
pocztku (zobacz rysunek 3.10).
Program z listingu 3.3 oblicza, ile czasu trzeba skada pienidze, aby dosta okrelon
emerytur, przy zaoeniu, e kadego roku wpacana jest taka sama kwota, i przy okrelo-
nej stopie oprocentowania wpaconych pienidzy.
(Nie naley ufa temu programowi przy planowaniu emerytury. Pominito w nim kilka
szczegw, takich jak inflacja i przewidywana dugo ycia).
102 Java. Podstawy
Rysunek 3.9.
Diagram
przepywu
sterowania
instrukcji
if-else if (wiele
odgazie)
Ptla while sprawdza warunek na samym pocztku dziaania. W zwizku z tym jej instrukcje
mog nie zosta wykonane ani razu. Aby mie pewno, e instrukcje zostan wykonane co
najmniej raz, sprawdzanie warunku trzeba przenie na sam koniec. Do tego suy ptla
do-while. Jej skadnia jest nastpujca:
do instrukcja while (warunek)
Ta instrukcja najpierw wykonuje instrukcj (ktra zazwyczaj jest blokiem instrukcji), a dopiero
potem sprawdza warunek. Nastpnie znowu wykonuje instrukcj i sprawdza warunek itd.
Kod na listingu 3.4 oblicza nowe saldo na koncie emerytalnym, a nastpnie pyta, czy jeste-
my gotowi przej na emerytur:
do
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
year++;
// Drukowanie aktualnego stanu konta.
. . .
Rozdzia 3. Podstawowe elementy jzyka Java 103
Rysunek 3.10.
Diagram
przepywu
sterowania
instrukcji while
Ptla jest powtarzana, dopki uytkownik podaje odpowied N (zobacz rysunek 3.11). Ten pro-
gram jest dobrym przykadem ptli, ktra musi by wykonana co najmniej jeden raz, poniewa
uytkownik musi zobaczy stan konta, zanim podejmie decyzj o przejciu na emerytur.
/**
* Ten program demonstruje sposb uycia ptli <code>while</code>.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class Retirement
{
104 Java. Podstawy
Rysunek 3.11.
Diagram
przepywu
sterowania
instrukcji do-while
double balance = 0;
int years = 0;
balance += interest;
years++;
}
/**
* Ten program demonstruje uycie ptli <code>do/while</code>.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class Retirement2
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
double balance = 0;
int year = 0;
String input;
// Aktualizacja stanu konta, kiedy uytkownik nie jest gotowy do przejcia na emerytur.
do
{
// Dodanie tegorocznych patnoci i odsetek.
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
year++;
Rysunek 3.12.
Diagram
przepywu
sterowania
ptli for
Na pierwszym miejscu z reguy znajduje si inicjacja licznika. Drugie miejsce zajmuje waru-
nek, ktry jest sprawdzany przed kadym powtrzeniem instrukcji ptli. Na trzeciej pozycji
umieszczamy informacj na temat sposobu zmiany wartoci licznika.
Mimo i w Javie, podobnie jak w C++, w rnych miejscach ptli for mona wstawi prawie
kade wyraenie, niepisana zasada gosi, e do dobrego stylu naley, aby w tych miejscach
inicjowa, sprawdza i zmienia warto jednej zmiennej. Nie stosujc si do tej reguy,
mona napisa bardzo zagmatwane ptle.
Jednak nawet w granicach dobrego stylu programowania mona sobie pozwoli na wiele.
Mona na przykad utworzy ptl zmniejszajc licznik:
Rozdzia 3. Podstawowe elementy jzyka Java 107
Zmienna zadeklarowana na pierwszej pozycji w ptli for ma zasig do koca ciaa tej ptli.
for (int i = 1; i <= 10; i++)
{
. . .
}
// Tutaj zmienna i ju nie jest dostpna.
Innymi sowy, warto zmiennej zadeklarowanej w wyraeniu ptli for nie jest dostpna
poza t ptl. W zwizku z tym, aby mc uy wartoci licznika ptli poza t ptl, trzeba
go zadeklarowa poza jej nagwkiem!
int i;
for (i = 1; i <= 10; i++)
{
. . .
}
// Zmienna i tutaj te jest dostpna.
Z drugiej jednak strony w kilku ptlach for mona zdefiniowa zmienn o takiej samej nazwie:
for (int i = 1; i <= 10; i++)
{
. . .
}
. . .
for (int i = 11; i <= 20; i++) // W tym miejscu dozwolona jest ponowna deklaracja zmiennej
i.
{
. . .
}
Ten program oblicza szanse wygrania na loterii. Jeli na przykad loteria polega na wybra-
niu szeciu liczb z przedziau 1 50, to istnieje (50*49*48*47*46*45)/(1*2*3*4*5*6) mo-
liwych kombinacji, co oznacza, e nasze szanse s jak 1 do 15 890 700. Powodzenia!
W sekcji 3.10.1 znajduje si opis uoglnionej ptli for (zwanej take ptl typu
for each), ktra zostaa dodana w wersji Java SE 5.
import java.util.*;
/**
* Ten program demonstruje zastosowanie ptli <code>for</code>.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class LotteryOdds
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
/*
* Obliczanie wspczynnika dwumianowego n*(n1)*(n2)**(nk+1)/(1*2*3**k)
*/
int lotteryOdds = 1;
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds * (n - i + 1) / i;
Na przykad do utworzenia systemu menu zawierajcego cztery opcje, jak ten na rysunku 3.13,
mona uy kodu podobnego do tego poniej:
Scanner in = new Scanner(System.in);
System.out.print("Wybierz opcj (1, 2, 3, 4) ");
int choice = in.nextInt();
switch (choice)
{
case 1:
. . .
break;
case 2:
. . .
break;
case 3:
. . .
break;
case 4:
. . .
break;
default:
// Nieprawidowe dane.
. . .
break;
}
Wykonywanie programu zaczyna si od etykiety case, ktra pasuje do wybranej opcji, i jest
kontynuowane do napotkania instrukcji break lub koca instrukcji switch. Jeli adna z ety-
kiet nie zostanie dopasowana, nastpi wykonanie czci oznaczonej przez etykiet default
jeli taka istnieje.
Na przykad:
String input = . . .;
switch (input.toLowerCase())
{
case "tak": // OK od Java SE 7
110 Java. Podstawy
Rysunek 3.13.
Diagram
przepywu
sterowania
instrukcji switch
. . .
break;
. . .
}
Size sz = . . .;
switch (sz)
{
case SMALL: // Nie trzeba byo pisa Size.SMALL.
. . .
break;
. . .
}
Przyjrzyjmy si najpierw instrukcji break bez etykiety. Tej samej instrukcji break, za pomoc
ktrej wychodzi si z instrukcji switch, mona uy do przerwania dziaania ptli. Na przykad:
while (years <= 100)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
if (balance >= goal) break;
years++;
}
112 Java. Podstawy
Wyjcie z ptli nastpi, kiedy warto znajdujcej si na samej grze ptli zmiennej years
przekroczy 100 albo znajdujca si w rodku zmienna balance bdzie miaa warto wik-
sz lub rwn goal. Oczywicie t sam warto zmiennej years mona by byo obliczy
bez uycia instrukcji break:
while (years <= 100 && balance < goal)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
if (balance < goal)
years++;
}
Naley jednak zauway, e wyraenie sprawdzajce balance < goal jest w tej wersji uyte
dwukrotnie. Aby unikn tego powtrzenia, niektrzy programici stosuj instrukcj break.
W Javie dostpna jest te instrukcja break z etykiet (brak jej natomiast w jzyku C++),
ktra umoliwia wyjcie z kilku zagniedonych ptli. Czasami w gboko zagniedonych
ptlach dziej si dziwne rzeczy. W takiej sytuacji najlepiej jest wyj cakiem na zewntrz.
Zaprogramowanie takiego dziaania za pomoc dodatkowych warunkw w rnych testach
ptli jest rozwizaniem mao wygodnym.
Poniej znajduje si przykadowy kod prezentujcy dziaanie instrukcji break. Naley zauwa-
y, e etykieta musi si znajdowa przed najbardziej zewntrzn ptl, z ktrej chcemy wyj.
Ponadto po etykiecie w tym miejscu musi si znajdowa dwukropek.
Scanner in = new Scanner(System.in);
int n;
read_data:
while (. . .) // Ta ptla jest opatrzona etykiet.
{
. . .
for (. . .) // Ta zagniedona ptla nie ma etykiety.
{
System.out.print("Podaj liczb >= 0: ");
n = in.nextInt();
if (n < 0) // To nie powinno mie miejsca nie mona kontynuowa.
break read_data;
// Wyjcie z ptli z etykiet read_data.
. . .
}
}
// Ta instrukcja jest wykonywana bezporednio po przerwaniu ptli.
if (n < 0) // Sprawdzenie, czy ma miejsce niepodana sytuacja.
{
// Obsuga niechcianej sytuacji.
}
else
{
// Wykonywanie w normalnym toku.
}
Rozdzia 3. Podstawowe elementy jzyka Java 113
Jeli zostan podane nieprawidowe dane, instrukcja break z etykiet przeniesie sterowanie
do miejsca bezporednio za blokiem opatrzonym t etykiet. Nastpnie, tak jak w kadym
przypadku uycia instrukcji break, trzeba sprawdzi, czy wyjcie z ptli nastpio w toku
normalnego dziaania, czy zostao spowodowane przez instrukcj break.
W zwizku z tym, jeli tsknisz za instrukcj goto i moesz umieci blok bezpored-
nio przed miejscem, do ktrego ma nastpi przejcie, moesz uy instrukcji break!
Oczywicie nie polecamy tej metody programowania. Zauwa te, e przejcie jest
moliwe tylko w jedn stron nie mona wskoczy do bloku.
Na zakoczenie zostaa jeszcze instrukcja continue, ktra podobnie jak instrukcja break
zmienia normalny przepyw sterowania. Instrukcja continue przenosi sterowanie do nagwka
najgbiej zagniedonej ptli. Na przykad:
Scanner in = new Scanner(System.in);
while (sum < goal)
{
System.out.print("Podaj liczb: ");
n = in.nextInt();
if (n < 0) continue;
sum += n; // Wyraenie nie zostanie wykonane, jeli n < 0.
}
Instrukcja continue uyta w ptli for powoduje przejcie do czci aktualizujcej warto
zmiennej w nagwku tej ptli. Przyjrzyjmy si nastpujcemu przykadowi:
for (count = 1; count <= 100; count++)
{
System.out.print("Podaj liczb (-1 koczy dziaanie programu): ");
n = in.nextInt();
if (n < 0) continue;
sum += n; // Wyraenie nie zostanie wykonane, jeli n < 0.
}
Istnieje take wersja instrukcji continue z etykiet, ktra powoduje przejcie do nagwka ptli
z odpowiedni etykiet.
114 Java. Podstawy
Wielu programistw myli instrukcje break i continue. Ich stosowanie nie jest
obowizkowe i to, co mona osign przy ich uyciu, da si zawsze uzyska w inny
sposb. W tej ksice nigdy nie uywamy instrukcji break i continue.
Niestety w dziaaniach na wielkich liczbach nie mona uywa dobrze nam znanych operato-
rw arytmetycznych, jak + czy *. Zamiast nich trzeba uywa odpowiednich metod, jak add
i multiply, dostpnych w klasach wielkich liczb:
BigInteger c = a.add(b); // c = a + b
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b + 2)
Listing 3.6 przedstawia zmodyfikowan wersj programu loteryjnego z listingu 3.5. W tej
wersji dziaa ona take po podaniu bardzo duych liczb. Jeli na przykad loteria polega na
wyborze 60 liczb ze zbioru 1 490, program ten poinformuje nas, e nasze szanse wynosz
1 do 716 395 843 461 995 557 415 116 222 540 092 933 411 717 612 789 263 493 493 351
013 459 481 104 668 848. Powodzenia!
Przy uyciu wielkich liczb odpowiednikiem tej instrukcji jest ponisza instrukcja:
lotteryOdds = lotteryOdds.multiply(BigInteger.valueOf(n - i + 1)).divide(BigInteger.
valueOf(i));
Rozdzia 3. Podstawowe elementy jzyka Java 115
/**
* Ten program wykorzystuje wielkie liczby do obliczenia szans wygrania na loterii.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class BigIntegerTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
/*
* Obliczanie wspczynnika dwumianowego n*(n1)*(n2)**(nk+1)/(1*2*3**k)
*/
java.math.BigInteger 1.1
java.math.BigDecimal 1.1
3.10. Tablice
Tablica jest rodzajem struktury danych bdc zestawem elementw tego samego typu. Dostp
do kadego z tych elementw mona uzyska za pomoc jego indeksu w postaci liczby typu
int. Jeli na przykad a jest tablic liczb cakowitych, to a[i] jest i-tym elementem tej tablicy.
Deklaracja zmiennej tablicowej polega na okreleniu typu tablicy (czyli podaniu typu elemen-
tw i nawiasw kwadratowych []) i nazwy zmiennej. Poniej znajduje si przykadowa dekla-
racja tablicy zdolnej do przechowywania liczb cakowitych:
int[] a;
Powysza instrukcja tylko deklaruje zmienn a. Nie inicjuje jej jednak tablic. Do utworze-
nia tablicy potrzebny jest operator new.
int[] a = new int[100];
Powysza instrukcja tworzy i inicjuje tablic, w ktrej mona zapisa 100 liczb cakowitych.
Dugo tablicy nie musi by staa, np. instrukcja new int[n] tworzy tablic o dugoci n.
Powysza instrukcja tworzy tablic dziesiciu acuchw, z ktrych kady jest null. Jeli
w tablicy maj by zapisane puste acuchy, naley je do niej przekaza:
for (int i = 0; i < 10; i++) names[i] = "";
Rozmiaru tablicy nie mona zmieni (ale mona oczywicie zmienia jej poszczeglne ele-
menty). Jeli konieczne s czste zmiany rozmiaru tablicy w trakcie dziaania programu, naley
uy listy ArrayList (wicej informacji na ten temat znajduje si w rozdziale 5.).
ustawia podan zmienn na kady element kolekcji i wykonuje instrukcj (ktra oczywicie
moe by blokiem instrukcji). Kolekcja musi by tablic lub obiektem klasy implementujcej
interfejs Iterable, jak np. ArrayList. Listy ArrayList omawiamy w rozdziale 5., a interfejs
Iterable w drugim rozdziale drugiego tomu.
118 Java. Podstawy
Ten sam efekt mona oczywicie uzyska za pomoc typowej ptli for:
for (int i = 0; i < a.length; i++)
System.out.println(a[i]);
Ptla typu for each jest jednak bardziej zwiza i mniej podatna na bdy (brak indeksw
pocztkowego i kocowego).
Zmienna ptlowa ptli typu for each przemierza elementy tablicy, nie wartoci
indeksw.
Ptla typu for each jest bardzo miym udoskonaleniem jzyka w stosunku do tradycyjnej
formy, jeli chcemy przetworzy wszystkie elementy tablicy. Nadal jednak ptla for znaj-
duje wiele zastosowa, na przykad kiedy nie chcemy przemierza caej kolekcji danych lub
musimy uy indeksu w ptli.
Naley zauway, e w przypadku zastosowania tej skadni nie uywa si operatora new.
Powysze wyraenie przydziela pami dla nowej tablicy i zapenia j wartociami poda-
nymi midzy klamrami. Sprawdza liczb pocztkowych wartoci i odpowiednio ustawia
rozmiar tworzonej tablicy. Za pomoc tej metody mona ponownie zainicjowa tablic, nie
tworzc przy tym nowej zmiennej. Na przykad zapis:
smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 };
Wynik przedstawia rysunek 3.14. Aby rzeczywicie skopiowa wszystkie elementy jednej
tablicy do innej, naley uy metody copyTo dostpnej w klasie Arrays:
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);
Rysunek 3.14.
Kopiowanie
zmiennej
tablicowej
Drugi parametr okrela rozmiar nowej tablicy. Metoda ta jest czsto wykorzystywana do zwik-
szania rozmiaru tablicy:
luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);
Dodatkowe elementy s zapeniane zerami, jeli tablica przechowuje liczby, lub wartociami
false, jeli przechowywane s wartoci logiczne. Jeli rozmiar nowej tablicy jest mniejszy
ni pierwotny, kopiowane s elementy z pocztku tablicy.
120 Java. Podstawy
Tablice w Javie nie s tym samym co tablice w C++ na stosie (ang. stack). S
natomiast w zasadzie odpowiednikiem wskanikw do tablic alokowanych na stercie
(ang. heap). To znaczy:
int[] a = new int[100]; // Java
W Javie nazwa programu nie jest przechowywana w tablicy args w metodzie main.
Jeli na przykad program zostanie uruchomiony nastpujco:
java Message -h wiecie
element args[0] bdzie zawiera warto parametru "-h", a nie acuch "Message"
czy "java".
Program widoczny na listingu 3.7 stanowi przykad praktycznego zastosowania tablic. Jego
dziaanie polega na losowaniu kilku liczb na loterii. Jeli na przykad zagramy w wybr
szeciu liczb z 49, wynik moe by nastpujcy:
Postaw na nastpujce liczby. Dziki nim zdobdziesz bogactwo!
1
10
23
29
31
34
/**
* Ten program demonstruje zastosowanie tablic.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class LotteryDrawing
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
122 Java. Podstawy
i-ty wynik bdzie liczb przechowywan w indeksie i. Pocztkowo jest to i+1, ale nieba-
wem si przekonamy, e zawarto tablicy numbers zmienia si po kadym losowaniu.
result[i] = numbers[r];
Trzeba si zabezpieczy, aby nie wylosowa tej samej liczby ponownie wszystkie liczby
na loterii musz by inne. W tym celu zastpujemy element numbers[r] ostatni liczb
w tablicy i zmniejszamy n o 1.
numbers[r] = numbers[n - 1];
n--;
Rozdzia 3. Podstawowe elementy jzyka Java 123
Naszym celem jest to, aby za kadym razem by losowany indeks, a nie rzeczywiste warto-
ci. Indeks ten wskazuje na element tablicy zawierajcej wartoci, ktre nie zostay jeszcze
wylosowane.
java.util.Arrays 1.2
Przypumy, e chcemy utworzy tabel liczb pokazujc, jaki bdzie zwrot z inwestycji
10 000 z przy rnych stopach procentowych skadanych rocznie. Tabela 3.8 przedstawia
taki scenariusz.
Jak zwykle tablicy nie mona uywa, dopki si jej nie zainicjuje za pomoc wyraenia new.
W tym przypadku inicjacja moe wyglda nastpujco:
balances = new double[NYEARS][NRATES];
Jeli znane s elementy tablicy, mona uy skrconej notacji inicjacji tablicy wielowymia-
rowej, ktra nie wymaga wywoania new. Na przykad:
int[][] magicSquare =
{
{16, 3, 2, 13},
{5, 10, 11, 8},
Rozdzia 3. Podstawowe elementy jzyka Java 125
{9, 6, 7, 12},
{4, 15, 14, 1}
};
Dostp do elementw takiej tablicy uzyskujemy za pomoc dwch indeksw, np. balances
[i][j].
Ptla typu for each nie sprawdza automatycznie wszystkich elementw tablicy
dwuwymiarowej. Przechodzi tylko przez wiersze, ktre s tablicami jednowymia-
rowymi. Aby dotrze do wszystkich elementw tablicy dwuwymiarowej a, naley zagnie-
dzi jedn ptl w drugiej:
for (double[] row : a)
for (double value : row)
Dziaania na wartociach
// Obliczenie odsetek.
double interest = oldBalance * interestRate[j];
System.out.println();
Rozdzia 3. Podstawowe elementy jzyka Java 127
System.out.println();
}
}
}
Wyraenie balances[i] odwouje si do i-tej podtablicy, ktra jest i-tym wierszem tablicy.
Wiersz ten sam jest tablic, a wic balances[i][j] odwouje si do j-tego wiersza tej tablicy.
Rwnie atwe jest tworzenie tablic postrzpionych (ang. ragged arrays), czyli takich, w kt-
rych wiersze maj rne dugoci. Oto typowy przykad. Utworzymy tablic, w ktrej ele-
ment w i-tym wierszu i j-tej kolumnie jest rwny liczbie moliwych wynikw loterii pole-
gajcej na losowaniu j liczb spord i liczb.
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
Jako e j nie moe by wiksze od i, powstaje macierz trjktna. Wiersz i-ty ma i+1 ele-
mentw (zezwalamy na niewybranie adnego elementu mona to zrobi tylko w jeden
sposb). Aby utworzy tak tablic postrzpion, naley najpierw alokowa w pamici tablic
przechowujc wiersze.
int[][] odds = new int[NMAX + 1][];
Po utworzeniu tablicy mona dziaa na jej elementach w normalny sposb, pod warunkiem
e nie przekroczymy zakresu.
for (int n = 0; n < odds.length; n++)
for (int k = 0; k < odds[n].length; k++)
{
// Obliczenie lotteryOdds
. . .
odds[n][k] = lotteryOdds;
}
{
final int NMAX = 10;
odds[n][k] = lotteryOdds;
}
W tym rozdziale:
Wstp do programowania obiektowego
Uywanie standardowych klas
Definiowanie wasnych klas
Pola i metody statyczne
Parametry metod
Konstrukcja obiektw
Pakiety
cieka klas
Komentarze dokumentacyjne
Porady dotyczce projektowania klas
Osoby, ktre do tej pory nie miay do czynienia z programowaniem obiektowym, powinny
bardzo uwanie przeczyta ten rozdzia. Programowanie obiektowe wymaga innego sposobu
mylenia ni programowanie proceduralne. Przestawienie si bywa czasami trudne, ale kon-
tynuacja nauki Javy bez znajomoci technik obiektowych byaby niemoliwa.
Programici jzyka C++ odkryj w tym rozdziale (podobnie jak w poprzednim) duo podo-
biestw midzy Jav i C++. Jednak Java i C++ rni si na tyle, e take programici C++
powinni przeczyta ten rozdzia z uwag. W przestawieniu si na nowy jzyk bd pomocne
uwagi dotyczce jzyka C++.
132 Java. Podstawy
Program obiektowy skada si z obiektw. Kady obiekt udostpnia okrelony zestaw funkcji,
a ich szczegy implementacyjne s ukryte. Wiele obiektw uywanych w programach pocho-
dzi z biblioteki. Cz z nich programista tworzy jednak wasnorcznie. To, czy programista
zdecyduje si na budow wasnego obiektu, czy skorzysta z ju istniejcego, zaley od jego
czasu i moliwoci. Dopki obiekt spenia wymagania, z reguy nie ma potrzeby zagbiania
si w tajniki jego implementacji. W programowaniu obiektowym implementacja obiektu nie
ma znaczenia, dopki dziaa on zgodnie z oczekiwaniami.
4.1.1. Klasy
Klasa jest szablonem, z ktrego tworzy si obiekty. Jeli klasy s foremkami do robienia
ciastek, to obiekty s samymi ciastkami. Konstruujc obiekt, tworzymy egzemplarz klasy.
Rysunek 4.1.
Programowanie
proceduralne
a programowanie
obiektowe
Aby hermetyzacja speniaa swoje zadanie, metody nie mog by bezporednio wywoywane
na rzecz skadowych obiektw klas innych ni ich wasna. Dane obiektowe powinny by uy-
wane w programie tylko za porednictwem metod obiektw zawierajcych te dane. Hermety-
zacja nadaje obiektowi charakter czarnej skrzynki, co jest kluczowe dla koncepcji wielo-
krotnego uycia kodu, jak i jego niezawodnoci. Oznacza to, e sposb przechowywania danych
w klasie moe si diametralnie zmieni, ale dopki udostpnia ona te same metody do mani-
pulacji tymi danymi, aden obiekt nie zostanie tym dotknity.
Budow klas w Javie uatwia jeszcze jedna cecha programowania obiektowego: klasy mona
budowa poprzez rozszerzanie (ang. extending) innych klas. Wszystkie klasy w Javie dziedzi-
cz po jednej klasie bazowej o nazwie Object. Wicej informacji na temat tej klasy znajduje
si w rozdziale 5.
Kiedy rozszerzamy istniejc klas, nowo powstaa klasa ma wszystkie cechy i metody klasy
rozszerzanej. Nowe metody i pola s dostpne tylko w nowej klasie. Proces rozszerzania klasy
w celu utworzenia nowej klasy nazywa si dziedziczeniem (ang. inheritance). Szczegowe
informacje na temat dziedziczenia znajduj si w kolejnym rozdziale.
4.1.2. Obiekty
Aby sprawnie porusza si w wiecie programowania obiektowego, naley zna trzy podsta-
wowe cechy obiektu:
Zachowanie obiektu co mona z obiektem zrobi i jakie metody mona
wywoywa na jego rzecz.
134 Java. Podstawy
Wszystkie obiekty bdce egzemplarzami tej samej klasy s do siebie podobne pod tym
wzgldem, e charakteryzuj si takim samym zachowaniem. Zachowanie obiektu definiuj
metody, ktre mona wywoywa.
Kady obiekt przechowuje informacje o tym, jak aktualnie wyglda. Jest to stan obiektu.
Stan obiektu moe si zmienia w czasie, ale nie samoczynnie. Zmiana stanu obiektu musi
by spowodowana wywoaniem metod (jeli stan obiektu zmieni si, mimo e nie wywoano
na jego rzecz adnej metody, oznacza to, e zostaa zamana zasada hermetyzacji).
Stan obiektu nie wystarczy jednak, aby ten obiekt w peni opisa, poniewa istnieje jeszcze
tosamo obiektu. Na przykad w systemie przetwarzania zamwie dwa zamwienia s
odrbne, mimo i dotycz zakupu tego samego produktu. Naley zauway, e poszczeglne
obiekty bdce egzemplarzami tej samej klasy zawsze maj inn tosamo i zazwyczaj
rni si stanami.
Te kluczowe cechy mog midzy sob oddziaywa. Na przykad stan obiektu moe mie
wpyw na jego zachowanie (jeli zamwienie zostao wysane lub opacone, obiekt moe
odmwi wykonania metody, ktra dodaje lub usuwa elementy; podobnie jest w przypadku,
gdy zamwienie jest puste, to znaczy adne produkty nie zostay jeszcze dodane obiekt nie
powinien wwczas zezwoli na jego wysanie).
Prosta zasada dotyczca nadawania nazw klasom nakazuje tworzenie nazw z rzeczownikw
obecnych w analizie problemu. Metody natomiast odpowiadaj czasownikom.
1
Ze wzgldu na to, e take polscy programici zazwyczaj stosuj angielskie nazwy w swoich programach,
nie tumacz adnych nazw, tylko podaj ich polskie odpowiedniki, gdy jest to uzasadnione przyp. tum.
Rozdzia 4. Obiekty i klasy 135
Z tych rzeczownikw mona utworzy nastpujce nazwy klas: Item, Order, Shipping
Address itd.
Zwizek zalenoci (czyli uywa) jest najbardziej oczywisty, a zarazem oglny. Na przy-
kad klasa Order uywa klasy Account, poniewa obiekty klasy Order potrzebuj dostpu do
obiektw Account w celu sprawdzenia wypacalnoci klienta. Natomiast klasa Item nie jest
zalena od klasy Account, poniewa obiekty klasy Item nie potrzebuj informacji o kontach
klientw. Zatem klasa zaley od innej klasy, jeli metody tej pierwszej uywaj obiektw
tej drugiej lub na nich operuj.
Liczb klas wzajemnie zalenych naley ogranicza do minimum. Jeli klasa A nie wie nic
o istnieniu klasy B, to nie maj dla niej znaczenia adne zmiany w klasie B (a to oznacza, e
zmiany wprowadzone w klasie B nie powoduj powstawania bdw w klasie A)! W termi-
nologii inynierii oprogramowania okrela si to mianem skojarzenia, czyli stopniem powi-
zania midzy klasami (ang. coupling).
Agregacja (czyli zwizek zawiera) jest atwa do zrozumienia, poniewa opisuje konkretne
zjawisko. Na przykad obiekt klasy Order zawiera obiekty klasy Item. Innymi sowy, obiekty
klasy A zawieraj obiekty klasy B.
Dziedziczenie (czyli zwizek jest) wyraa zwizek pomidzy klas ogln i klas specjaln.
Na przykad klasa RushOrder (szybkie zamwienie) dziedziczy po klasie Order. Wyspecja-
lizowana klasa RushOrder ma specjalne metody do obsugi priorytetw i inn metod do
obliczania opat za transport, ale pozostae jej metody, jak dodawanie produktw i pobiera-
nie opat, s odziedziczone po klasie Order. Oglnie rzecz biorc, jeli klasa A rozszerza
klas B, klasa A dziedziczy metody po klasie B, ale ma wiksze moliwoci od klasy B (dzie-
dziczenie jest bardzo wanym zagadnieniem i zostao szczegowo opisane w nastpnym
rozdziale).
Wielu programistw rysuje diagramy klas jzyka UML (ang. Unified Modeling Language)
obrazujce powizania midzy klasami. Przykad takiego diagramu przedstawia rysunek 4.2.
Klasy s reprezentowane przez prostokty, a powizania maj posta strzaek z rnymi
dodatkami. Tabela 4.1 przedstawia najczciej uywane w UML typy strzaek.
Rysunek 4.2.
Diagram klas
Rozdzia 4. Obiekty i klasy 137
Konstruktor ma zawsze tak sam nazw jak klasa. Zatem konstruktor klasy Date ma nazw
Date. Aby utworzy obiekt klasy Date, naley uy konstruktora tej klasy i operatora new:
new Date()
To wyraenie tworzy nowy obiekt. Obiekt ten jest inicjowany aktualn dat i godzin.
Metod mona te wywoa na rzecz tworzonego obiektu. Jedn z metod klasy Date jest
toString. Zwraca ona reprezentacj acuchow daty. Poniej przedstawiono sposb wywo-
ania metody toString na rzecz tworzonego obiektu klasy Date:
String s = new Date().toString();
Rysunek 4.3 przedstawia zmienn obiektow birthday, ktra jest referencj do nowo utwo-
rzonego obiektu.
Rysunek 4.3.
Tworzenie
nowego obiektu
definiuje zmienn obiektow o nazwie deadline, ktra moe si odwoywa do obiektw typu
Date. Koniecznie trzeba pamita, e zmienna deadline nie jest obiektem ani nawet nie
odwouje si jeszcze do adnego obiektu. Obecnie nie mona na jej rzecz wywoywa ad-
nych metod klasy Date. Ponisza instrukcja:
s = deadline.toString(); // jeszcze nie
spowodowaaby bd kompilacji.
Konieczna jest uprzednia inicjacja zmiennej deadline. S dwie moliwoci. Mona oczywi-
cie inicjacji tej dokona za pomoc nowo utworzonego obiektu:
deadline = new Date();
W tej chwili obie zmienne s referencjami (odwouj si) do tego samego obiektu (zobacz
rysunek 4.4).
Trzeba sobie uwiadomi, e zmienna obiektowa nie jest obiektem, tylko referencj do
obiektu.
Rozdzia 4. Obiekty i klasy 139
Rysunek 4.4.
Zmienne
obiektowe
odwoujce si
do tego samego
obiektu
Warto kadej zmiennej obiektowej jest referencj do obiektu, ktry jest przechowywany
gdzie indziej. Warto zwracana przez operator new te jest referencj. Instrukcja typu:
Date deadline = new Date();
skada si z dwch czci. Wyraenie new Date() tworzy obiekt typu Date, a jego wartoci
jest referencja do tego nowo utworzonego obiektu. Referencja ta zostaje zapisana w zmien-
nej deadline.
Aby zaznaczy, e zmienna obiektowa nie odwouje si do adnego obiektu, naley jej war-
to ustawi na null.
deadline = null;
. . .
if (deadline != null)
System.out.println(deadline);
Chocia nie musimy tego wiedzie, by uywa klasy Date, czas jest reprezentowany przez
liczb milisekund (ujemn lub dodatni), ktry upyny od ustalonego momentu (tak zwa-
nej epoki). Tym momentem jest 1 stycznia 1970 o godzinie 00:00:00 UTC. UTC oznacza
Coordinated Universal Time naukowy standard wyraania czasu, ktry z przyczyn prak-
tycznych jest rwnoznaczny z lepiej znanym GMT, czyli Greenwich Mean Time.
Okazuje si jednak, e klasa Date nie jest zbyt uyteczna przy manipulacji datami. Projek-
tanci jzyka Java ustalili, e zapis daty w formacie 31 grudnia 1999, 23:59:59 jest z gry
przyjt konwencj, okrelan przez kalendarz. Ten konkretny zapis jest zgodny z najbar-
dziej rozpowszechnionym na wiecie kalendarzem gregoriaskim. Ten sam moment w cza-
sie zostaby cakiem inaczej przedstawiony w chiskim lub hebrajskim kalendarzu ksiy-
cowym, nie mwic ju kalendarzu marsjaskim.
140 Java. Podstawy
Oczywicie wskanik Date* nie jest zainicjowany, dopki nie uyjemy operatora new.
Skadnia w Javie jest prawie taka sama jak w C++.
Date* birthday = new Date(); // C++
Jeli skopiujemy jedn zmienn do innej zmiennej, obie zmienne bd si odwoyway do
tej samej daty bd wskazywa ten sam obiekt. Odpowiednikiem referencji null Javy
jest wskanik NULL w C++.
Wszystkie obiekty w Javie znajduj si na stercie (ang. heap). Jeli obiekt zawiera jak
zmienn obiektow, zmienna ta zawiera tylko wskanik do innego obiektu na stercie.
Wskaniki w C++ s rdem mnstwa bdw. atwo mona utworzy bdny wskanik
albo nieodpowiednio przydzieli pami. W Javie tych problemw nie ma. Jeli uyjemy
niezainicjowanego wskanika, interpreter z pewnoci zgosi bd, zamiast generowa
losowe wyniki. Zarzdzaniem pamici zajmuje si natomiast system zbierania nieuytkw.
C++ umoliwia implementacj obiektw, ktre automatycznie tworz kopie samych
siebie. Su do tego konstruktory kopiujce i operatory przypisania. Na przykad kopi
listy dowiza jest nowa lista dowiza o takiej samej zawartoci, lecz osobnym zbiorze
dowiza. Dziki temu mona projektowa klasy zachowujce si podobnie jak typy
wbudowane. W Javie konieczne jest uyciu metody clone, aby utworzy kopi obiektu.
Klasa Date zawiera kilka metod do porwnywania dwch momentw w czasie. Na przykad
metody before i after informuj, czy dany moment w czasie jest wczeniejszy, czy pniejszy
ni inny moment:
if (today.before(birthday))
System.out.println("Jest jeszcze czas, aby kupi prezent.");
Rozdzia 4. Obiekty i klasy 141
Klasa Date udostpnia te metody getDay, getMonth i getYear, ale ich stosowanie
jest odradzane. Metoda jest odradzana, jeli twrca biblioteki dojdzie do wniosku,
e metoda ta w ogle nie powinna bya powsta.
Metody te naleay do klasy Date, zanim twrcy biblioteki zdali sobie spraw, e lepiej
bdzie utworzy osobne klasy dla kalendarzy. Po wprowadzeniu klas kalendarzy metody
klasy Date zostay oznaczone jako odradzane (ang. deprecated). Mona ich nadal uy-
wa, ale kompilator bdzie zgasza niezbyt eleganckie ostrzeenia. Dobrze jest trzyma
si z dala od odradzanych metod, poniewa mog one zosta w przyszoci usunite
z biblioteki.
Klasa GregorianCalendar zawiera duo wicej metod ni klasa Date. Przede wszystkim ma
kilka przydatnych konstruktorw. Wyraenie:
new GregorianCalendar()
Mona utworzy obiekt ustawiony na pnoc okrelonej daty, podajc rok, miesic i dzie:
new GregorianCalendar(1999, 11, 31)
Obiekt klasy GregorianCalendar zawiera pola przechowujce dat, na ktr obiekt ten zostanie
ustawiony. Dziki hermetyzacji nie sposb odgadn, jakiej reprezentacji uywa ta klasa, nie
zagldajc do jej kodu rdowego, ale oczywicie dziki hermetyzacji nie ma to znaczenia.
Znaczenie maj metody udostpniane przez klas.
Zadaniem kalendarza jest obliczanie atrybutw, takich jak data, dzie tygodnia, miesica lub
roku, dla okrelonego punktu w czasie. Aby sprawdzi ktre z tych ustawie, naley posuy
si metod akcesora get klasy GregorianCalendar. Dostp do wybranych elementw mona
uzyska za pomoc staych zdefiniowanych w klasie Calendar, takich jak Calendar.MONTH
czy Calendar.DAY_OF_WEEK.
142 Java. Podstawy
Dodatkowo do obiektu kalendarza mona doda dowoln liczb dni, tygodni, miesicy itd.:
deadline.add(Calendar.MONTH, 3); // Przeniesienie terminu o 3 miesice.
Pomidzy metodami get oraz set i add jest zasadnicza rnica. Pierwsza z nich tylko sprawdza
stan obiektu i zwraca informacje o nim. Metody set i add zmieniaj stan obiektu. Metody, ktre
modyfikuj pola egzemplarza, nazywaj si mutatorami (ang. mutator method), a te, ktre
daj do nich tylko dostp, nosz nazw akcesorw (ang. accessor method).
Oglnie przyjta konwencja gosi, aby metody akcesora poprzedza przedrostkiem get,
a mutatora przedrostkiem set. Na przykad klasa GregorianCalendar zawiera metody getTime
i setTime, ktre odpowiednio pobieraj i ustawiaj punkt w czasie reprezentowany przez obiekt:
Date time = calendar.getTime();
calendar.setTime(time);
Podobnie, aby sprawdzi rok, miesic lub dzie obiektu klasy Date, naley utworzy obiekt
klasy GregorianCalendar, ustawi czas i wywoa metod get:
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
int year = calendar.get(Calendar.YEAR);
Rozdzia 4. Obiekty i klasy 143
Biecy dzie jest oznaczony gwiazdk (*). Jak wida, program musi wiedzie, jak obliczy
dugo miesica oraz dzie tygodnia.
Nastpnie ustawiamy zmienn d na pierwszy dzie miesica i pobieramy dzie tygodnia dla
tej daty:
d.set(Calendar.DAY_OF_MONTH, 1);
int weekday = d.get(Calendar.DAY_OF_WEEK);
Zwr uwag, e pierwszy wiersz kalendarza jest odpowiednio wcity, aby pierwszy dzie
miesica by przyporzdkowany do odpowiedniego dnia tygodnia. Trudnoci mog wynik-
n z tego, e w USA tydzie zaczyna si od niedzieli i koczy w sobot, a w Europie od
poniedziaku i koczy w niedziel.
Aby zobaczy wynik tego programu dla innej lokalizacji, naley w pierwszej linijce
metody main doda poniszy wiersz kodu:
Locale.setDefault(Locale.ITALY);
{
indent++;
d.add(Calendar.DAY_OF_MONTH, -1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
Metoda getShortWeekdays zwraca acuch zoony ze skrtw nazw dni tygodnia w jzyku
uytkownika (np. Pn, Wt itd. po polsku). Indeksami w tej tablicy s numery dni tygodnia.
Ponisza ptla drukuje nagwek:
do
{
System.out.printf("%4s", weekdayNames[weekday]);
d.add(Calendar.DAY_OF_MONTH, 1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
while (weekday != firstDayOfWeek);
System.out.println();
Moemy teraz przej do drukowania pozostaej czci kalendarza. Robimy wcicie pierw-
szego wiersza i ustawiamy obiekt daty z powrotem na pocztek miesica. Wprowadzamy ptl,
w ktrej zmienna d przemierza wszystkie dni miesica.
W kadym powtrzeniu drukowana jest liczba. Jeli warto d odpowiada dzisiejszej dacie,
dodawana jest gwiazdka. Po dojciu do pocztku kolejnego tygodnia najpierw drukujemy
nowy wiersz. Nastpnie warto d ustawiamy na kolejny dzie:
d.add(Calendar.DAY_OF_MONTH, 1);
Kiedy si zatrzymamy? Nie wiadomo, czy miesic ma 31, 30, 29, czy 28 dni. Powtarzamy
ptl, dopki d mieci si w biecym miesicu.
do
{
. . .
}
while (d.get(Calendar.MONTH) == month);
/**
* @version 1.4 2007-04-07
* @author Cay Horstmann
*/
Rozdzia 4. Obiekty i klasy 145
d.set(Calendar.DAY_OF_MONTH, 1);
do
{
// Drukowanie dnia.
int day = d.get(Calendar.DAY_OF_MONTH);
System.out.printf("%3d", day);
}
while (d.get(Calendar.MONTH) == month);
// Ptla koczy dziaanie, jeli d jest pierwszym dniem nastpnego miesica.
Ten program ma na celu pokazanie, w jaki sposb mona wykorzysta interfejs klasy do
bardzo zoonych zada bez znajomoci szczegw implementacyjnych.
java.util.GregorianCalendar 1.1
GregorianCalendar()
Tworzy obiekt kalendarza reprezentujcy biec dat i godzin w domylnej
strefie czasowej i lokalizacji.
GregorianCalendar(int year, int month, int day)
GregorianCalendar(int year, int month, int day, int hour, int minutes,
int seconds)
Tworzy kalendarz gregoriaski z podanej daty i godziny.
Parametry: year rok
month miesic wartoci liczone s od zera
(np. stycze ma numer 0)
day dzie
hour godzina (od 0 do 23)
minutes minuta (od 0 do 59)
seconds sekunda (od 0 do 59)
int get(int field)
Pobiera warto okrelonego elementu.
Parametry: field Calendar.ERA, Calendar.YEAR, Calendar.MONTH,
Calendar.WEEK_OF_YEAR,
Calendar.WEEK_OF_MONTH,
Calendar.DAY_OF_MONTH, Calendar.DAY_OF_YEAR,
Calendar.DAY_OF_WEEK,
Calendar.DAY_OF_WEEK_IN_MONTH,
Rozdzia 4. Obiekty i klasy 147
Calendar.AM_PM, Calendar.HOUR,
Calendar.HOUR_OF_DAY,
Calendar.MINUTE, Calendar.SECOND,
Calendar.MILLISECOND,
Calendar.ZONE_OFFSET, Calendar.DST_OFFSET
void set(int field, int value)
Ustawia warto okrelonego elementu.
Parametry: field Jedna ze staych przyjmowanych przez metod get
value Nowa warto
void set(int year, int month, int day)
void set(int year, int month, int day, int hour, int minutes, int seconds)
Ustawia nowe wartoci elementw.
Parametry: year rok
month miesic wartoci liczone s od zera
(np. stycze ma numer 0)
day dzie
hour godzina (od 0 do 23)
minutes minuty (od 0 do 59)
seconds sekundy (od 0 do 59)
void add(int field, int amount)
Metoda wykonujca dziaania arytmetyczne na datach. Dodaje okrelon ilo
czasu do okrelonego pola czasu. Aby na przykad doda 7 dni do biecej daty
w kalendarzu, naley zastosowa wywoanie: c.add(Calendar.DAY_OF_MONTH, 7).
Parametry: field Element, ktry ma by zmodyfikowany
(za pomoc jednej ze staych metody get).
amount Liczba, o jak ma by zmieniona warto elementu
(moe by ujemna).
int getFirstDayOfWeek()
Pobiera pierwszy dzie tygodnia dla lokalizacji uytkownika, na przykad
Calendar.SUNDAY w USA.
void setTime(Date time)
Ustawia kalendarz na podany moment w czasie.
Parametr: time punkt w czasie
Date getTime()
Pobiera punkt w czasie reprezentowany przez aktualn warto obiektu kalendarza.
148 Java. Podstawy
java.text.DateFormatSymbols 1.1
String[] getShortWeekdays()
String[] getShortMonths()
String[] getWeekdays()
String[] getMonths()
Pobiera nazwy dni tygodnia lub miesicy dla obecnej lokalizacji. Jako indeksy
wykorzystuje stae dnia tygodnia i miesica klasy Calendar.
konstruktor1
konstruktor2
. . .
metoda1
metoda2
. . .
}
Przyjrzyjmy si poniszej, bardzo uproszczonej wersji klasy Employee, ktr mona wyko-
rzysta w firmie do napisania systemu obsugi listy pac.
class Employee
{
// pola
private String name;
private double salary;
private Date hireDay;
// konstruktor
public Employee(String n, double s, int year, int month, int day)
Rozdzia 4. Obiekty i klasy 149
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
// metoda
public String getName()
{
return name;
}
// kolejne metody
. . .
}
Zanim przejdziemy do dogbnej analizy klasy Employee, pokaemy jej zastosowanie w pro-
gramie, ktry przedstawia listing 4.2.
/**
* Ten program sprawdza dziaanie klasy Employee.
* @version 1.11 2004-02-19
* @author Cay Horstmann
*/
public class EmployeeTest
{
public static void main(String[] args)
{
// Wstawienie trzech obiektw pracownikw do tablicy staff.
Employee[] staff = new Employee[3];
class Employee
{
private String name;
private double salary;
private Date hireDay;
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// W klasie GregorianCalendar stycze ma numer 0.
hireDay = calendar.getTime();
}
W programie tym tworzymy tablic o nazwie Employee i wstawiamy do niej trzy obiekty
reprezentujce pracownikw:
Employee[] staff = new Employee[3];
Plik rdowy ma nazw EmployeeTest.java, poniewa nazwa pliku musi by taka sama jak
nazwa klasy publicznej. W jednym pliku moe by tylko jedna klasa publiczna i dowolna
liczba klas niepublicznych.
W trakcie kompilacji tego pliku kompilator utworzy dwa pliki klas: EmployeeTest.class
i Employee.class.
Aby uruchomi program, naley interpreterowi kodu bajtowego poda nazw klasy zawie-
rajcej metod main:
java EmployeeTest
Interpreter zaczyna wykonywanie kodu od metody main w klasie EmployeeTest. Program ten
z kolei tworzy trzy nowe obiekty Employee i pokazuje ich stan.
W przypadku tego stylu programowania s dwa sposoby kompilacji programu. Mona wywo-
a kompilator Javy z symbolem wieloznacznym:
javac Employee*.java
Ta druga opcja moe si wydawa nieco zaskakujca, biorc pod uwag, e plik Employee.java
nie jest w ogle jawnie kompilowany. Kiedy kompilator napotyka klas Employee w pliku
EmployeeTest.java, szuka pliku o nazwie Employee.class. Jeli go nie znajdzie, pobiera plik
o nazwie Employee.java i kompiluje go. Kompilator robi nawet co wicej: jeli znacznik
czasu pliku Employee.java jest nowszy ni pliku Employee.class, kompilator automatycznie
dokona jego ponownej kompilacji.
Osoby znajce uniksowe narzdzie make (lub jego odpowiednik w Windowsie, jak
np. nmake), mog traktowa kompilator Javy tak, jakby mia wbudowan funkcjo-
nalno tego narzdzia.
Wszystkie metody tej klasy s publiczne. Sowo kluczowe public oznacza, e dan metod
moe wywoa kada inna metoda z kadej klasy (cztery dostpne specyfikatory dostpu s
opisane w tym i kolejnym rozdziale).
Sowo kluczowe private oznacza, e do pl klasy Employee maj dostp tylko metody tej
klasy. adna metoda zewntrzna nie moe odczyta ani zmodyfikowa tych wartoci.
Pola w klasie mona oznaczy sowem kluczowym public, ale nie jest to zalecane.
Ich wartoci mogyby by odczytane i zmodyfikowane z kadego miejsca programu,
a to jest cakowicie sprzeczne z ide hermetyzacji. Kada metoda z kadej klasy moe
zmodyfikowa publiczne pole z naszego dowiadczenia wynika, e tak si dzieje
zawsze w najmniej oczekiwanym momencie. Zalecamy stosowanie zawsze specyfikatora
private dla pl klas.
Jak wida, konstruktor ma tak sam nazw jak klasa. Konstruktor ten dziaa, kiedy tworzony
jest obiekt klasy Employee, i nadaje polom okrelone przez programist pocztkowe wartoci.
wartoci pl bd nastpujce:
name = "James Bond";
salary = 100000;
hireDay = January 1, 1950;
Rozdzia 4. Obiekty i klasy 153
Konstruktory w Javie dziaaj tak samo jak w C++. Nie naley jednak zapomina,
e obiekty w Javie s przechowywane na stercie i e konstruktor musi by wywoy-
wany przy uyciu operatora new. Programici C++ czsto zapominaj o tym operatorze,
programujc w Javie:
Employee number007("James Bond", 100000, 1950, 1, 1);
// C++, nie Java.
Powyszy kod zadziaa w jzyku C++, ale nie w Javie.
Naley pamita, aby nie utworzy zmiennej lokalnej o takiej samej nazwie jak pole
klasy. Na przykad w poniszym kodzie wysoko pensji nie zostanie ustawiona:
public Employee(String n, double s, . . .)
{
String name = n; // bd
double salary = s; // bd
. . .
}
Konstruktor deklaruje dwie zmienne lokalne o nazwach name i salary. S one dostpne
wycznie w tym konstruktorze. Przesaniaj pola klasy o takich samych nazwach. Niekt-
rzy programici wliczajc autorw niniejszej ksiki pisz szybciej, ni myl,
poniewa maj nawyk dodawania nazwy typu. Jest to bardzo nieprzyjemny rodzaj bdu,
ktry trudno wytropi. Trzeba pamita, aby nie nada adnej zmiennej w metodzie
takiej samej nazwy jak zmiennej pola klasy.
salary += raise;
}
ustawia now warto zmiennej skadowej salary obiektu, na rzecz ktrego zostanie wywo-
ana. Spjrzmy na ponisze wywoanie:
number007.raiseSalary(5);
Metoda raiseSalary pobiera dwa parametry. Pierwszy z nich, zwany parametrem niejawnym
(ang. implicit parameter), to obiekt klasy Employee, ktry znajduje si przed nazw metody.
Drugi parametr, liczba w nawiasach za nazw metody, to parametr jawny (ang. explicit
parameter).
Jak wida, parametry jawne s wypisane w deklaracji metody, na przykad double byPercent.
Parametr niejawny nie pojawia si w deklaracji metody.
Niektrzy programici wol ten styl pisania kodu, poniewa wyranie odrnia on zmienne
skadowe od zmiennych lokalnych.
Czy nie byoby prociej, gdyby pola name, salary i hireDay byy publiczne? Wtedy nie
trzeba by byo tworzy dla nich oddzielnych metod.
Chodzi o to, e pole name jest tylko do odczytu. Po ustawieniu jego wartoci za pomoc kon-
struktora nie ma sposobu, aby t warto zmieni. W ten sposb zyskujemy gwarancj, e
pole name nigdy nie zostanie uszkodzone.
Pole salary nie jest tylko do odczytu, ale jego warto mona zmieni wycznie przy uyciu
metody raiseSalary. Jeli warto tego pola jest nieprawidowa, wiadomo, e trzeba poszu-
ka bdu w tej metodzie. Gdyby pole salary byo publiczne, rdo problemw z nim mo-
goby si znajdowa wszdzie.
Czasami konieczne jest pobranie i ustawienie wartoci skadowej obiektu. Do tego celu potrzebne
s trzy rzeczy:
prywatne pole,
publiczna metoda akcesora,
publiczna metoda mutatora.
To oznacza wicej pracy ni w przypadku utworzenia publicznego pola danych, ale metoda
ta ma te zalety.
Oczywicie metody akcesora i mutatora mog mie duo pracy z konwersj ze starej repre-
zentacji danych na now. Prowadzi to jednak do drugiej korzyci: metody mutatora mog
sprawdza bdy, podczas gdy kod, ktry po prostu przypisuje warto polu, takiej moli-
woci nie ma. Na przykad metoda setSalary moe pilnowa, aby pensja pracownika nie bya
nisza od zera.
Pamitaj, aby nie pisa metod akcesora, ktre zwracaj referencje do obiektw
zmienialnych. Zamalimy t zasad w klasie Employee, w ktrej metoda getHireDay
zwraca obiekt klasy Date:
class Employee
{
private Date hireDay;
. . .
public Date getHireDay()
{
return hireDay;
}
. . .
}
Powd nie jest oczywisty. Zarwno zmienna d, jak i harry.hireDay odwouj si do tego
samego obiektu (zobacz rysunek 4.5). Wywoanie metody mutatora na rzecz obiektu d
automatycznie powoduje modyfikacj prywatnego stanu obiektu klasy Employee!
Jeli konieczne jest zwrcenie referencji do modyfikowalnego obiektu, naley najpierw
sklonowa ten obiekt. Klon jest wiern kopi obiektu i jest przechowywany w osobnej
lokalizacji. Szczegowy opis technik klonowania znajduje si w rozdziale 6. Poniej znajduje
si poprawny kod:
class Employee
{
. . .
public Date getHireDay()
{
return hireDay.clone();
}
. . .
}
Naley pamita, aby do tworzenia kopii modyfikowalnych obiektw uywa metody clone.
Rozdzia 4. Obiekty i klasy 157
Metoda ta ma dostp do prywatnych skadowych obiektu harry, co nie jest adnym zasko-
czeniem. Uzyskuje jednak te dostp do prywatnych skadowych obiektu boss. Jest to dozwo-
lone, poniewa obiekt boss jest typu Employee, a metody klasy Employee maj dostp do pry-
watnych pl wszystkich obiektw tej klasy.
Aby utworzy prywatn metod, naley zamiast sowa kluczowego public uy sowa private.
Jeli metoda jest prywatna, nie ma obowizku dba o jej dostpno w przypadku zmiany
implementacji. Po wprowadzeniu zmian w sposobie reprezentacji danych jej implementacja
moe si okaza trudniejsza lub zupenie niepotrzebna. Chodzi o to, e jeli metoda jest
prywatna, wiadomo, e nikt jej nie uywa poza klas, i mona si jej bez obawy pozby.
Jeli metoda jest publiczna, nie mona jej usun, poniewa moe z niej korzysta jaki inny
fragment programu.
Kady obiekt klasy Employee bdzie mia wasne pole id, ale pole nextId bdzie wspdzielone
przez wszystkie obiekty tej klasy. Innymi sowy, jeli zostanie utworzonych 1000 obiektw
klasy Employee, powstanie 1000 skadowych obiektu o nazwie id po jednej dla kadego
obiektu, ale pole statyczne o nazwie nextId bdzie tylko jedno. Pole to bdzie istniao, nawet
jeli nie bdzie ani jednego obiektu klasy Employee. Pole to naley do klasy, a nie do kon-
kretnego obiektu.
Pole id obiektu harry zostaje ustawione na aktualn warto pola statycznego nextId, po
czym warto tego pola jest zwikszana o 1:
harry.id = Employee.nextId;
Employee.nextId++;
Gdyby sowo kluczowe static zostao pominite, PI byoby zwykym polem klasy Math.
To znaczy, e dostp do niego prowadziby poprzez obiekt klasy Math i kady obiekt tej
klasy miaby skadow PI.
Inn czsto uywan sta statyczn jest System.out. Jej deklaracja w klasie System wyglda
nastpujco:
public class System
{
. . .
public static final PrintStream out = . . .;
. . .
}
W klasie System dostpna jest metoda setOut, ktra umoliwia ustawienie staej
System.out na inny strumie. Jak to moliwe? Metoda setOut jest metod rodzim,
ktrej implementacja zostaa napisana w innym ni Java jzyku programowania. Metody
rodzime mog obchodzi mechanizmy kontrolne jzyka Java. Rozwizanie to jest jednak
bardzo rzadko stosowane i nie naley si nim posugiwa.
oblicza warto dziaania xa. Nie jest do tego potrzebny aden obiekt klasy Math. Innymi
sowy, nie ma parametru niejawnego.
Metody statyczne mona zapamita jako takie, ktre nie maj parametru this (w metodzie
niestatycznej parametr this odwouje si do parametru niejawnego metody zobacz pod-
rozdzia 4.3.5, Parametry jawne i niejawne).
Poniewa metody statyczne nie dziaaj na obiektach, za ich pomoc nie mona operowa
na skadowych obiektw. Maj natomiast dostp do pl statycznych swoich klas. Poniej
znajduje si przykad takiej metody statycznej:
Rozdzia 4. Obiekty i klasy 161
Czy mona w tej metodzie pomin sowo kluczowe static? Tak, ale wtedy do jej wywo-
ania potrzebna by bya referencja do obiektu typu Employee.
Pola i metody statyczne w Javie maj takie same przeznaczenie jak w jzyku C++.
Rnica pomidzy tymi jzykami polega w tym przypadku na skadni. W C++ dostp
do pola statycznego lub metody statycznej poza jej zakresem uzyskuje si przy uyciu
operatora ::, np. Math::PI.
Termin statyczny (ang. static) ma ciekaw histori. Zosta on po raz pierwszy uyty
w jzyku C do okrelenia zmiennej lokalnej, ktra nie znikaa po wyjciu z bloku. Wtedy
nazwa ta miaa sens zmienna pozostawaa w pamici i bya dostpna po ponownym
wejciu do bloku. Drugie znaczenie sowa kluczowego static dotyczyo zmiennych i funkcji
globalnych, do ktrych nie byo dostpu z innych plikw. Powodem uycia tego sowa bya
ch uniknicia wprowadzania nowego sowa kluczowego. W kocu w jzyku C++ sowo
static zyskao swoje trzecie znaczenie oznacza zmienne i funkcje, ktre nale do danej
klasy, ale nie nale do jej obiektw. To samo znaczenie ma niniejsze sowo w Javie.
Metoda main nie dziaa na adnym obiekcie kiedy program rozpoczyna dziaanie, nie ma
w nim jeszcze adnych obiektw. Jej zadaniem jest tworzenie i uruchamianie obiektw
wymaganych przez program.
Listing 4.3 przedstawia prost wersj klasy Employee zawierajc pole statyczne nextId
i metod statyczn getNextId. Program ten wstawia do tablicy trzy obiekty typu Employee
i drukuje informacje o reprezentowanych przez nie pracownikach. Na kocu drukuje kolejny
numer identyfikacyjny, aby zademonstrowa dziaanie metody statycznej.
Kada klasa moe zawiera metod main, co jest bardzo przydatne przy przeprowa-
dzaniu testw jednostkowych klas. Moemy na przykad wstawi metod main do
klasy Employee:
class Employee
{
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
. . .
public static void main(String[] args) // test jednostkowy
{
Employee e = new Employee("Romeo", 50000, 2003, 3, 31);
e.raiseSalary(10);
System.out.println(e.getName() + " " + e.getSalary());
}
. . .
}
Jeli klasa Employee wchodzi w skad wikszej aplikacji, uruchomienie tego programu
za pomoc poniszego polecenia:
java Aplikacja
class Employee
{
private static int nextId = 1;
name = n;
salary = s;
id = 0;
}
i
java StaticTest
lokalizacj zmiennej dostarczonej przez wywoujcego. W zwizku z tym metoda moe zmo-
dyfikowa warto zmiennej przekazanej przez referencj, ale nie moe tego zrobi ze zmienn
przekazan przez warto. Okrelenia wywoanie przez s standardowo uywane w ter-
minologii programistycznej do opisu parametrw metod i dotycz nie tylko Javy; jest jesz-
cze jeden termin tego typu wywoanie przez nazw (ang. call by name), ale ma on ju
tylko znaczenie historyczne, poniewa by stosowany w jzyku Algol jednym z najstar-
szych jzykw programowania wysokiego poziomu.
W Javie zawsze stosowane s wywoania przez warto. Oznacza to, e metoda otrzymuje
kopi wartoci wszystkich parametrw, a wic nie moe zmodyfikowa wartoci przekaza-
nych do niej zmiennych.
Bez wzgldu na to, jaka jest implementacja tej metody, wiadomo, e po jej wywoaniu warto
zmiennej percent nadal bdzie wynosia 10.
Przyjrzyjmy si tej sytuacji nieco uwaniej. Niech nasza metoda sprbuje potroi warto
swojego parametru:
public static void tripleValue(double x) // nie dziaa
{
x = 3 * x;
}
Wywoajmy t metod:
double percent = 10;
tripleValue(percent);
To jednak nie dziaa. Po wywoaniu metody warto zmiennej percent nadal wynosi 10.
Oto opis zdarze:
1. Zmienna x jest inicjowana kopi wartoci zmiennej percent (tzn. 10).
2. Warto zmiennej x jest potrojona teraz wynosi 30. Ale zmienna percent
ma nadal warto 10 (zobacz rysunek 4.6).
3. Metoda koczy dziaanie, a zmienna x nie jest ju uywana.
Wiemy ju, e metoda nie moe zmieni wartoci parametru typu podstawowego. Z para-
metrami obiektowymi jest inaczej. Mona z atwoci utworzy metod, ktra potraja pensj
pracownika:
public static void tripleSalary(Employee x) // dziaa
{
x.raiseSalary(200);
}
166 Java. Podstawy
Rysunek 4.6.
Modyfikacja
parametru
liczbowego
nie ma staego
efektu
Jak wida, implementacja metody, ktra zmienia stan parametru w postaci obiektu, jest atw
i czsto stosowan metod programowania. Prostota bierze si std, e metoda odbiera kopi
referencji do obiektu i zarwno orygina, jak i kopia odwouj si do tego samego obiektu.
Gdyby w Javie stosowane byy wywoania przez referencj dla obiektw, ta metoda dziaaaby:
Employee a = new Employee("Alicja", . . .);
Employee b = new Employee("Bartosz", . . .);
swap(a, b);
// Czy a odwouje si teraz do Bartosza, czy Alicji?
Wysiek ten idzie jednak na marne. Zmienne parametrowe x i y wychodz z uycia po zako-
czeniu metody. Oryginalne zmienne a i b nadal odwouj si do tych samych obiektw, do
ktrych odwoyway si przed wywoaniem metody (zobacz rysunek 4.8).
Powyszy opis problemu stanowi dowd na to, e jzyk programowania Java nie uywa
wywoa przez referencj dla obiektw. W zamian referencje do obiektw s przekazy-
wane przez warto.
168 Java. Podstawy
Oto zestawienie zasad dotyczcych tego, co mona, a czego nie mona robi z parametrami
metod w Javie:
Metoda nie moe zmodyfikowa parametru typu podstawowego (czyli bdcego
liczb lub wartoci logiczn).
Metoda moe zmieni stan obiektu przekazanego jako parametr.
Metoda nie moe sprawi, aby parametr obiektowy zacz si odwoywa
do nowego obiektu.
Powysze twierdzenia prezentuje program z listingu 4.4. Najpierw prbuje potroi warto
parametru liczbowego, co koczy si niepowodzeniem:
Testowanie tripleValue:
Przed: percent=10.0
Koniec metody: x=30.0
Po: percent=10.0
Po zakoczeniu dziaania metody stan obiektu, do ktrego odwouje si zmienna harry, jest
zmieniony. Jest to moliwe dziki temu, e metoda ta zmodyfikowaa stan obiektu poprzez
kopi referencji do niego.
Testowanie swap:
Przed: a=Alicja
Przed: b=Grzegorz
Koniec metody: x=Grzegorz
Koniec metody: y=Alicja
Po: a=Alicja
Po: b=Grzegorz
Jak wida, parametry x i y zostay zamienione, ale zmienne a i b pozostay bez zmian.
W C++ moliwe jest zarwno wywoanie przez warto, jak i referencj. Parametry
bdce referencjami oznaczane s symbolem &. Na przykad metody void triple
Value(double& x) czy void swap(Employee& x, Employee& y), ktre modyfikuj swoje
parametry, mona z atwoci zaimplementowa.
/*
* Test 2. Metody mog zmienia stan parametrw bdcych obiektami.
*/
System.out.println("\nTestowanie tripleSalary:");
Employee harry = new Employee("Grzegorz", 50000);
System.out.println("Przed: salary=" + harry.getSalary());
tripleSalary(harry);
System.out.println("Po: salary=" + harry.getSalary());
/*
* Test 3. Metody nie mog dodawa nowych obiektw do parametrw obiektowych.
*/
System.out.println("\nTestowanie swap:");
Employee a = new Employee("Alicja", 70000);
Employee b = new Employee("Grzegorz", 60000);
System.out.println("Przed: a=" + a.getName());
System.out.println("Przed: b=" + b.getName());
swap(a, b);
System.out.println("Po: a=" + a.getName());
System.out.println("Po: b=" + b.getName());
170 Java. Podstawy
4.6.1. Przecianie
Przypomnijmy, e klasa GregorianCalendar miaa wicej ni jeden konstruktor. Do wyboru
byy dwa:
GregorianCalendar today = new GregorianCalendar();
lub
GregorianCalendar deadline = new GregorianCalendar(2099, Calendar.DECEMBER, 31);
W Javie mona przeciy dowoln metod. W zwizku z tym peny opis metody
skada si z nazwy i typw argumentw. Informacje te nazywane s sygnatur
metody. Na przykad klasa String zawiera cztery metody publiczne o nazwie indexOf.
Oto ich sygnatury:
indexOf(int)
indexOf(int, int)
indexOf(String)
indexOf(String, int)
Okrelenie typu zwrotnego nie wchodzi w skad sygnatury metody. Oznacza to, e nie
mona utworzy dwch metod o takich samych nazwach i typach parametrw, ale r-
nych typach zwrotnych.
Wemy jako przykad klas Employee. Wyobramy sobie, e nie okrelilimy w konstrukto-
rze sposobu inicjacji niektrych jej pl. Domylnie pole salary miaoby warto 0, a pola
name i hireDay miayby wartoci null.
Nie jest to jednak dobre rozwizanie, poniewa w wyniku wywoania metody getName lub
getHireDay otrzymalibymy warto null, ktrej raczej nie oczekiwalibymy:
Date h = harry.getHireDay();
calendar.setTime(h); // Jeli h ma warto null, zostanie zgoszony wyjtek.
Konstruktor domylny jest stosowany, w przypadku gdy programista nie utworzy adnego
konstruktora. Konstruktor ten ustawia wszystkie pola na wartoci domylne. W zwizku
z tym wszystkie dane liczbowe bdce skadowymi obiektu miayby warto 0, wartoci
logiczne byyby ustawione na false, a zmienne obiektowe na null.
Jeli klasa ma przynajmniej jeden konstruktor, ale nie ma konstruktora domylnego, nie mona
tworzy jej obiektw bez podania odpowiednich parametrw konstrukcyjnych. Na przykad
pierwsza wersja klasy Employee na listingu 4.2 zawieraa jeden konstruktor:
Employee(String name, double salary, int y, int m, int d)
W przypadku tej klasy utworzenie obiektu z wartociami domylnymi nie byoby moliwe.
To znaczy, e ponisze wywoanie spowodowaoby bd:
e = new Employee();
Naley pamita, e konstruktor domylny jest dostpny tylko wtedy, gdy klasa
nie ma adnego innego konstruktora. Aby umoliwi tworzenie obiektw klasy,
ktra ma ju konstruktor, za pomoc widocznego poniej wywoania:
new NazwaKlasy()
Warto inicjujca nie musi by staa. W poniszym przykadzie pole jest inicjowane wywoa-
niem metody. Wemy pod uwag klas Employee, w ktrej kady pracownik ma swj iden-
tyfikator id. Pole to moe by inicjowane nastpujco:
class Employee
{
private static int nextId;
private int id = assignId();
. . .
private static int assignId()
{
int r = nextId;
nextId++;
return r;
}
. . .
}
Wad tej metody jest to, e stosowane w niej nazwy nic nie mwi o przeznaczeniu para-
metrw.
Jest to cakiem dobre rozwizanie. Ju na pierwszy rzut oka wiadomo, jakie jest przeznaczenie
kadego z parametrw.
Sowo kluczowe uyte w takim przypadku jest bardzo przydatne. Wsplny kod konstruktorw
wystarczy napisa tylko jeden raz.
public Employee()
{
name = "";
salary = 0;
}
. . .
}
Najpierw zostanie zainicjowane pole id w bloku inicjujcym, bez wzgldu na to, ktry
konstruktor zostanie wywoany. Blok inicjujcy jest wykonywany na pocztku, a po nim
instrukcje zawarte w konstruktorze.
Sposb ten nigdy nie jest niezbdny i nie jest zbyt powszechnie stosowany. Zazwyczaj pro-
ciej jest umieci kod inicjujcy wewntrz konstruktora.
W bloku inicjujcym mona ustawia wartoci pl, mimo e ich definicje znajduj
si dopiero w dalszej czci klasy. Aby jednak unikn cyklicznych definicji, nie
mona odczytywa wartoci pl, ktre s inicjowane pniej. Zasady te zostay szcze-
gowo opisane w sekcji 8.3.2.3 specyfikacji jzyka Java (http://docs.oracle.com/
javase/specs). Poniewa reguy te s na tyle skomplikowane, e nawet programici
kompilatorw mieli z nimi problemy wczesne wersje Javy zawieray subtelne bdy
w ich implementacji zalecamy umieszczanie blokw inicjujcych za definicjami pl.
Oczywicie dobrze jest tak zorganizowa swj kod inicjujcy, aby inny programista mg
go bez wikszych problemw zrozumie. Na przykad klasa, w ktrej konstruktory s uza-
lenione od kolejnoci deklaracji pl danych, byaby nieco dziwna i podatna na bdy.
Pole statyczne mona zainicjowa, podajc warto pocztkow lub korzystajc ze statycz-
nego bloku inicjujcego. Pierwszy z tych sposobw ju znamy:
private static int nextId = 1;
Jeli inicjacja pl statycznych klasy odbywa si za pomoc bardziej zoonego kodu, mona
si posuy statycznym blokiem inicjujcym.
Kod naley umieci w bloku opatrzonym etykiet static. Oto przykad: chcemy, aby numery
identyfikacyjne pracownikw zaczynay si od losowej liczby cakowitej mniejszej od 10 000.
Rozdzia 4. Obiekty i klasy 177
Inicjacja statyczna nastpuje w chwili pierwszego zaadowania klasy. Pola statyczne, podobnie
jak zmienne skadowe, przybieraj wartoci domylne 0, false lub null, jeli nie zostan im
nadane jawnie inne wartoci. Inicjatory pl statycznych i statyczne bloki inicjujce s wyko-
nywane w takiej kolejnoci, w jakiej znajduj si w deklaracji klasy.
W wyniku wywoania powyszej klasy za pomoc polecenia java Hello statyczny blok
inicjujcy wydrukuje napis Witaj, wiecie, a potem pojawi si paskudny komunikat
o bdzie informujcy o braku metody main. Mona unikn tego poajania, umieszczajc
na kocu bloku inicjujcego wywoanie System.exit(0).
Program na listingu 4.5 demonstruje w praktyce zagadnienia, ktre zostay omwione w tym
podrozdziale:
przecianie konstruktorw,
wywoanie innego konstruktora za pomoc sowa kluczowego this(),
konstruktor domylny,
zastosowanie bloku inicjujcego obiektw,
statyczny blok inicjujcy,
inicjacja zmiennych skadowych.
/**
* Ten program demonstruje techniki konstrukcji obiektw.
* @version 1.01 2004-02-19
* @author Cay Horstmann
*/
public class ConstructorTest
{
public static void main(String[] args)
{
// Wstawienie do tablic staff trzech obiektw klasy Employee.
178 Java. Podstawy
class Employee
{
private static int nextId;
public Employee(double s)
{
// Wywoanie konstruktora Employee(String, double).
this("Employee #" + nextId, s);
}
// Konstruktor domylny.
public Employee()
{
// Zmienna name zainicjowana wartoci "" patrz niej.
// Zmienna salary nie jest jawnie ustawiona inicjacja wartoci 0.
// Zmienna id jest inicjowana w bloku inicjujcym.
}
return name;
}
java.util.Random 1.0
Random()
Tworzy nowy generator liczb losowych.
int nextInt(int n) 1.2
Zwraca losow liczb z przedziau od 0 do n-1.
Oczywicie niektre obiekty korzystaj z innych zasobw ni pami, jak np. pliki lub uchwyty
do innych obiektw, ktre wykorzystuj zasoby systemowe. W takiej sytuacji koniecznie
trzeba zwrci do ponownego uytku wykorzystywany zasb, kiedy przestanie by potrzebny.
Do kadej klasy mona doda metod finalize. Jest ona wywoywana przed usuniciem
obiektu przez system zbierania nieuytkw. Nie naley jednak polega na metodzie finalize
do przywracania zasobw, ktrych jest mao, poniewa nigdy nie wiadomo, kiedy nastpi jej
wywoanie.
Jeli dany zasb musi by zamknity natychmiast po zakoczeniu jego uywania, trzeba
o to zadba we wasnym zakresie. Do tego celu suy metoda close, ktr programista
180 Java. Podstawy
wywouje w celu skasowania okrelonych zasobw i gdy skoczy prac z obiektem. W czci
11.2.4, Instrukcja try z zasobami, dowiesz si, jak sprawi, aby metoda ta bya wywoy-
wana automatycznie.
4.7. Pakiety
Wygodnym sposobem na organizacj pracy i oddzielenie wasnych klas od pozostaych jest
umieszczanie klas w tak zwanych pakietach (ang. packages).
Gwnym powodem stosowania pakietw jest ch uniknicia kolizji nazw klas. Przypumy,
e dwch niezalenych programistw wpadnie na wietny pomys napisania klasy o nazwie
Employee. Dopki obie te klasy znajduj si w osobnych pakietach, nie ma adnego konfliktu.
Aby zachowa unikatowo nazw pakietw, firma Sun zaleca stosowanie w tych nazwach
odwrconych domen internetowych (ktre s unikatowe). W obrbie takiego pakietu mona
nastpnie tworzy kolejne podpakiety. Na przykad jeden z programistw zarejestrowa
domen horstmann.com. Po odwrceniu otrzymujemy com.horstmann. Pakiet ten mona nastp-
nie podzieli na podpakiety o nazwach typu com.horstmann.corejava.
Dostp do klasy publicznej z innego pakietu mona uzyska na dwa sposoby. Pierwszy z nich
polega na dodaniu penej nazwy pakietu przed nazw kadej klasy. Na przykad:
java.util.Date today = new java.util.Date();
Ta metoda jest oczywicie bardzo pracochonna. Prostszy i czciej stosowany sposb polega
na uyciu instrukcji import. Instrukcja ta umoliwia stosowanie skrconego zapisu odwoa
do klas w pakiecie. Dziki jej zastosowaniu nie trzeba pisa penych nazw klas.
Mona zaimportowa cay pakiet lub tylko jedn klas, a instrukcje import powinny si
znajdowa na samym pocztku pliku rdowego (ale pod instrukcjami package). Na przy-
kad ponisza instrukcja importuje wszystkie klasy znajdujce si w pakiecie java.util:
import java.util.*;
Rozdzia 4. Obiekty i klasy 181
nie jest potrzebny przedrostek okrelajcy pakiet. Mona take zaimportowa tylko okrelon
klas z pakietu:
import java.util.Date;
Zapis java.util.* jest prostszy i nie wywiera adnego wpywu na rozmiar kodu. Jednak
dziki importowi poszczeglnych klas oddzielnie osoba czytajca kod moe si atwiej
zorientowa, ktre klasy s w uyciu.
W edytorze Eclipse dostpna jest opcja Organize Imports, ktr mona znale
w menu Source. Po jej uyciu takie importy pakietw jak java.util.* s auto-
matycznie zastpowane list importw konkretnych klas, np.:
import java.util.ArrayList;
import java.util.Date;
Funkcja ta jest niezwykle przydatnym narzdziem.
Naley jednak pamita, e symbolu * mona uy do importu tylko jednego pakietu. Zapis
java.* lub java.*.* do importu wszystkich pakietw z przedrostkiem java jest niedozwolony.
Kompilator nie wie, ktrej klasy o nazwie Date uy. Problem ten mona rozwiza, dodajc
instrukcj importu konkretnej klasy:
import java.util.*;
import java.sql.*;
import java.util.Date;
Co zrobi w sytuacji, w ktrej potrzebne s obie te klasy? Konieczne jest uywanie za ka-
dym razem penej nazwy pakietu z nazw klasy.
java.util.Date deadline = new java.util.Date();
java.sql.Date today = new java.sql.Date(...);
Lokalizacja klas w pakietach naley do kompilatora. Kod bajtowy w plikach klas zawsze
zawiera pene nazwy pakietw w odwoaniach do innych klas.
182 Java. Podstawy
Programici jzyka C++ czsto myl instrukcj import z dyrektyw #include. Nie
maj one ze sob nic wsplnego. W C++ konieczne jest uycie dyrektywy #include,
aby doczy zewntrzne deklaracje, poniewa kompilator C++ nie przeszukuje adnych
plikw z wyjtkiem tego, ktry kompiluje, i doczonych plikw nagwkowych. Kompilator
Java otworzy kady plik, jeli wskae mu si jego lokalizacj.
W Javie mona cakowicie pomin mechanizm import, ale to wymagaoby podawania
penych nazw klas, jak java.util.Date. W jzyku C++ dyrektyw #include nie da si
unikn.
Jedyna korzy, jaka pynie z uywania instrukcji import, to wygoda. Mona si odwoa
do klasy za pomoc nazwy, ktra jest krtsza ni pena nazwa pakietu. Na przykad po
dodaniu instrukcji import java.util.* (albo import.java.util.Date) do klasy java.
util.Date mona odwoywa si, piszc tylko Date.
Odpowiednikiem pakietw w jzyku C++ s przestrzenie nazw. Instrukcje Javy package
i import mona traktowa jako odpowiedniki dyrektyw namespace i using w C++.
metod i pl statycznych klasy System bdzie mona uywa bez przedrostka w postaci nazwy
tej klasy:
out.println("egnaj, wiecie!"); // tj. System.out
exit(0); // tj. System.exit
Wtpliwe jest, aby programici chcieli skraca nazwy System.out i System.exit, poniewa
wtedy kod byby mniej przejrzysty. Z drugiej strony kod:
sqrt(pow(x, 2) + pow(y, 2))
package com.horstmann.corejava;
public class Employee
{
. . .
}
Jeli na pocztku pliku nie ma instrukcji package, klasy znajdujce si w tym pliku nale
do pakietu domylnego (ang. default package). Pakiet domylny nie ma nazwy. Wszystkie
prezentowane do tej pory klasy naleay do tego pakietu.
Program na listingach 4.6 i 4.7 jest rozoony na dwa pakiety: klasa PackageTest naley do
pakietu domylnego, a klasa Employee do pakietu com.horstmann.corejava. W zwizku z tym
plik Employee.java musi si znajdowa w podkatalogu com/horstmann/corejava. Struktura
katalogw jest nastpujca:
. (katalog bazowy)
PackageTest.java
PackageTest.class
com/
horstmann/
corejava/
Employee.java
Employee.class
Aby skompilowa ten program, naley przej do katalogu bazowego i wykona polecenie:
javac PackageTest.java
/**
* Ten program demonstruje uycie pakietw.
* @author cay
* @version 1.11 2004-02-19
* @author Cay Horstmann
*/
public class PackageTest
{
public static void main(String[] args)
{
// Dziki instrukcji import nie ma koniecznoci stosowania penej nazwy
// com.horstmann.corejava.Employee.
Employee harry = new Employee("Hubert Kowalski", 50000, 1989, 10, 1);
harry.raiseSalary(5);
import java.util.*;
/**
* @version 1.10 1999-12-18
* @author Cay Horstmann
*/
public class Employee
{
private String name;
private double salary;
private Date hireDay;
Przypomnijmy sobie program z listingu 4.2. Klasa Employee nie jest tam zdefiniowana jako
publiczna. W zwizku z tym dostp do niej maj tylko inne klasy (np. EmployeeTest) znajdu-
jce si w tym samym pakiecie (tu domylnym). W przypadku klas to domylne dziaanie jest
korzystne, ale jeli chodzi o zmienne, to wybr ten nie by trafny. Zmienne musz by jawnie
186 Java. Podstawy
Naley zauway, e zmienna warningString nie jest prywatna! Oznacza to, e metody wszyst-
kich klas z pakietu java.awt maj do niej dostp i mog modyfikowa jej warto (np. ustawi
na acuch Zaufaj mi!). Ze zmiennej tej korzystaj jednak tylko metody nalece do klasy
Window, w zwizku z czym najlepszym rozwizaniem byoby oznaczy j sowem kluczowym
private. Prawdopodobnie programista pisa kod w popiechu i najzwyczajniej zapomnia
o modyfikatorze dostpu (nie podajemy nazwiska tego programisty kady moe sobie
sam zajrze do omawianego pliku rdowego).
Zadziwiajce jest to, e nigdy nie naprawiono tego bdu, mimo i pisalimy o nim
we wszystkich dziewiciu wydaniach tej ksiki. Najwidoczniej osoby zajmujce
si implementacj Javy nie czytaj naszych publikacji. Co wicej, w klasie przybyo z cza-
sem sporo nowych pl, ale tylko okoo poowa z nich ma modyfikator dostpu private.
Czy jest to jaki problem? To zaley, poniewa pakiety nie s zamknitymi jednostkami.
Oznacza to, e kady moe doda do pakietu swoje klasy. Oczywicie zoliwi lub niedo-
uczeni programici mog napisa kod, ktry bdzie modyfikowa zmienne o zasigu pakieto-
wym. Na przykad w pierwszych wersjach Javy mona byo z atwoci przemyci dodat-
kow klas do pakietu java.awt. Wystarczyo na pocztku jej kodu napisa:
package java.awt;
Nastpnie plik z tak klas trzeba byo umieci w podkatalogu java.awt na ciece klas i ju
dostp do wntrznoci pakietu java.awt stawa otworem. Ta sztuczka umoliwiaa ustawie-
nie acucha z ostrzeeniem (zobacz rysunek 4.9).
Rysunek 4.9.
Zmiana acucha
ostrzeenia
w oknie apletu
W JDK 1.2 wprowadzono zmiany w mechanizmie adujcym klasy (ang. class loader), aby
jawnie zabrania adowania klas uytkownika, ktrych pakiety maj nazwy zaczynajce si
od sowa java. Oczywicie klasy niestandardowe z tej ochrony nie skorzystaj. W zamian
udostpniono mechanizm piecztowania pakietw (ang. package sealing), ktrego zadaniem
Rozdzia 4. Obiekty i klasy 187
jest rozwizanie problemw z rnymi sposobami dostpu do pakietw. Jeli pakiet jest zapie-
cztowany, nie mona do niego doda adnych nowych klas. W rozdziale 10. opisujemy tech-
niki tworzenia plikw JAR zawierajcych pakiety zapiecztowane.
Pliki klas mona take przechowywa w plikach JAR (ang. Java archive). Plik JAR zawiera
skompresowane pliki klas i katalogi oraz pozwala zaoszczdzi miejsce i polepszy wydaj-
no. Wikszo niestandardowych bibliotek uywanych w programach ma posta co najmniej
jednego pliku JAR. W JDK take jest dostpna pewna liczba takich plikw, np. jre/lib/rt.jar,
ktry zawiera tysice klas bibliotecznych. Techniki tworzenia plikw JAR zostay opisane
w rozdziale 10.
Struktura plikw JAR jest zgodna z formatem ZIP. W zwizku z tym do pliku rt.jar
i kadego innego o takim rozszerzeniu mona zajrze przy uyciu dowolnego narz-
dzia ZIP.
3. Ustaw ciek klas. cieka klas to zbir lokalizacji, ktre mog zawiera pliki klas.
W systemie UNIX trzeba zastosowa symbol zastpczy dla *, aby unikn rozwinicia
powoki.
Wszystkie pliki JAR (ale nie .class) znajdujce si w katalogu archives s dodawane do
cieki klas.
Pliki biblioteczne wykonawcze (plik rt.jar i inne pliki JAR, ktre znajduj si w katalogach
jre/lib i jre/lib/ext) s zawsze przeszukiwane w celu znalezienia klas. Nie dodaje si ich
bezporednio do cieki klas.
cieka klas zawiera list wszystkich katalogw i plikw JAR, od ktrych naley zacz
szukanie klas. Przeanalizujmy nasz przykadow ciek klas:
/home/user/classdir:.:/home/user/archives/archive.jar
a w kodzie rdowym uyto klasy Employee. Najpierw kompilator prbuje kolejno znale
klasy java.lang.Employee (poniewa pakiet java.lang jest zawsze importowany domyl-
nie), java.util.Employee, com.horstmann.Employee i Employee w biecym pakiecie. Szuka
Rozdzia 4. Obiekty i klasy 189
wszystkich tych klas we wszystkich lokalizacjach wymienionych na ciece klas. Jeli kom-
pilator znajdzie wicej ni jedn z tych klas, zgasza bd kompilacji (poniewa klasy musz
by unikatowe, kolejno instrukcji import nie ma znaczenia).
Kompilator idzie nawet o krok dalej i sprawdza, czy plik rdowy klasy nie jest nowszy ni
skompilowany plik tej klasy. Jeli plik rdowy jest nowszy, jest on automatycznie kom-
pilowany jeszcze raz. Przypomnijmy, e z innych pakietw mona importowa tylko klasy
publiczne. Jeden plik rdowy moe zawiera tylko jedn klas publiczn, a nazwa tego pliku
i nazwa zawartej w nim klasy publicznej musz si ze sob zgadza. Dziki temu kompilator
nie ma problemw z lokalizacj plikw rdowych klas publicznych. Klasy niepubliczne
mona importowa z biecego pakietu. Ich definicje mog si znajdowa w plikach o innych
nazwach. Jeli zostanie zaimportowana klasa z biecego pakietu, kompilator przeszuka
wszystkie pliki rdowe znajdujce si w tym pakiecie w celu znalezienia definicji tej klasy.
lub
java -classpath c:\classdir;.;c:\archives\archive.jar MyProg.java
Cae polecenie musi si znajdowa w jednym wierszu. Dugie polecenia tego typu najlepiej
wstawia do skryptw powoki lub plikw wsadowych.
Ustawianie cieki za pomoc opcji -classpath jest metod preferowan, ale nie jedyn. Inny
sposb polega na ustawieniu zmiennej rodowiskowej CLASSPATH. Dokadna procedura zaley
od konkretnej powoki. W powoce Bourne Again (bash) naley uy nastpujcego polecenia:
export CLASSPATH=/home/user/classdir:.:/home/user/archives/archive.jar
W powoce C:
setenv CLASSPATH /home/user/classdir:.:/home/user/archives/archive.jar
W systemie Windows:
set CLASSPATH=c:\classdir;.;c:\archives\archive.jar
Profesjonaln dokumentacj moe stworzy kady, kto doda do kodu rdowego komentarze
zaczynajce si od specjalnej sekwencji znakw /**. Jest to bardzo wygodne rozwizanie,
poniewa umoliwia przechowywanie kodu i dokumentacji do niego w jednym miejscu. Jeli
kod i dokumentacja znajduj si w osobnych plikach, z czasem mog si pojawi midzy
nimi rozbienoci. Jednak dziki temu, e komentarze dokumentacyjne znajduj si w tym
samym pliku co kod rdowy, aktualizacja obu jest znacznie uatwiona.
Znaczenie sowa kluczowego protected zostao opisane w rozdziale 5., a interfejsy w roz-
dziale 6.
W tekcie komentarza mona uywa znacznikw HTML, takich jak <em></em> (sucy
do emfazy), <code></code> (sucy do oznaczania fragmentw kodu), <strong></strong>
(dajcy silne wyrnienie) czy nawet <img /> (do wstawiania obrazw). Powinno si jed-
nak unika nagwkw <h1> i poziomych kresek <hr>, poniewa mog wchodzi w interak-
cje z formatowaniem dokumentu.
4.9.4. Komentarze do pl
Komentarze s potrzebne tylko do pl publicznych, co na og oznacza zmienne statyczne.
Na przykad:
/**
* Kolor karo.
*/
public static final int HEARTS = 1;
@since tekst
Dodaje pozycj Since. Tekst to opis wersji, w ktrej wprowadzono dan funkcj.
Na przykad @since version 1.7.1.
@deprecated tekst
Dodaje komentarz informujcy, e dana klasa, metoda lub zmienna nie powinny
by uywane. Tekst powinien zawiera informacj o zamienniku. Na przykad:
@deprecated W zamian naley uywa <code>setVisible(true)</code>
Znacznikw @see mona wstawi kilka, ale wszystkie musz by w jednym miejscu.
194 Java. Podstawy
Dziaaniem programu javadoc mona sterowa za pomoc rozmaitych opcji wiersza polece.
Na przykad opcje -author i -version dodaj do dokumentacji to, co oznaczono znacznikami
@author i @version (przy domylnych ustawieniach znaczniki te s pomijane). Inna przydatna
opcja to -link, ktra dodaje cza do klas standardowych. Na przykad ponisze polecenie:
javadoc -link http://docs.oracle.com/javase/7/docs/api *.java
Opcja -linksource konwertuje wszystkie pliki rdowe na pliki HTML (brak kolorowania
skadni, ale s numery wierszy). Nazwa kadej klasy i metody jest zamieniana na odnonik
do rda.
Wicej moliwoci konfiguracyjnych daj tak zwane doclety (ang. doclets). Mona
napisa doclet umoliwiajcy generowanie dokumentacji w dowolnym formacie
innym ni HTML. Poniewa niewiele osb potrzebuje takiej moliwoci, po szczegowe
informacje na temat docletw odsyamy do dokumentacji internetowej, ktra znajduje
si pod adresem http://docs.oracle.com/javase/1.5.0/docs/guide/javadoc/doclet/
overview.html.
Dziki temu znacznie prociej jest wprowadza zmiany w adresach, jak na przykad
w przypadku koniecznoci dodania obsugi adresw midzynarodowych.
4. Nie wszystkie pola wymagaj wasnych metod dostpu i zmiany.
Po utworzeniu obiektu zmiany moe wymaga na przykad wysoko pensji
pracownika, ale z pewnoci nie data zatrudnienia. Ponadto obiekty czsto
zawieraj skadowe, do ktrych nikt spoza klasy nie powinien mie dostpu.
Moe to by na przykad tablica skrtw nazw wojewdztw w klasie Address.
5. Klasy o zbyt duej funkcjonalnoci powinny by dzielone.
Oczywicie ta wskazwka nie jest precyzyjna, poniewa kady ma inne zdanie
na temat tego, ile to jest za duo funkcji. Jeli jednak istnieje moliwo
podzielenia zoonej klasy na dwie prostsze, naley z tej moliwoci skorzysta
(z drugiej strony nie naley przesadza klasy zawierajce po jednej metodzie
to odchylenie w drug stron).
Ponisza klasa jest przykadem zego stylu projektowania:
public class CardDeck // zy styl
{
private int[] value;
private int[] suit;
public CardDeck() { . . . }
public void shuffle() { . . . }
public int getTopValue() { . . . }
public int getTopSuit() { . . . }
public void draw() { . . . }
}
public CardDeck() { . . . }
public void shuffle() { . . . }
Rozdzia 4. Obiekty i klasy 197
W tym rozdziale opisalimy podstawowe informacje dotyczce obiektw i klas, dziki kt-
rym Java jest jzykiem obiektowym. Aby jednak jzyk by w peni obiektowy, musi obsu-
giwa dziedziczenie i polimorfizm. Tym wasnociom Javy zosta powicony nastpny
rozdzia.
198 Java. Podstawy
5
Dziedziczenie
W tym rozdziale:
Klasy, nadklasy i podklasy
Klasa bazowa Object
Klasa ArrayList
Obiekty osonowe i automatyczne opakowywanie typw
Metody ze zmienn liczb parametrw
Klasy wyliczeniowe
Refleksja
Porady projektowe dotyczce dziedziczenia
Rozdzia 4. wprowadzi pojcia klas i obiektw. Ten rozdzia wprowadza kolejne zagadnienie
o fundamentalnym znaczeniu dla programowania obiektowego dziedziczenie (ang. inhe-
ritance). Z zaoenia technika ta umoliwia tworzenie nowych klas na bazie klas ju istnie-
jcych. Klasa, ktra dziedziczy po innej klasie, przejmuje jej metody i pola oraz dodaje
wasne metody i pola, ktre su przystosowaniu do nowych zada. Technika ta ma kluczowe
znaczenie dla programowania w Javie.
Dodatkowo rozdzia ten opisuje refleksj (ang. reflection), czyli technik inspekcji klas
w trakcie dziaania programu. Mimo e refleksja daje ogromne moliwoci, jest bez wt-
pienia technik skomplikowan. Poniewa ma ona wiksze znaczenie dla twrcw narzdzi
ni programistw aplikacji, t cz rozdziau mona przeczyta pobienie i wrci do niej
w razie potrzeby.
200 Java. Podstawy
Sowo kluczowe extends oznacza, e tworzona jest nowa klasa na podstawie istniejcej klasy.
Klasa istniejca nazywana jest nadklas (ang. superclass), klas bazow (ang. base class)
lub klas macierzyst (ang. parent class). Nowo utworzona klasa nazywa si podklas
(ang. subclass), klas pochodn (ang. derived class) lub klas potomn (ang. child class).
Wikszo programistw Javy uywa terminw nadklasa i podklasa, ale niektrym
bardziej odpowiada analogia klasy macierzystej i potomnej, ktra bardzo dobrze pasuje do
koncepcji dziedziczenia.
Klasa Employee jest nadklas, ale nie ze wzgldu na to, e jest wysza rang albo ma wiksz
funkcjonalno. W rzeczywistoci jest odwrotnie: podklasa ma wiksz funkcjonalno ni
nadklasa. Bdzie mona si o tym przekona, kiedy przejdziemy do analizy klasy Manager,
ktra zawiera wicej danych i ma wiksz funkcjonalno ni jej nadklasa Employee.
Klasa Manager zawiera dodatkowe pole do przechowywania dodatku do pensji i now metod
do ustawiania jego wysokoci:
class Manager extends Employee
{
private double bonus;
. . .
public void setBonus(double b)
{
bonus = b;
}
}
Powyszego pola i powyszej metody nie wyrnia nic szczeglnego. Po utworzeniu obiektu
klasy Manager mona na jego rzecz wywoa metod setBonus:
Manager boss = . . .;
boss.setBonus(5000);
Oczywicie na rzecz obiektu klasy Employee nie mona wywoa metody setBonus. Nie ma jej
wrd metod tej klasy.
Natomiast na rzecz obiektw klasy Manager mona wywoywa metody getName i getHireDay,
mimo e nie zostay one zdefiniowane bezporednio w tej klasie. S dostpne, poniewa
zostay odziedziczone po klasie Employee.
Podobnie zostay odziedziczone pola name, salary i hireDay. Kady obiekt klasy Manager
ma cztery pola: name, salary, hireDay i bonus.
W definicji klasy rozszerzajcej inn klas konieczne jest podanie tylko rnic pomidzy
tymi klasami. Metody oglnego przeznaczenia naley umieszcza w nadklasie, a metody
bardziej wyspecjalizowane w podklasie. Wydzielanie wsplnego kodu i umieszczanie go
w nadklasie jest czsto stosowan technik programowania obiektowego.
Niektre metody obecne w klasie nadrzdnej s niewaciwe dla klasy Manager. Jest tak w przy-
padku metody getSalary, ktra powinna zwraca sum podstawy wynagrodzenia i dodatku.
Konieczne jest napisanie nowej metody przesaniajcej (ang. override) metod z nadklasy:
class Manager extends Employee
{
. . .
public double getSalary()
{
. . .
}
. . .
}
Jak powinna wyglda implementacja tej metody? Na pierwszy rzut oka wydaje si to pro-
ste wystarczy zwrci sum pl salary i bonus:
public double getSalary()
{
return salary + bonus; // nie dziaa
}
202 Java. Podstawy
Tak si jednak nie da. Metoda getSalary klasy Manager nie ma bezporedniego dostpu do
prywatnych pl nadklasy Employee. Oznacza to, e metoda getSalary klasy Manager nie ma
bezporedniego dostpu do pola salary, mimo e kady obiekt tej klasy zawiera pole o nazwie
salary. Tylko metody klasy Employee maj dostp do tych prywatnych pl. Jeli metody
klasy Manager chc uzyska do nich dostp, musz zrobi to co wszystkie inne metody
uy interfejsu publicznego. W tym przypadku konieczne jest uycie metody getSalary klasy
Employee.
Sprbujmy jeszcze raz. Zamiast bezporednio odwoywa si do pola salary, uyjemy metody
getSalary:
public double getSalary()
{
double baseSalary = getSalary(); // nadal nie dziaa
return baseSalary + bonus;
}
Problem polega na tym, e metoda getSalary wywouje sama siebie, poniewa klasa Manager
zawiera metod o takiej nazwie (dokadniej mwic, jest to ta metoda, ktr prbujemy
zaimplementowa). W ten sposb powstaa nieskoczona seria wywoa jednej metody, ktra
prowadzi do zaamania programu.
wywoa metod getSalary klasy Employee. Poniej znajduje si poprawna wersja metody
getSalary w klasie Manager:
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
Wiemy ju, e do podklasy mona dodawa nowe pola oraz dodawa lub przesania metody
z nadklasy. Dziedziczenie nie umoliwia natomiast pozbycia si adnych metod ani pl.
W Javie do wywoywania metod nadklasy suy sowo kluczowe super. W C++ w takiej
sytuacji naley uy nazwy nadklasy z operatorem ::. Na przykad metoda getSalary
klasy Manager wywoywaaby Employee::getSalary zamiast super.getSalary.
Rozdzia 5. Dziedziczenie 203
Jeli konstruktor podklasy nie wywouje jawnie konstruktora nadklasy, wywoywany jest
konstruktor domylny (bezparametrowy) nadklasy. Jeli nadklasa nie ma konstruktora domyl-
nego, a konstruktor podklasy nie wywouje jawnie adnego innego konstruktora nadklasy,
kompilator zgosi bd.
W jzyku C++ nie uywa si w konstruktorze sowa kluczowego super, tylko skadni
listy inicjujcej nadklasy. Konstruktor Manager w C++ wygldaby nastpujco:
Manager::Manager(String n, double s, int year, int month, int day) // C++
: Employee(n, s, year, month, day)
{
bonus = 0;
}
Oto praktyczny przykad obrazujcy powysze rozwaania. Utworzymy nowy obiekt klasy
Manager i ustawimy dodatek dla kierownika:
Manager boss = new Manager("Karol Parol", 80000, 1987, 12, 15);
boss.setBonus(5000);
Moliwo odwoywania si przez obiekty (jak zmienna e) do wielu rnych typw nosi
nazw polimorfizmu (ang. polymorphism). Automatyczny dobr odpowiednich metod w trak-
cie dziaania programu nazywa si wizaniem dynamicznym (ang. dynamic binding). Oba
te zagadnienia zostay szczegowo opisane w tym rozdziale.
Listing 5.1 przedstawia program demonstrujcy rnic naliczania pensji dla obiektw klasy
Employee (listing 5.2) i Manager (listing 5.3).
/**
* Ten program demonstruje dziedziczenie.
* @version 1.21 2004-02-21
* @author Cay Horstmann
*/
Rozdzia 5. Dziedziczenie 205
staff[0] = boss;
staff[1] = new Employee("Henryk Kwiatek", 50000, 1989, 10, 1);
staff[2] = new Employee("Artur Kwiatkowski", 40000, 1990, 3, 15);
package inheritance;
import java.util.Date;
import java.util.GregorianCalendar;
acuchw dziedziczenia moe by wiele. Na przykad klas Employee mog rozszerza klasy
Programmer i Secretary, ktre nie musz mie nic wsplnego z klas Manager (albo ze sob
nawzajem). Struktura ta moe mie dowoln dugo.
Rozdzia 5. Dziedziczenie 207
Rysunek 5.1.
Hierarchia
dziedziczenia
klasy Employee
5.1.2. Polimorfizm
W podjciu decyzji dotyczcej tego, czy w danym przypadku zastosowa dziedziczenie,
pomaga prosta zasada. Regua jest mwi, e kady obiekt podklasy jest obiektem nadklasy.
Na przykad kady kierownik jest pracownikiem. W zwizku z tym klasa Manager moe by
podklas klasy Employee. Oczywicie twierdzenia tego nie mona odwrci nie kady
pracownik jest kierownikiem.
Regu relacji jest mona take sformuowa jako zasad zamienialnoci (ang. substitution
principle). Zasada ta gosi, e wszdzie tam, gdzie mona uy obiektu nadklasy, mona uy
obiektu podklasy.
W tym przypadku zmienne staff[0] i boss odwouj si do tego samego obiektu. Jednak
dla kompilatora staff[0] jest obiektem wycznie klasy Employee.
Typ zadeklarowany zmiennej staff[0] to Employee, a metoda setBonus nie jest obecna w tej
klasie.
Nie mona przypisa referencji nadklasy do zmiennej podklasy. Na przykad ponisze przypi-
sanie jest niedozwolone:
Manager m = staff[i]; // bd
Powd jest jasny nie wszyscy pracownicy s kierownikami. Gdyby powysze przypisa-
nie udao si i zmienna m byaby referencj do obiektu klasy Employee, ktry nie jest kie-
rownikiem, to moliwe byoby te wywoanie m.setBonus(), a to z kolei spowodowaoby
bd czasu wykonania.
Mwi si, e obie metody getKolega maj kowariantne typy zwrotne (ang. covariant
return types).
3. Jeli metoda jest prywatna, statyczna lub finalna albo jest konstruktorem, kompilator
wie, ktr dokadnie metod wywoa (modyfikator final jest opisany w kolejnym
podrozdziale). Nazywa si to wizaniem statycznym (ang. static binding).
W przeciwnym przypadku to, ktra metoda zostanie wywoana, zaley
od rzeczywistego typu parametru niejawnego; musi te by zastosowane wizanie
dynamiczne w trakcie dziaania programu. W naszym przykadzie kompilator
wygenerowaby instrukcj wywoujc metod f(String) za pomoc wizania
dynamicznego.
210 Java. Podstawy
Przeanalizujmy ten proces na przykadzie wywoania e.getSalary() z listingu 5.1. Obiekt e jest
typu Employee. Klasa Employee zawiera tylko jedn metod o nazwie getSalary, ktra nie ma pa-
rametrw. Dziki temu w tym przypadku nie ma problemu z rozstrzyganiem przeciania.
Poniewa metoda getSalary nie jest prywatna, statyczna ani finalna, jest wizana dynamicz-
nie. Maszyna wirtualna tworzy tabele metod dla klas Employee i Manager. Tabela Employee
wskazuje, e wszystkie jej metody s zdefiniowane w samej klasie Employee:
Employee:
getName() -> Employee.getName()
getSalary() -> Employee.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(double)
To jednak nie wszystko. Jak si niebawem dowiemy, klasa Employee ma nadklas Object,
po ktrej dziedziczy kilka metod. Na razie ignorujemy te dodatkowe metody.
Tabela metod klasy Manager wyglda nieco inaczej. Trzy metody s odziedziczone, jedna
przedefiniowana, a jedna zostaa dodana.
Manager:
getName() -> Employee.getName()
getSalary() -> Manager.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(double)
setBonus(double) -> Manager.setBonus(double)
Kiedy metoda jest przesaniana, jej odpowiednik w podklasie musi mie przynajm-
niej tak sam widoczno jak orygina. Jeli metoda w nadklasie jest publiczna,
w podklasie rwnie musi by publiczna. Pominicie specyfikatora public w metodzie
podklasy jest czstym bdem. W takim przypadku kompilator informuje, e usiowano
zastosowa niszy przywilej dostpu.
Finalna moe te by metoda w klasie. W takim przypadku nie mona jej przesoni w ad-
nej z podklas (wszystkie metody w klasie finalnej s finalne). Na przykad:
class Employee
{
. . .
public final String getName()
{
return name;
}
. . .
}
Przypomnijmy, e pola rwnie mog by finalne. Warto takiego pola nie moe
by zmieniana po utworzeniu obiektu. Jeli klasa jest finalna, tylko jej metody s
automatycznie finalne, nie dotyczy to pl.
Jest tylko jeden powd, dla ktrego warto zdefiniowa klas lub metod jako finaln: aby
zapewni, e adna podklasa nie zmieni semantyki. Na przykad metody getTime i setTime
klasy Calendar s finalne. Oznacza to, e projektanci tej klasy wzili na siebie ciar odpowie-
dzialnoci za konwersj pomidzy klas Date a stanem kalendarza. adna podklasa nie powinna
mie moliwoci mieszania si w to. Klasa String te jest finalna. Oznacza to, e nie mona
utworzy jej podklasy. Innymi sowy, jeli mamy referencj do obiektu String, wiemy, e
jest to obiekt klasy String i nic innego.
212 Java. Podstawy
Na pocztku istnienia Javy niektrzy programici uywali sowa kluczowego final w nadziei,
e unikn narzutu powodowanego przez wizanie dynamiczne. Jeli metoda nie jest prze-
sonita i jest krtka, kompilator moe zoptymalizowa jej wywoywanie poprzez proces
polegajcy na wstawieniu jej kodu w miejsce wywoania (ang. inlining). Na przykad wywo-
anie metody e.getName() zostaoby zastpione dostpem do pola e.name. Jest to godna uwagi
poprawa procesory nie przepadaj za rozgazianiem, poniewa stoi ono w sprzecznoci
z ich strategi pobierania zawczasu kolejnej funkcji podczas przetwarzania innej. Jeli jed-
nak metoda getName moe by przesonita w innej klasie, kompilator nie moe zastosowa
wstawiania kodu, poniewa nie wie, jak dziaa ta przesonita wersja.
5.1.5. Rzutowanie
Przypomnijmy z rozdziau 3., e proces wymuszania konwersji pomidzy dwoma typami
nazywa si rzutowaniem (ang. casting). W Javie dostpna jest specjalna notacja oznaczaj-
ca rzutowanie. Na przykad:
double x = 3.405;
int nx = (int) x;
Podobnie jak od czasu do czasu konieczna jest konwersja typu double na int, tak samo bywa,
e trzeba przekonwertowa referencj do obiektu jednej klasy na referencj do obiektu innej
klasy. Do rzutowania referencji do obiektw uywa si podobnej notacji jak do rzutowania
typw liczbowych. Nazw klasy docelowej naley umieci w nawiasach i wstawi przed
referencj, ktra ma by rzutowana. Na przykad:
Manager boss = (Manager) staff[0];
Tego typu rzutowanie moe by potrzebne tylko w jednego rodzaju sytuacji aby w peni
wykorzysta obiekt, ktrego typ zosta chwilowo zgubiony. Na przykad w klasie TestManager
tablica staff musiaa by typu Employee, poniewa niektre z przechowywanych w niej
obiektw reprezentoway zwykych pracownikw. Aby uzyska dostp do nowych skado-
wych obiektw klasy Manager, konieczne by byo ich przekonwertowanie z powrotem na
Rozdzia 5. Dziedziczenie 213
Wiadomo, e kady obiekt w Javie ma typ. Typ ten okrela rodzaj obiektu, do ktrego odwo-
uje si zmienna, i wskazuje, co obiekt ten moe robi. Na przykad zmienna staff[i] odwouje
si do obiektu klasy Employee (a wic moe take odwoywa si do obiektu klasy Manager).
Kompilator sprawdza, czy programista nie wymaga zbyt wiele, zapisujc warto w zmiennej.
Jeli przypisze referencj do obiektu podklasy do zmiennej obiektowej nadklasy, zmniejsza
swoje wymagania, wic kompilator bez problemu si na to zgodzi. Jeli jednak referencja do
obiektu nadklasy zostanie przypisana zmiennej obiektu podklasy, zwiksza swoje wymagania.
W takim przypadku konieczne jest zastosowanie rzutowania, ktre umoliwi sprawdzenie
tych wymaga w trakcie dziaania programu.
I wreszcie, kompilator nie pozwoli na rzutowanie, ktre nie ma szans powodzenia. Na przykad
rzutowanie:
Date c = (Date) staff[1];
Podsumowujc:
Rzutowanie jest moliwe wycznie w obrbie hierarchii dziedziczenia.
Przy rzutowaniu typu nadklasy na typ podklasy naley sprawdzi wykonalno
operacji za pomoc operatora instanceof.
Test:
x instanceof C
nie wygeneruje wyjtku, jeli zmienna x ma warto null, tylko zwrci warto false.
Sens tego zachowania polega na tym, e skoro null oznacza, i referencja nie wska-
zuje na aden obiekt, z pewnoci nie odwouje si do obiektu klasy C.
214 Java. Podstawy
Faktem jest, e konwersja typu obiektu za pomoc rzutowania nie jest z reguy dobrym pomy-
sem. W naszym przykadzie rzadko potrzebne jest rzutowanie obiektu klasy Employee na obiekt
klasy Manager. Metoda getSalary dziaa prawidowo na obiektach obu tych klas. Wizanie
dynamiczne, na ktrym opiera si polimorfizm, automatycznie lokalizuje odpowiedni metod.
Jedyna sytuacja, w ktrej potrzebne jest takie rzutowanie, ma miejsce wtedy, gdy chcemy uy
metody dostpnej tylko dla obiektw klasy Manager, czyli setBonus. Jeli wywoanie metody
setBonus na rzecz obiektw klasy Employee okae si konieczne, naley rozway moli-
wo, e nadklasa zostaa le zaprojektowana. Niewykluczone, e dobrym rozwizaniem okae
si dodanie do nadklasy metody setBonus. Pamitajmy, e do przerwania dziaania programu
wystarczy jeden nieprzechwycony wyjtek. Zasadniczo najlepiej jest wystrzega si rzuto-
wania i operatora instanceof.
jest odpowiednikiem:
Manager* boss = dynamic_cast<Manager*>(staff[1]); // C++
Jest tylko jedna rnica. Jeli rzutowanie nie powiedzie si, nie powstaje obiekt null,
ale wyjtek. W tym sensie przypomina to znane z C++ rzutowanie referencji. Jest to
jedna z bolczek. W C++ mona zadba o test typu i konwersj w jednej operacji.
Manager* boss = dynamic_cast<Manager*>(staff[1]); // C++
if (boss != NULL) . . .
Po co w ogle zaprzta sobie gow takim poziomem abstrakcji? Niektre cechy ma kada
osoba, np. nazwisko. Zarwno studenci, jak i pracownicy maj nazwiska, a wic wprowadze-
nie wsplnej nadklasy umoliwia wydzielenie metody getName i przeniesienie jej na wyszy
poziom hierarchii dziedziczenia.
Rozdzia 5. Dziedziczenie 215
Rysunek 5.2.
Diagram
dziedziczenia
klasy Person
i jej podklas
Dodamy teraz now metod o nazwie getDescription, ktra zwraca krtki opis osoby, np.:
pracownik zarabiajcy 50 000,00 z
student specjalizacji informatyka
Implementacja tej metody dla klas Employee i Student jest atwa. Jakie natomiast informacje
mona poda w klasie Person? Klasa ta ma jedynie informacje na temat nazwiska osoby.
Oczywicie mona zaimplementowa metod Person.getDescription(), ktra zwraca pusty
acuch. Istnieje jednak lepsze rozwizanie. Dziki uyciu sowa kluczowego abstract
w ogle nie ma potrzeby implementowania tej metody.
public abstract String getDescription();
// nie jest potrzebna adna implementacja
Poza metodami abstrakcyjnymi klasy abstrakcyjne mog zawiera pola i metody konkretne.
Na przykad klasa Person przechowuje nazwisko osoby i ma metod konkretn, ktra zwraca
te dane.
abstract class Person
{
private String name;
public Person(String n)
{
name = n;
}
public abstract String getDescription();
public String getName()
{
return name;
}
}
Metody abstrakcyjne peni rol symbolu zastpczego dla metod, ktre s implementowane
w podklasach. Przy rozszerzaniu klasy abstrakcyjnej programista ma do wyboru jedn z dwch
opcji: moe pozostawi niezdefiniowane niektre lub wszystkie metody nadklasy wtedy
podklasa rwnie musi by abstrakcyjna, albo zdefiniowa wszystkie metody i wtedy pod-
klasa nie jest abstrakcyjna.
Jako przykad zdefiniujemy klas Student, ktra bdzie rozszerzaa abstrakcyjn klas Person
i implementowaa metod getDescription. Poniewa adna z metod klasy Student nie jest
abstrakcyjna, klasa ta rwnie nie musi by abstrakcyjna.
Klas mona zdefiniowa jako abstrakcyjn, nawet jeli nie zawiera adnych metod abstrak-
cyjnych.
Nie mona tworzy obiektw klas abstrakcyjnych. To znaczy, e jeli klasa ma w deklaracji
sowo abstract, nie moe mie obiektw. Na przykad ponisze wyraenie:
new Person("Wincenty Witos")
Warto zauway, e mona tworzy zmienne obiektowe klas abstrakcyjnych, ale musz si
one odwoywa do obiektw nieabstrakcyjnych podklas. Na przykad:
Person p = new Student("Wincenty Witos", "Ekonomia");
Klasa Student zawiera definicj metody getDescription. W zwizku z tym wszystkie metody
tej klasy s konkretne, czyli nie jest ona abstrakcyjna.
Program przedstawiony na listingach 5.4, 5.5, 5.6 i 5.7 definiuje abstrakcyjn nadklas
o nazwie Person i dwie konkretne podklasy o nazwach Employee i Student. Do tablicy refe-
rencji typu Person wstawiane s obiekty klas Employee i Student:
Person[] people = new Person[2];
people[0] = new Employee(. . .);
people[1] = new Student(. . .);
/**
* Ten program demonstruje klasy abstrakcyjne.
* @version 1.01 2004-02-21
* @author Cay Horstmann
*/
public class PersonTest
{
public static void main(String[] args)
{
Person[] people = new Person[2];
public Person(String n)
{
name = n;
}
return name;
}
}
import java.util.Date;
import java.util.GregorianCalendar;
/**
* @param n imi i nazwisko studenta
Rozdzia 5. Dziedziczenie 219
Poniszy fragment kodu odpowiada za wydruk imion i nazwisk oraz opisw powyszych
obiektw:
for (Person p : people)
System.out.println(p.getName() + ", " + p.getDescription());
Czy nie jest to wywoanie niezdefiniowanej metody? Naley pamita, e zmienna p nie
odwouje si nigdy do obiektu Person, poniewa nie mona utworzy obiektu klasy abstrak-
cyjnej Person. Zmienna p zawsze odwouje si do obiektu konkretnej podklasy, jak Employee
lub Student. Dla tych obiektw metoda getDescription jest zdefiniowana.
Czy mona by byo pomin abstrakcyjn metod nadklasy abstrakcyjnej Person i zdefinio-
wa metody getDescription w podklasach Employee i Student? Gdybymy tak zrobili, nie-
moliwe byoby wywoanie metody getDescription na rzecz zmiennej p, poniewa kom-
pilator zezwala na wywoywanie tylko tych metod, ktre s zdefiniowane w danej klasie.
Jednak metody klasy Manager maj dostp tylko do pola hireDay obiektw Manager, a nie
innych obiektw klasy Employee. Ograniczenie to ma zapobiec naduywaniu mechanizmu
ochrony w celu tworzenia podklas tylko po to, aby uzyska dostp do chronionych pl.
Wicej sensu ma tworzenie chronionych metod. Metoda moe by chroniona, jeli jej uy-
cie moe sprawia problemy. Oznacza to, e podklasy (ktre prawdopodobnie dobrze znaj
swoich przodkw) z pewnoci uyj danej metody prawidowo, podczas gdy metody innych
klas niekoniecznie.
Dobrym przykadem tego rodzaju metody jest metoda clone z klasy Object wicej
szczegw znajdziesz w rozdziale 6.
Skadowe chronione klasy w Javie s widoczne dla wszystkich jej podklas i innych
klas w pakiecie. Jest to nieco inne pojcie ochrony ni w C++ i powoduje ono, e
skadowe chronione w Javie s jeszcze mniej bezpieczne ni w C++.
Jeli adna nadklasa nie jest jawnie podana, to automatycznie jest ni klasa Object. Ponie-
wa kada klasa w Javie stanowi rozszerzenie klasy Object, trzeba si zapozna z usugami
wiadczonymi przez t klas. W tym rozdziale opisujemy tylko podstawowe zagadnienia,
a po szczegowe informacje odsyamy do kolejnych rozdziaw lub dokumentacji inter-
netowej (niektre metody klasy Object mog by uywane tylko podczas pracy z wtkami
wicej o wtkach dowiesz si w rozdziale 14.).
Rozdzia 5. Dziedziczenie 221
Oczywicie zmienna typu Object jest jedynie generycznym kontenerem dla dowolnych war-
toci. Aby zrobi z niej uytek, trzeba posiada wiedz na temat oryginalnego typu i wyko-
na rzutowanie:
Employee e = (Employee) obj;
W Javie tylko typy podstawowe (liczby, znaki i wartoci logiczne) nie s obiektami.
Wszystkie typy tablicowe bez wzgldu na to, czy przechowuj obiekty, czy typy podsta-
wowe s typami klasowymi rozszerzajcymi klas Object.
Employee[] staff = new Employee[10];
obj = staff; // OK
obj = new int[10]; // OK
W jzyku C++ nie ma uniwersalnej klasy bazowej, jednak kady wskanik mona
przekonwertowa na wskanik void*.
Na przykad dwch pracownikw uznamy za rwnych, jeli maj identyczne imi i nazwisko,
pensj i zostali zatrudnieni w tym samym czasie (w prawdziwej bazie danych pracownikw
lepiej byoby porwna identyfikatory pracownikw; ten przykad ma na celu zobrazowanie
mechanizmw implementacyjnych metody equals).
class Employee
{
. . .
public boolean equals(Object otherObject)
{
// Szybkie sprawdzenie, czy obiekty s identyczne.
if (this == otherObject) return true;
Metoda getClass zwraca nazw klasy obiektu szczegowy opis tej metody znajduje si
w dalszej czci tego rozdziau. W naszym tecie dwa obiekty mog by rwne tylko wtedy,
kiedy nale do tej samej klasy.
Aby zabezpieczy si na wypadek, gdyby zmienne name lub hireDay byy null,
mona uy metody Objects.equals. Wywoanie Objects.equals(a, b) zwraca
true, jeli oba argumenty s null, false jeli jeden z argumentw jest null, a w pozo-
staych przypadkach wywouje a.equals(b). Przy uyciu tej metody ostatni instrukcj
w metodzie Employee.equals mona zmieni nastpujco:
return Objects.equals(name, other.name)
&& salary == other.salary
&& Object.equals(hireDay, other.hireDay);
Implementujc metod equals w podklasie, najpierw naley wywoa metod equals nale-
c do nadklasy. Jeli test zakoczy si niepowodzeniem, obiekty nie mog by rwne. Jeli
pola nadklasy s rwne, mona porwnywa skadowe obiektw podklasy.
class Manager extends Employee
{
. . .
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;
// Metoda super.equals sprawdzia, czy this i otherObject nale do tej samej klasy.
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
}
Dziki temu obiekt otherObject moe nalee do podklasy klasy Employee. Metoda ta moe
jednak sprawia problemy. Specyfikacja jzyka Java wymaga, aby metoda equals miaa nast-
pujce wasnoci:
Rozdzia 5. Dziedziczenie 223
Jednak w przypadku gdy parametry nale do rnych klas, regua symetrii moe pocign za
sob pewne konsekwencje. Przeanalizujmy ponisze wywoanie:
e.equals(m)
gdzie e jest obiektem klasy Employee, a m klasy Manager. Tak si skada, e kady z nich ma
takie samo imi i nazwisko, pensj i dat zatrudnienia. Jeli wywoanie Employee.equals
uyje operatora instanceof, zostanie zwrcona warto true. Oznacza to jednak, e wywo-
anie odwrotne:
m.equals(e)
take musi zwrci warto true regua symetrii nie zezwala na zwrcenie wartoci false
ani spowodowanie wyjtku.
To krpuje klas Manager. Jej metoda equals zmusz ja do porwnywania si z klas Employee
bez brania pod uwag informacji waciwych tylko kierownikom! Nagle operator instan-
ceof przesta wydawa si tak atrakcyjny!
Niektrzy twierdz, e test getClass jest bdny, poniewa amie regu zamienialnoci. Czsto
przytaczany jest przykad metody equals w klasie AbstractSet, ktra sprawdza, czy dwa
zbiory maj te same elementy. Klasa AbstractSet ma dwie konkretne podklasy: TreeSet
i HashSet. Kada z nich lokalizuje elementy za pomoc innego algorytmu. Bez wzgldu na
implementacj potrzebna jest moliwo porwnywania zbiorw.
Jednak przykad zbioru dotyczy raczej wskiej specjalizacji. Mona by byo zdefiniowa metod
AbstractSet.equals jako finaln, poniewa nikt nie powinien zmienia semantyki rwnoci
zbiorw (obecnie metoda ta nie jest finalna, dziki czemu moliwe jest zaimplementowanie
w podklasie bardziej efektywnego algorytmu porwnujcego).
W przypadku klas Employee i Manager dwa obiekty uznajemy za rwne, jeli maj takie same
pola. Jeli dwa obiekty klasy Manager maj takie same imiona i nazwiska, pensje i daty zatrud-
nienia, ale rne dodatki do pensji, chcemy, aby byy uznane za rne. Dlatego uylimy testu
getClass.
Ta metoda deklaruje parametr jawny jako typ Employee. W wyniku tego nie przesania ona
metody equals z klasy Object, ale tworzy zupenie now metod.
Mona chroni si przed tego typu bdem, oznaczajc metody majce przesania
metody nadklasy znacznikiem @Override:
@Override public boolean equals(Object other)
Jeli zostanie popeniony bd i zdefiniowana nowa metoda, kompilator zgosi bd.
Przypumy na przykad, e dodano ponisz deklaracj do klasy Employee:
@Override public boolean equals(Employee other)
Zostanie zgoszony bd, poniewa ta metoda nie przesania adnej metody w nad-
klasie Object.
java.util.Arrays 1.2
java.util.Objects 7
to dwie rne liczby. Tabela 5.1 przedstawia trzy przykadowe kody mieszajce zwrcone
przez metod hashCode klasy String.
Metoda hashCode znajduje si w klasie Object. Dlatego kady obiekt ma domylny kod mie-
szajcy, ktry jest derywowany od adresu obiektu w pamici. Przeanalizujmy poniszy kod:
String s = "OK";
StringBuilder sb = new StringBuilder(s);
System.out.println(s.hashCode() + " " + sb.hashCode());
String t = new String("OK");
StringBuilder tb = new StringBuilder(t);
System.out.println(t.hashCode() + " " + tb.hashCode());
Naley zauway, e acuchy s i t maj takie same kody, poniewa kody mieszajce a-
cuchw s pochodnymi ich zawartoci. Obiekty sb i tb maj rne kody, poniewa dla klasy
StringBuilder nie zdefiniowano metody hashCode. W zwizku z tym domylna metoda hashCode
klasy Object utworzya ich kody mieszajce na podstawie adresw w pamici.
Metoda hashCode powinna zwraca liczb cakowit (moe by ujemna). Aby kody miesza-
jce rnych obiektw byy rne, wystarczy uy kombinacji kodw mieszajcych pl tych
obiektw.
class Employee
{
public int hashCode()
{
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
. . .
}
Po drugie, gdy trzeba poczy kilka wartoci skrtu (ang. hash value), mona wywoa
metod Objects.hash, przekazujc jej wszystkie te wartoci. Spowoduje to wywoanie metody
Objects.hashCode dla kadego argumentu i poczenie wartoci. Wwczas metoda Employee.
hashCode moe by o wiele prostsza:
public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}
Definicje metod equals i hashCode musz si ze sob zgadza jeli x.equals(y) zwraca
warto true, to x.hashCode() musi mie tak sam warto jak y.hashCode(). Jeli na przykad
metoda Employee.equals porwnuje identyfikatory pracownikw, metoda hashCode musi
miesza identyfikatory, a nie imiona i nazwiska pracownikw lub adresy w pamici.
java.lang.Object 1.0
int hashCode()
Zwraca kod mieszajcy obiektu. Kod ten moe by kad dodatni lub ujemn
liczb cakowit. Identyczne obiekty powinny mie takie same kody mieszajce.
java.lang.Objects 7
java.util.Arrays 1.2
Wikszo metod toString (ale nie wszystkie) ma nastpujcy format: nazwa klasy plus
wartoci pl wymienione w nawiasach kwadratowych. Poniej znajduje si implementacja
metody toString w klasie Employee:
public String toString()
{
return "Employee[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
Mona to jednak zrobi nieco lepiej. Zamiast na sztywno wpisywa nazw klasy w meto-
dzie toString, nazw t mona pobra za pomoc wywoania getClass().getName().
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
{
return super.toString()
+ "[bonus=" + bonus
+ "]";
}
}
Metoda toString jest bardzo czsto uywana z jednego wanego powodu: za kadym razem,
gdy obiekt jest czony z acuchem za pomoc operatora +, kompilator automatycznie
wywouje metod toString, aby utworzy acuchow reprezentacj obiektu. Na przykad:
Point p = new Point(10, 20);
String message = "Aktualne pooenie to " + p;
// Automatyczne wywoanie p.toString().
Klasa Object zawiera metod toString, ktra drukuje nazw klasy i kod mieszajcy obiektu.
Na przykad wywoanie:
System.out.println(System.out)
Jest to spowodowane tym, e programista implementujcy klas PrintStream nie zada sobie
trudu, aby przesoni metod toString.
Metoda toString jest doskonaym narzdziem do rejestracji danych. Wiele klas biblioteki stan-
dardowej zawiera metod toString, ktra umoliwia uzyskanie informacji na temat stanu
obiektu. Jest to szczeglnie przydatne w przypadku komunikatw rejestracyjnych typu:
System.out.println("Aktualne pooenie = " + position);
Jak piszemy w rozdziale 11., jeszcze lepszym rozwizaniem jest uycie obiektu klasy Logger
i zastosowanie nastpujcego wywoania:
Logger.global.info("Aktualne pooenie = " + position);
Program na listingu 5.8 zawiera implementacje metod equals, hashCode oraz toString w kla-
sach Employee (listing 5.9) i Manager (listing 5.10).
230 Java. Podstawy
Niestety tablice dziedzicz metod toString po klasie Object, przez co typ tablicy
jest drukowany w przestarzaym formacie. Na przykad:
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
String s = "" + luckyNumbers;
Powyszy kod zwraca acuch typu [I@e48e1b (przedrostek [I oznacza tablic liczb
cakowitych). Mona temu zaradzi poprzez wywoanie metody Arrays.toString.
Poniszy kod:
String s = Arrays.toString(luckyNumbers);
zwrci acuch [2, 3, 5, 7, 11, 13].
Aby prawidowo wydrukowa zawarto tablicy wielowymiarowej (tzn. tablicy tablic), naley
uy metody Arrays.deepToString.
package equals;
/**
* Jest to program demonstrujcy uycie metody equals.
* @version 1.12 2012-01-26
* @author Cay Horstmann
*/
public class EqualsTest
{
public static void main(String[] args)
{
Employee alice1 = new Employee("Alicja Adamczuk", 75000, 1987, 12, 15);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alicja Adamczuk", 75000, 1987, 12, 15);
Employee bob = new Employee("Bartosz Borkowski", 50000, 1989, 10, 1);
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;
{
return super.hashCode() + 17 * new Double(bonus).hashCode();
}
java.lang.Object 1.0
Class getClass()
Zwraca obiekt Class zawierajcy informacje dotyczce klasy obiektu. W dalszej
czci tego rozdziau dowiemy si, e w Javie istnieje zamknita w klasie Class
reprezentacja czasu wykonywania klas.
boolean equals(Object otherObject)
Porwnuje dwa obiekty. Zwraca warto true, jeli oba obiekty wskazuj ten
sam obszar pamici, lub false w przeciwnym przypadku. We wasnych klasach
powinno si t metod przesania.
String toString()
Zwraca acuch reprezentujcy warto obiektu. We wasnych klasach powinno
si t metod przesania.
java.lang.Class 1.0
String getName()
Zwraca nazw klasy.
Class getSuperClass()
Zwraca nadklas danej klasy w postaci obiektu klasy Class.
W Javie sytuacja ta przedstawia si znacznie lepiej. Rozmiar tablicy mona ustawi w trakcie
dziaania programu.
int actualSize = . . .;
Employee[] staff = new Employee[actualSize];
234 Java. Podstawy
Oczywicie powyszy fragment kodu nie rozwizuje w peni problemu dynamicznej zmiany
rozmiaru tablic w trakcie dziaania programu. Kiedy rozmiar tablicy jest ustalony, nie mona go
atwo zmieni. W Javie najprostszy sposb na poradzenie sobie z tak czsto spotykan
sytuacj jest uycie klasy o nazwie ArrayList. Obiekty tej klasy s podobne do tablic, z t
rnic, e automatycznie dostosowuj swoje rozmiary w wyniku dodawania i odejmowania
elementw. Nie wymaga to adnych modyfikacji kodu.
ArrayList jest klas generyczn z parametrem typu. Aby okreli typ obiektw prze-
chowywanych przez list tablicow, naley poda nazw klasy w nawiasach ostrych, np.
ArrayList<Employee>. Sposb definiowania klas generycznych zosta opisany w rozdziale 13.,
cho znajomo tych technik nie jest konieczna, aby mc uywa typu ArrayList.
Poniszy kod deklaruje i tworzy list tablicow przechowujc obiekty klasy Employee:
ArrayList<Employee> staff = new ArrayList<Employee>();
Wpisywanie parametru typu po obu stronach jest troch mudne. Dlatego w Java 7 parametr
ten mona po prawej stronie opuci.
ArrayList<Employee> staff = new ArrayList<>();
Jest to tzw. skadnia diamentowa (ang. diamond syntax), nazwana tak ze wzgldu na to, e
pusty nawias <> przypomina ksztatem diament. Naley j stosowa w poczeniu z opera-
torem new. Kompilator sprawdza, co si dzieje z now wartoci. Jeeli zostaje przypisana do
zmiennej, przekazana do metody lub zwrcona przez metod, to kompilator sprawdza oglny
typ tej zmiennej lub tego parametru albo tej metody. Nastpnie wstawia ten typ w nawias <>.
W przedstawionym przykadzie znajduje si przypisanie new ArrayList() do zmiennej typu
ArrayList<Employee>, wic typ oglny to Employee.
Przed wersj 5.0 w Javie nie byo klas generycznych. Zamiast tego bya sama
klasa ArrayList, ktra moga przechowywa elementy typu Object. Nadal mona
uywa klasy ArrayList bez <>. Jest to tak zwany typ surowy (ang. raw), w ktrym usu-
nito parametr typu.
Nowe elementy do listy s dodawane za pomoc metody add. Poniszy fragment kodu
zapenia list tablicow pracownikami:
staff.add(new Employee("Henryk Kwiatek", . . .));
staff.add(new Employee("Waldemar Kowalski", . . .));
Lista tablicowa zawiera wewntrzn tablic referencji do obiektw. Kiedy skoczy si miej-
sce w tej tablicy, lista wykonuje swoje magiczne sztuczki. Jeli wewntrzna tablica jest pena
i zostanie wywoana metoda add, lista automatycznie utworzy wiksz tablic i skopiuje do
niej wszystkie obiekty.
Rozdzia 5. Dziedziczenie 235
Jeli liczba elementw, ktre bd przechowywane w tablicy, jest znana, przynajmniej w przy-
blieniu, przed zapenieniem listy naley wywoa metod ensureCapacity.
staff.ensureCapacity(100);
Pomidzy pojemnoci listy tablicowej a rozmiarem tablicy jest dua rnica. Tablica
o rozmiarze 100 zawiera 100 miejsc, w ktrych moe przechowywa dane. Lista tabli-
cowa o pojemnoci 100 ma moliwo przechowywania 100 elementw (a nawet
wicej kosztem dodatkowej realokacji). Jednak na pocztku, nawet po jej utworzeniu,
lista tablicowa nie zawiera adnych elementw.
zwrci biec liczb elementw w licie tablicowej staff. To wywoanie jest odpowiedni-
kiem wywoania:
a.length
dla tablicy a.
Kiedy jest ju pewne, e rozmiar listy tablicowej si nie zmieni, mona wywoa metod trim
ToSize. Metoda ta dostosowuje rozmiar bloku pamici przechowujcego list dokadnie
do aktualnego rozmiaru listy. Nadmiar pamici zostanie wyczyszczony przez system zbie-
rania nieuytkw.
Jeli po dopasowaniu rozmiaru listy zostan dodane do niej nowe elementy, blok ponownie
zostanie przeniesiony, co zabiera czas. Metody trimToSize naley uywa wycznie wtedy,
gdy jest pewne, e do listy tablicowej nie bd dodawane ju nowe elementy.
Klasa ArrayList przypomina dostpny w C++ szablon vector. Jedno i drugie jest
typem generycznym. Ale szablon vector przecia operator [], dziki czemu
dostp do elementw jest wygodniejszy. Poniewa w Javie nie mona przecia ope-
ratorw, trzeba uywa do tego celu metod. Ponadto wektory w C++ s kopiowane
przez warto. Jeli a i b s wektorami, przypisanie a = b tworzy nowy wektor a o takiej
samej dugoci jak b i kopiuje wszystkie elementy z b do a. Takie samo przypisanie w Javie
powoduje, e zarwno a, jak i b odwouj si do tej samej listy tablicowej.
236 Java. Podstawy
java.util.ArrayList<T> 1.2
ArrayList<T>()
Tworzy pust list tablicow.
ArrayList<T>(int initialCapacity)
Tworzy pust list tablicow o podanej pojemnoci.
Parametry: initialCapacity Pocztkowa pojemno listy
boolean add(T obj)
Dodaje element na kocu listy. Zawsze zwraca warto true.
Parametry: obj Element do dodania
int size()
Zwraca liczb elementw aktualnie przechowywanych w licie (warto ta nie
moe by wiksza ni pojemno listy).
void ensureCapacity(int capacity)
Zapewnia, e lista bdzie miaa wystarczajc pojemno do przechowywania
danej liczby elementw, bez potrzeby realokacji wewntrznej tablicy.
Parametry: capacity Docelowa pojemno listy
void trimToSize()
Redukuje pojemno listy do jej aktualnego rozmiaru.
Zamiast skadni z operatorem [], aby uzyska dostp do obiektw w celu ich odczytania
lub modyfikacji, konieczne jest stosowanie metod get i set.
dla tablicy a (tak samo jak w tablicach, numerowanie indeksw zaczyna si od zera).
Rozdzia 5. Dziedziczenie 237
Nie naley wywoywa list.set(i, x), jeli rozmiar tablicy nie jest wikszy od i.
Na przykad poniszy kod jest bdny:
ArrayList<Employee> list = new ArrayList<>(100); // Pojemno 100, rozmiar 0
list.set(0, x); // Nie ma jeszcze elementu 0
Do zapeniania tablicy uywaj metody add zamiast set, ktr naley stosowa tylko
w celu podmiany wczeniej dodanego elementu.
Gdy nie byo generycznych klas, metoda get surowej klasy ArrayList nie miaa
innego wyjcia, jak zwraca obiekty klasy Object. Z tego powodu wywoujcy t
metod musia rzutowa zwrcon warto na odpowiedni typ:
Employee e = (Employee) staff.get(i);
Ponadto surowa klasa ArrayList nie jest w peni bezpieczna. Jej metody add i set
przyjmuj obiekty kadego typu. Ponisze wywoanie:
staff.set(i, new Date());
w czasie kompilacji spowoduje tylko ostrzeenie, a problemy zaczn si dopiero po
uzyskaniu obiektu i prbie rzutowania go. Przy uyciu ArrayList<Employee> kompilator
wykryje ten bd.
Czasami moliwe jest wzicie tego, co najlepsze, z tablic i list tablicowych elastycznoci
i wygodnego dostpu do elementw. Trzeba zastosowa nastpujc sztuczk. Naley utwo-
rzy list tablicow i wstawi do niej wszystkie potrzebne elementy:
ArrayList<X> list = new ArrayList<>();
while (. . .)
{
x = . . .;
list.add(x);
}
Aby doda element w rodku listy, naley uy metody add z parametrem okrelajcym indeks:
int n = staff.size() / 2;
staff.add(n, e);
Od Java 5.0 zawarto listy tablicowej mona przemierza za pomoc ptli typu for each:
for (Employee e : staff)
dziaania zwizane z e
import java.util.*;
/**
* Ten program demonstruje uycie klasy ArrayList.
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class ArrayListTest
{
public static void main(String[] args)
{
// Wstawienie do listy staff trzech obiektw klasy Employee.
ArrayList<Employee> staff = new ArrayList<>();
Rozdzia 5. Dziedziczenie 239
java.util.ArrayList<T> 1.2
Mimo e kompilator nie zgasza adnego bdu ani ostrzeenia, to wywoanie nie
jest w peni bezpieczne. Metoda update moe doda do listy tablicowej elementy,
ktre nie s typu Employee. Kiedy te elementy s pobierane, powstaje wyjtek. Mimo
e brzmi to strasznie, dziaanie to jest dokadnie takie samo jak przed Java SE 5.0.
Integralno maszyny wirtualnej nigdy nie jest zagroona. W tej sytuacji nie zostaje
naruszone bezpieczestwo, ale nie ma te adnych korzyci z testw przeprowadzanych
w czasie kompilacji.
Jeli natomiast obiekt surowej klasy ArrayList zostanie przypisany do typu z parametrem,
zostanie wywietlone ostrzeenie.
ArrayList<Employee> result = employeeDB.find(query); // Powoduje ostrzeenie
Niewiele mona z tym zrobi. Pracujc ze starszym kodem, naley przyglda si ostrzeeniom
zgaszanym przez kompilator i pociesza si tym, e nie maj one wielkiego znaczenia.
Rozdzia 5. Dziedziczenie 241
Jeli natomiast obiekt klasy Integer zostanie przypisany do wartoci int, zostanie automa-
tycznie odpakowany. To znaczy kompilator przekonwertuje:
int n = list.get(i);
na:
int n = list.get(i).intValue();
242 Java. Podstawy
W wikszoci przypadkw wydaje si, e typy podstawowe i ich osony s jednym i tym
samym. Jest midzy nimi tylko jedna znaczca rnica: tosamo. Jak wiadomo, operator
== zastosowany do obiektw osonowych sprawdza tylko, czy obiekty te maj identyczne
lokalizacje w pamici. W zwizku z tym ponisze porwnanie prawdopodobnie zakoczy-
oby si niepowodzeniem:
Integer a = 1000;
Integer b = 1000;
if (a == b) ...
Jednak implementacja Javy moe, jeli tak zdecyduje, opakowa czsto pojawiajce si
wartoci w identyczne obiekty i wtedy takie porwnanie zakoczyoby si powodzeniem.
Taka dwuznaczno nie jest podana. Rozwizaniem problemu jest porwnywanie obiek-
tw osonowych za pomoc metody equals.
Obiekty osonowe typw liczbowych s take czsto uywane do innego celu. Projektanci Javy
odkryli, e obiekty osonowe s dobrym miejscem na przechowywanie niektrych podsta-
wowych metod, jak te, ktre su do konwersji acuchw cyfr na liczby.
Kod ten nie ma nic wsplnego z obiektami klasy Integer metoda parseInt jest statyczna.
Jednak klasa Integer bya dobrym miejscem na przechowywanie tej metody.
W opisie API znajduj si informacje o innych waniejszych metodach klasy Integer. Pozostae
klasy odpowiadajce typom liczbowym zawieraj podobne metody.
Rozdzia 5. Dziedziczenie 243
java.lang.Integer 1.0
int intValue()
Zwraca warto obiektu Integer jako liczb typu int (przesania metod intValue
z klasy Number).
static String toString(int i)
Zwraca nowy obiekt klasy String reprezentujcy liczb i w systemie dziesitnym.
static String toString(int i, int radix)
Zwraca reprezentacj liczby i w systemie okrelonym przez parametr radix.
Static int parseInt(String s)
Static int parseInt(String s, int radix)
Zwraca liczb cakowit utworzon z cyfr w acuchu s. acuch ten musi
reprezentowa liczb w systemie dziesitnym (w przypadku pierwszej metody)
lub w systemie okrelonym przez parametr radix (druga metoda).
static Integer valueOf(String s)
244 Java. Podstawy
java.text.NumberFormat 1.1
Number parse(String s)
Zwraca warto liczbow, jeli acuch s reprezentuje liczb.
i
System.out.printf("%d %s", n, "widgets");
dotycz tej samej metody, mimo e pierwsze z nich ma dwa parametry, a drugie trzy.
W powyszym kodzie trzykropek () jest czci kodu Javy. Okrela on, e metoda moe
przyjmowa dowoln liczb obiektw (parametr fmt jest obowizkowy).
Innymi sowy, z punktu widzenia programisty implementujcego metod printf typ para-
metru Object jest dokadnie tym samym co Object[].
Mona definiowa wasne metody ze zmienn liczb parametrw. Parametry te mog by ka-
dego typu, take podstawowego. Poniej znajduje si prosty przykad takiej funkcji
zwraca najwiksz liczb w zbiorze o zmiennych rozmiarach:
public static double max(double... values)
{
double largest = Double.MIN_VALUE;
for (double v : values) if (v > largest) largest = v;
return largest;
}
Kompilator przekazuje tablic new double[] {3.1, 40.4, -5} do funkcji max.
Typ zdefiniowany przez powysz deklaracj jest w rzeczywistoci klas. Ma ona dokadnie
cztery egzemplarze nie mona tworzy jej nowych obiektw.
W zwizku z tym do porwnywania typw wyliczeniowych nie trzeba uywa metody equals.
Wystarczy operator ==.
Wszystkie typy wyliczeniowe s podklasami klasy Enum. Dziedzicz po niej kilka metod.
Do najbardziej przydatnych naley metoda toString, ktra zwraca nazw staej wylicze-
niowej. Na przykad wywoanie Size.SMALL.ToString() zwraca acuch SMALL.
Kady typ wyliczeniowy ma statyczn metod values, ktra zwraca wszystkie wartoci wyli-
czenia. Na przykad wywoanie:
Size[] values = Size.values();
Metoda ordinal zwraca pooenie staej wyliczeniowej w deklaracji enum, zaczynajc liczenie
od zera. Na przykad wywoanie Size.MEDIUM.ordinal() zwraca warto 1.
import java.util.*;
/**
* Ten program demonstruje typy wyliczeniowe.
* @version 1.0 2004-05-24
* @author Cay Horstmann
*/
public class EnumTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj rozmiar: (SMALL, MEDIUM, LARGE, EXTRA_LARGE) ");
String input = in.next().toUpperCase();
Size size = Enum.valueOf(Size.class, input);
System.out.println("rozmiar=" + size);
System.out.println("skrt=" + size.getAbbreviation());
if (size == Size.EXTRA_LARGE)
System.out.println("Dobra robota -- nie pomine znaku podkrelenia _.");
Rozdzia 5. Dziedziczenie 247
}
}
enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
java.lang.Enum<E> 5.0
5.7. Refleksja
Biblioteka refleksyjna dostarcza bogaty i zaawansowany zestaw narzdzi do pisania progra-
mw, ktre dynamicznie zarzdzaj kodem Javy. Mechanizm ten jest czsto wykorzystywany
w JavaBeans skadniku architekturalnym Javy (wicej informacji na temat JavaBeans
znajduje si w drugim tomie). Dziki refleksji Java obsuguje narzdzia, do ktrych przy-
zwyczajeni s uytkownicy jzyka Visual Basic. Narzdzia suce do szybkiej budowy
aplikacji mog dynamicznie uzyskiwa informacje o funkcjonalnoci dodawanych klas,
zwaszcza kiedy w trakcie projektowania lub dziaania programu dodawane s nowe klasy.
Do informacji tych mona jednak uzyska dostp take dziki specjalnej klasie. Klasa prze-
chowujca te informacje ma nazw Class. Metoda getClass() klasy Object zwraca egzem-
plarz klasy Class.
Employee e;
. . .
Class cl = e.getClass();
Podobnie jak obiekt klasy Employee opisuje cechy okrelonego pracownika, obiekt klasy Class
opisuje cechy okrelonej klasy. Chyba najczciej uywan metod klasy Class jest metoda
getName, ktra zwraca nazw klasy. Na przykad ponisza instrukcja:
System.out.println(e.getClass().getName() + " " + e.getName());
drukuje:
Employee Henryk Kwiatek
Jeli klasa naley do jakiego pakietu, nazwa tego pakietu stanowi cz nazwy tej klasy:
Date d = new Date();
Class cl = d.getClass();
String name = cl.getName(); // Zmienna name jest ustawiana na java.util.Date.
Obiekt klasy Class odpowiadajcy nazwie wybranej klasy mona utworzy za pomoc sta-
tycznej metody forName.
String className = "java.util.Date";
Class cl = Class.forName(className);
Metody tej naley uy, jeli nazwa klasy jest przechowywana w acuchu, ktry zmienia
si w czasie dziaania programu. Powyszy kod dziaa, jeli className jest nazw klasy lub
Rozdzia 5. Dziedziczenie 249
Przy uruchamianiu programu najpierw adowana jest klasa zawierajca metod main.
aduje ona wszystkie klasy, ktrych potrzebuje. Kada z tych zaadowanych klas
aduje kolejne klasy, ktrych potrzebuje itd. W przypadku duej aplikacji proces ten moe
zajmowa duo czasu, co denerwowaoby uytkownika. Mona jednak zastosowa pewn
sztuczk, ktra da uytkownikowi wraenie, e program uruchamia si szybciej. Naley
sprawi, aby klasa zawierajca metod main nie odwoywaa si bezporednio do innych
klas. Najpierw naley wywietli ekran powitalny, a potem rcznie wymusi zaadowanie
pozostaych klas za pomoc wywoania Class.forName.
Trzecia metoda tworzenia obiektu typu Class jest wygodnym skrtem. Jeli T jest dowolnym
typem Javy, T.class jest odpowiadajcym mu obiektem klasy Class. Na przykad:
Class cl1 = Date.class; // Naley zaimportowa java.util.*;.
Class cl2 = int.class;
Class cl3 = Double[].class;
Naley zauway, e obiekt klasy Class w rzeczywistoci oznacza typ, ktry moe, ale nie musi
by klas. Na przykad int nie jest klas, ale int.class jest z pewnoci obiektem typu Class.
Maszyna wirtualna obsuguje unikatowy obiekt Class dla kadego typu. W zwizku z tym do
porwnywania obiektw class mona uywa operatora ==. Na przykad:
if (e.getClass() == Employee.class) . . .
Inna przydatna metoda umoliwia tworzenie w locie egzemplarzy klas. Nazywa si newIn
stance(). Na przykad:
e.getClass().newInstance();
Powysza instrukcja tworzy egzemplarz tego samego typu co e. Metoda newInstance wywouje
konstruktor domylny (ten, ktry nie przyjmuje adnych parametrw). Jeli klasa nie ma
konstruktora domylnego, powodowany jest wyjtek.
250 Java. Podstawy
Przy uyciu metod forName i newInstance mona utworzy obiekt z nazwy klasy przecho-
wywanej w acuchu.
String s = "java.util.Date";
Object m = Class.forName(s).newInstance();
Jeli konieczne jest podanie parametrw dla konstruktora klasy, ktrej obiekt jest
tworzony w ten sposb, nie mona uy powyszych instrukcji. W zamian trzeba uy
metody newInstance klasy Constructor.
Kiedy w czasie dziaania programu wystpuje bd, program moe spowodowa wyjtek.
Mechanizm wyjtkw zapewnia wiksz elastyczno ni koczenie programu, poniewa
mona napisa procedur, ktra przechwyci taki wyjtek i go odpowiednio obsuy.
Jeli nie ma procedury obsugi wyjtku, program zostaje zakoczony, a w konsoli zostaje
wydrukowany komunikat informujcy o jego typie. Komunikat taki moe si pojawi
w wyniku przypadkowego uycia referencji null lub przekroczenia rozmiaru tablicy.
Nie wszystkich bdw mona jednak unikn. Jeli wyjtek moe wystpi mimo najlepszych
stara programisty, kompilator nalega na napisanie procedury do jego obsugi. Przykadem
metody powodujcej kontrolowany wyjtek jest Class.forName. W rozdziale 11. opisano kilka
technik obsugi wyjtkw. W tym miejscu prezentujemy tylko najprostsz implementacj pro-
cedury obsugi wyjtku.
Instrukcje, ktre mog spowodowa wyjtek kontrolowany, naley umieci w bloku try.
W klauzuli catch naley wpisa kod obsugujcy wyjtek.
try
{
Instrukcje, ktre mog powodowa wyjtki
Rozdzia 5. Dziedziczenie 251
}
catch(Exception e)
{
Procedura obsugi wyjtku
}
Na przykad:
try
{
String name = . . .; // Pobranie nazwy klasy.
Class cl = Class.forName(name); // Moe spowodowa wyjtek.
Dziaania zwizane z cl
}
catch(Exception e)
{
e.printStackTrace();
}
Jeli klasa o podanej nazwie nie istnieje, reszta kodu w bloku try jest pomijana i nastpuje
przejcie do bloku catch (w tym miejscu drukujemy dane ze ledzenia stosu za pomoc metody
printStack klasy Throwable, ktra jest nadklas klasy Exception). Jeli adna z metod w bloku
try nie spowoduje wyjtku, kod w bloku catch zostaje pominity.
Konieczne jest dostarczanie procedur tylko dla wyjtkw kontrolowanych. Mona atwo
sprawdzi, ktre metody powoduj kontrolowane wyjtki. Kompilator zgasza problem za
kadym razem, kiedy wywoywana jest metoda mogca spowodowa wyjtek kontrolowany,
a nie napisane dla niej procedury obsugi wyjtkw.
java.lang.Class 1.0
java.lang.reflect.Constructor 1.1
java.lang.Throwable 1.0
void printStackTrace()
Drukuje obiekt Throwable i dane ze ledzenia stosu do standardowego strumienia
bdw.
252 Java. Podstawy
Listing 5.13 prezentuje sposb wydrukowania wszystkich informacji o klasie. Ten program
monituje o podanie nazwy klasy, po czym drukuje sygnatury wszystkich metod i konstruk-
torw oraz nazwy wszystkich pl klasy. Jeli na przykad programowi zostanie podana klasa
java.lang.Double, wydrukuje on nastpujce dane:
public class java.lang.Double extends java.lang.Number
{
public java.lang.Double(java.lang.String);
public java.lang.Double(double);
Naley zauway, e program ten potrafi przeanalizowa kad klas, ktr interpreter Javy
potrafi zaadowa, a nie tylko te klasy, ktre byy dostpne w czasie kompilacji. Ten pro-
gram bdziemy wykorzystywa w kolejnym rozdziale, w ktrym bdziemy zaglda do wntrza
klas wewntrznych generowanych automatycznie przez kompilator.
import java.util.*;
import java.lang.reflect.*;
/**
* Ten program wykorzystuje technik refleksji do wydrukowania penych informacji o klasie.
* @version 1.1 2004-02-21
* @author Cay Horstmann
*/
public class ReflectionTest
{
public static void main(String[] args)
{
// Wczytanie nazwy klasy z argumentw wiersza polece lub danych od uytkownika.
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Podaj nazw klasy (np. java.util.Date): ");
name = in.next();
}
try
{
// Drukowanie nazwy klasy i nadklasy (jeli != Object).
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("klasa " + name);
if (supercl != null && supercl != Object.class) System.out.print(" rozszerza
klas "
+ supercl.getName());
254 Java. Podstawy
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.exit(0);
}
/**
* Drukowanie wszystkich konstruktorw klasy.
* @param cl klasa
*/
public static void printConstructors(Class cl)
{
Constructor[] constructors = cl.getDeclaredConstructors();
/**
* Drukuje wszystkie metody klasy.
* @param cl klasa
*/
public static void printMethods(Class cl)
{
Method[] methods = cl.getDeclaredMethods();
System.out.print(" ");
// Drukowanie modyfikatorw, typu zwrotnego i nazwy metody.
Rozdzia 5. Dziedziczenie 255
/**
* Drukowanie wszystkich pl klasy.
* @param cl klasa
*/
public static void printFields(Class cl)
{
Field[] fields = cl.getDeclaredFields();
java.lang.Class 1.0
java.lang.reflect.Field 1.1
java.lang.reflect.Method 1.1
java.lang.reflect.Constructor 1.1
Class getDeclaringClass()
Zwraca obiekt klasy Class reprezentujcy klas, ktra definiuje dany konstruktor,
metod lub pole.
Class[] getExceptionTypes() (tylko klasy Constructor i Method)
Zwraca tablic obiektw Class, ktre reprezentuj typy wyjtkw powodowanych
przez metod.
int getModifiers()
Zwraca liczb cakowit opisujc modyfikatory konstruktora, metody lub pola.
Do analizy zwrconej wartoci su metody klasy Modifier.
String getName()
Zwraca w postaci acucha nazw konstruktora, metody lub pola.
Class[] getParameterTypes() (tylko klasy Constructor i Method)
Zwraca tablic obiektw klasy Class reprezentujcych typy parametrw.
Class getReturnType() (tylko w klasie Method)
Zwraca obiekt klasy Class reprezentujcy typ zwrotny.
java.lang.reflect.Modifier 1.1
Kluczowe znaczenie ma w tym przypadku metoda get z klasy Field. Jeli f jest obiektem
typu Field (na przykad utworzonym za pomoc metody getDeclaredFields), a obj jest
obiektem klasy, ktrej polem jest f, wywoanie f.get(obj) zwraca obiekt, ktrego wartoci
jest aktualna warto pola obiektu obj. Przeanalizujmy to nieco skomplikowane zagadnienie na
przykadzie.
Employee harry = new Employee("Henryk Kwiatek", 35000, 10, 1, 1989);
Class cl = harry.getClass();
// Obiekt Class reprezentujcy pracownika.
Field f = cl.getDeclaredField("name");
// Pole name klasy Employee.
Object v = f.get(harry);
// Warto pola name obiektu harry
// tj. obiekt klasy String "Henryk Kwiatek".
Ten kod sprawia jednak jeden problem. Poniewa pole name jest prywatne, metoda get spowo-
duje wyjtek IllegalAccessException. Za pomoc tej metody mona sprawdzi tylko wartoci
dostpnych pl. Zabezpieczenia w Javie zezwalaj na sprawdzenie, jakie pola zawiera obiekt,
ale nie pozwalaj na sprawdzenie ich wartoci bez odpowiednich uprawnie dostpu.
Metoda setAccessible naley do klasy AccessibleObject, ktra jest wspln nadklas klas
Field, Method i Constructor. Zostaa ona utworzona z myl o debugerach, schowkach i podob-
nych mechanizmach. Nieco dalej uywamy tej metody dla generycznej metody toString.
258 Java. Podstawy
Metoda get sprawia jeszcze jeden problem, z ktrym musimy sobie poradzi. Pole name jest
typu String, a wic nie ma problemu, eby zwrci jego warto jako typ Object, ale pole
salary jest typu double, a w Javie typy liczbowe nie s obiektami. W tym przypadku mona
uy metody getDouble z klasy Field lub wywoa metod get. W tym drugim przypadku
mechanizm refleksji automatycznie opakuje warto pola w obiekt odpowiedniego typu, tutaj
Double.
Listing 5.14 przedstawia generyczn metod toString, ktra dziaa z kad klas. Wszystkie
pola danych s pobierane za pomoc metody getDeclaredFields. Nastpnie metoda setAcce
ssible umoliwia dostp do wszystkich tych pl. Pobierane s nazwa i warto kadego
pola. Program na listingu 5.14 rekursywnie wywouje metod toString, zamieniajc kad
warto na acuch.
class ObjectAnalyzer
{
public String toString(Object obj)
{
Class cl = obj.getClass();
. . .
String r = cl.getName();
// Przegld pl tej klasy i wszystkich jej nadklas.
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// Pobranie nazw i wartoci wszystkich pl.
for (Field f : fields)
{
if (!Modifier.isStatic(f.getModifiers()))
{
if (!r.endsWith("[")) r += ","
r += f.getName() + "=";
try
{
Object val = f.get(obj);
r += toString(val);
}
catch (Exception e) { e.printStackTrace(); }
}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl != null);
return r;
}
. . .
}
W penej wersji kodu na listingu 5.14 konieczne byo rozwizanie kilku skomplikowanych
problemw. Cykliczne odwoania mog spowodowa nieskoczon rekursj. Dlatego klasa
Rozdzia 5. Dziedziczenie 259
ObjectAnalyzer (listing 5.15) zapamituje obiekty, ktre byy ju odwiedzane. Aby zajrze
do tablic, potrzebne jest zastosowanie innej metody. Wicej szczegw na ten temat znaj-
duje si w kolejnym podrozdziale.
zwraca:
java.util.ArrayList[elementData=class
java.lang.Object[]{java.lang.Integer[value=1][][],
java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer
[value=16][][],
java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]
import java.util.ArrayList;
/**
* Ten program analizuje obiekty za pomoc refleksji.
* @version 1.12 2012-01-26
* @author Cay Horstmann
*/
public class ObjectAnalyzerTest
{
public static void main(String[] args)
{
ArrayList<Integer> squares = new ArrayList<>();
for (int i = 1; i <= 5; i++)
squares.add(i * i);
System.out.println(new ObjectAnalyzer().toString(squares));
}
}
import java.lang.reflect.AccessibleObject;
260 Java. Podstawy
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
class ObjectAnalyzer
{
private ArrayList<Object> visited = new ArrayList<>();
/**
* Konwertuje obiekt na acuch zawierajcy list wszystkich pl.
* @param obj obiekt
* @return acuch zawierajcy nazw klasy obiektu oraz nazwy i wartoci wszystkich pl.
*/
public String toString(Object obj)
{
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String) obj;
if (cl.isArray())
{
String r = cl.getComponentType() + "[]{";
for (int i = 0; i < Array.getLength(obj); i++)
{
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
// Inspekcja pl tej klasy i wszystkich nadklas.
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// Pobranie nazw i wartoci wszystkich pl.
for (Field f : fields)
{
if (!Modifier.isStatic(f.getModifiers()))
{
if (!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try
{
Class t = f.getType();
Object val = f.get(obj);
if (t.isPrimitive()) r += val;
else r += toString(val);
}
catch (Exception e)
{
e.printStackTrace();
Rozdzia 5. Dziedziczenie 261
}
}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl != null);
return r;
}
}
java.lang.reflect.AccessibleObject 1.2
java.lang.Class 1.1
java.lang.reflect.Field 1.1
Jak napisa tak metod? Pomocny jest fakt, e tablic Employee[] mona przekonwertowa
na tablic Object[]. Brzmi obiecujco. Oto pierwsza prba.
public static Object[] badCopyOf(Object[] a, int newLength) // nieprzydatna
{
Object[] newArray = new Object[newLength];
System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength));
return newArray;
}
Niestety jest problem z uyciem powstaej tablicy. Ten kod zwraca tablic obiektw
(Object[]). Odpowiada za to poniszy wiersz:
new Object[newLength]
Aby tego dokona, trzeba zna typ elementw i rozmiar nowej tablicy.
3. Znajd odpowiedni typ dla tablicy za pomoc metody getComponentType (ktra jest
zdefiniowana tylko dla obiektw klasowych reprezentujcych tablice) klasy Class.
Naley zauway, e metoda copyOf moe powiksza tablice kadego typu, nie tylko prze-
chowujce obiekty.
int[] a = { 1, 2, 3, 4 };
a = (int[]) goodCopyOf(a);
Aby to byo moliwe, parametr metody goodCopyOf jest typu Object, a nie tablic obiektw
(Object[]). Tablic typu int[] mona przekonwertowa na Object, ale nie na tablic
obiektw!
Listing 5.16 demonstruje obie metody powikszania tablicy. Zauwa, e rzutowanie typu
zwrotnego metody badCopyOf spowoduje wyjtek.
import java.lang.reflect.*;
import java.util.*;
/**
* Ten program demonstruje zastosowanie refleksji do manipulacji tablicami.
* @version 1.2 2012-05-04
* @author Cay Horstmann
*/
public class CopyOfTest
{
public static void main(String[] args)
{
int[] a = { 1, 2, 3 };
a = (int[]) goodCopyOf(a);
System.out.println(Arrays.toString(a));
/**
* Ta metoda prbuje powikszy tablic, tworzc now tablic i kopiujc wszystkie elementy.
* @param a tablica, ktra ma by powikszona.
* @param newLength nowa dugo tablicy
* @return wiksza tablica zawierajca wszystkie elementy tablicy a. Zwrcona tablica jest
* typu Object[], a nie takiego samego jak a.
*/
public static Object[] badCopyOf(Object[] a, int newLength) // nieprzydatna
{
Object[] newArray = new Object[newLength];
264 Java. Podstawy
/**
* Ta metoda powiksza tablic, tworzc now tablic tego samego typu
* i kopiujc wszystkie elementy.
* @param a tablica, ktra ma by powikszona. Moe to by tablica obiektw lub
* elementw typu podstawowego.
* @return wiksza tablica zawierajca wszystkie elementy tablicy a.
*/
public static Object goodCopyOf(Object a, int newLength)
{
Class cl = a.getClass();
if (!cl.isArray()) return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
return newArray;
}
}
java.lang.reflect.Array 1.1
nie s bezpieczne, mog bowiem stanowi rdo bdw, a lepszym od nich rozwizaniem
s interfejsy (opisane w kolejnym rozdziale). Jednak dziki refleksji take w Javie mona
wywoywa dowolne metody.
Przypomnijmy sobie, e za pomoc metody get klasy Field mona sprawdzi dowolne pole
obiektu. Podobnie klasa Method zawiera metod invoke, ktra umoliwia wywoanie metody
zapakowanej w biecy obiekt klasy Method. Sygnatura metody invoke wyglda nastpujco:
Object invoke(Object obj, Object... args)
Jeli na przykad m1 reprezentuje metod getName klasy Employee, poniszy kod pokazuje, jak
mona j wywoa:
String n = (String) m1.invoke(harry);
Jeli typ zwrotny jest podstawowy, metoda invoke zwrci typ osony. Zamy na przykad,
e m2 reprezentuje metod getSalary klasy Employee. Zwrcony obiekt bdzie typu Double
i trzeba wykona odpowiednie rzutowanie. Mona do tego celu zastosowa automatyczne
odpakowywanie.
double s = (Double) m2.invoke(harry);
Jak uzyska obiekt klasy Method? Mona oczywicie wywoa metod getDeclaredMethods
i przeszuka zwrcon tablic w celu znalezienia danej metody. Mona rwnie wywoa
metod getMethod klasy Class. Jest ona podobna do metody getField, ktra przyjmuje nazw
pola w postaci acucha i zwraca obiekt typu Field. Jednak metod o takiej samej nazwie
moe by kilka, wic naley uwaa, aby si nie pomyli. Z tego powodu konieczne jest
podanie dodatkowo typw parametrw danej metody. Sygnatura metody getMethod jest
nastpujca:
Method getMethod(String name, Class... parameterTypes)
Oczywicie kod drukujcy tabel jest niezaleny od samej funkcji, dla ktrej zastosowano
wcicia.
double dx = (to - from) / (n - 1);
for (double x = from; x <= to; x += dx)
{
double y = (Double) f.invoke(null, x);
System.out.printf("%10.4f | %10.4f%n", x, y);
}
W powyszym kodzie f jest obiektem typu Method. Pierwszy parametr metody invoke ma
warto null, poniewa wywoywana jest metoda statyczna.
Listing 5.17 przedstawia peny kod generycznego tabulatora i kilka przykadowych wywoa.
import java.lang.reflect.*;
/**
* Ten program demonstruje sposb wywoywania metod poprzez refleksj.
* @version 1.2 2012-05-04
* @author Cay Horstmann
*/
public class MethodTableTest
{
public static void main(String[] args) throws Exception
{
// Pobranie wskanikw do metod square i sqrt.
Method square = MethodTableTest.class.getMethod("square", double.class);
Method sqrt = Math.class.getMethod("sqrt", double.class);
/**
Rozdzia 5. Dziedziczenie 267
/**
* Drukuje tablic wartoci x i y dla danej metody.
* @param od dolnej granicy wartoci x
* @param do grnej granicy wartoci x
* @param n liczba wierszy w tabeli
* @param f metoda z parametrem i typem zwrotnym typu double
*/
public static void printTable(double from, double to, int n, Method f)
{
// Drukowanie metody jako nagwka tabeli.
System.out.println(f);
Dodatkowo parametry i typy zwrotne metody invoke musz by typu Object. Oznacza to
konieczno czstego rzutowania w obie strony. W ten sposb kompilator zostaje pozbawiony
moliwoci sprawdzenia kodu. Przez to bdy ujawniaj si tylko podczas testw, kiedy s
trudniejsze do naprawienia. Ponadto kod tworzcy wskanik do metody przy uyciu refleksji
jest znacznie wolniejszy ni kod, ktry wywouje metody bezporednio.
Z wymienionych powodw zalecamy uywa obiektw klasy Method wycznie wtedy, kiedy
jest to absolutnie niezbdne. Prawie zawsze lepiej jest uy interfejsu i klas wewntrznych
(ktre s tematem kolejnego rozdziau). Zgadzamy si zwaszcza ze stanowiskiem projek-
tantw jzyka Java i nie polecamy uywania obiektw typu Method dla funkcji zwrotnych.
Dziki uyciu interfejsw dla metod zwrotnych (zobacz nastpny rozdzia) kod jest znacznie
szybszy i atwiejszy w utrzymaniu.
268 Java. Podstawy
java.lang.reflect.Method 1.1
Niestety zbir dni wolnych nie jest zamknity dla wszystkich odziedziczonych
metod. Jedn z publicznych metod klasy GregorianCalendar jest add. Za jej pomoc
dni witeczne mona zamieni w dni niewiteczne:
Holiday christmas;
christmas.add(Calendar.DAY_OF_MONTH, 12);
if (x jest typu 1)
dziaanie1(x);
else if (x jest typu 2)
dziaanie2(x);
W tym rozdziale:
Interfejsy
Klonowanie obiektw
Interfejsy i metody zwrotne
Klasy wewntrzne
Klasy poredniczce
Kolejne zagadnienie to klasy wewntrzne (ang. inner class). Z technicznego punktu widzenia
s one nieco skomplikowane, poniewa ich definicje znajduj si wewntrz innych klas, a ich
metody maj dostp do pl klas je zawierajcych. Klasy wewntrzne znajduj zastosowanie
przy projektowaniu zbiorw wsppracujcych ze sob klas. W szczeglnoci umoliwiaj
pisanie zwizego i profesjonalnego kodu obsugujcego zdarzenia GUI.
272 Java. Podstawy
6.1. Interfejsy
W Javie interfejs nie jest klas, ale zestawem wymaga, ktre musz by spenione, aby klasa
zostaa uznana za zgodn z danym interfejsem.
Typowy dostawca usug mwi co takiego: Jeli twoja klasa spenia wymagania okrelonego
interfejsu, ja wykonam moj usug. Przeanalizujmy konkretny przykad. Metoda sort
z klasy Array sortuje tablice obiektw, ale pod jednym warunkiem: obiekty te musz nale-
e do klas, ktre implementuj interfejs Comparable.
Oznacza to, e kada klasa implementujca powyszy interfejs musi mie metod compareTo,
ktra przyjmuje parametr typu Object i zwraca liczb cakowit.
Nadal mona uywa surowego typu Comparable bez parametru typu, ale wtedy
konieczne jest wykonywanie na wasn rk rzutowania parametru metody compareTo
na odpowiedni typ.
Oczywicie jest jeszcze jedno wymaganie, ktrego nie mona opisa w interfejsie. W wywo-
aniu x.compareTo(y) metoda compareTo musi porwna dwa podane obiekty i zwrci infor-
macj na temat tego, ktry z nich jest wikszy. Liczba ujemna oznacza, e wikszy jest y,
zero, e obiekty s rwne, a liczba dodatnia, e wikszy jest x.
Ten interfejs ma tylko jedn metod, ale s interfejsy, ktre maj ich wicej. Pniej prze-
konamy si, e interfejsy mog nawet zawiera definicje staych. Waniejsze jest jednak to,
czego interfejs nie moe zawiera. Interfejsy nie mog zawiera pl obiektowych ani imple-
Rozdzia 6. Interfejsy i klasy wewntrzne 273
Przejdmy do metody sort klasy Array. Chcemy posortowa tablic obiektw klasy Employee.
W zwizku z tym klasa Employee musi implementowa interfejs Comparable.
W przykadzie tym uylimy statycznej metody Double.compare, ktra zwraca warto ujemn,
gdy pierwszy argument jest mniejszy od drugiego, 0 gdy argumenty s rwne, oraz dodatni
warto w pozostaych przypadkach.
Od Java SE 5.0 powysze zadanie mona wykona nieco lepiej. Zaimplementujemy interfejs
Comparable<Employee>.
class Employee implements Comparable<Employee>
{
public int compareTo(Employee other)
{
return Double.compare(salary, other.salary);
}
. . .
}
Metoda compareTo interfejsu Comparable zwraca liczb cakowit. Jeli obiekty nie
s rwne, nie ma znaczenia, jaka liczba dodatnia lub ujemna zostanie zwrcona.
Ta elastyczno moe si okaza przydatna przy porwnywaniu pl przechowujcych liczby
cakowite. Niech na przykad kady pracownik ma unikatowy identyfikator w postaci liczby
cakowitej. Chcemy wykona sortowanie wedug identyfikatorw. W takim przypadku
wystarczy zwrci wynik dziaania id - inny.id. Warto ta bdzie ujemna, jeli pierwszy
identyfikator jest mniejszy od drugiego, bdzie wynosia 0, jeli s takie same, lub bdzie
dodatnia w przeciwnym przypadku. Istnieje jednak jedna puapka: porwnywane liczby
nie mog by zbyt due, poniewa mog spowodowa przekroczenie zakresu. Jeli wia-
domo, e identyfikatory nie mog mie wartoci ujemnych lub e ich maksymalna war-
to bezwzgldna nie przekracza wartoci (Integer.MAX_VALUE-1)/2, nie ma problemu.
Oczywicie sztuczka ta nie nadaje si do stosowania z liczbami zmiennoprzecinkowymi.
Rnica salary - inna.salary moe zosta zaokrglona do 0, jeli porwnywane liczby
maj bardzo zblione, ale nie identyczne wartoci. Wywoanie Double.compare(x, y)
zwraca -1, gdy x < y, lub 1, gdy x > 0.
Wiemy ju, co klasa musi zrobi, aby mc skorzysta z usugi sortowania musi zaimple-
mentowa metod compareTo. To nadzwyczaj rozsdne. Musi istnie jaki sposb na porw-
nywanie obiektw przez metod sort. Ale czemu klasa Employee nie moe definiowa metody
compareTo bez implementacji interfejsu Comparable?
Powodem wprowadzenia interfejsw w Javie byo to, e jest to jzyk ze cis kontrol
typw. Podczas tworzenia wywoania metody kompilator musi mie moliwo sprawdzenia,
czy ta metoda w ogle istnieje. W metodzie sort mona znale nastpujce instrukcje:
if (a[i].compareTo(a[j]) > 0)
{
// Zamiana miejscami obiektw a[i] i a[j].
. . .
}
Kompilator musi wiedzie, czy a[i] rzeczywicie udostpnia metod compareTo. Jeli a jest
tablic obiektw klasy implementujcej interfejs Comparable, wiadomo, e istnieje metoda
compareTo, poniewa kada klasa implementujca interfejs Comparable musi j definiowa.
Jeli obiekt a[i] nie naley do klasy implementujcej interfejs Comparable, maszyna
wirtualna zgasza wyjtek.
Rozdzia 6. Interfejsy i klasy wewntrzne 275
Listing 6.1 przedstawia peny kod programu sortujcego tablic egzemplarzy klasy Employee,
ktra z kolei zostaa zaprezentowana na listingu 6.2.
import java.util.*;
/**
* Ten program demonstruje sposb uycia interfejsu Comparable.
* @version 1.30 2004-02-27
* @author Cay Horstmann
*/
public class EmployeeSortTest
{
public static void main(String[] args)
{
Employee[] staff = new Employee[3];
Arrays.sort(staff);
/**
* Porwnuje pracownikw wedug wysokoci pensji.
* @param other inny obiekt klasy Employee
* @return warto ujemna, jeli pracownik ma nisz pensj ni inny (other) pracownik,
* 0, jeli pensje s rwne, w przeciwnym razie liczba dodatnia
*/
public int compareTo(Employee other)
{
return Double.compare(salary, other.salary);
}
}
java.lang.Comparable<T> 1.0
java.util.Arrays 1.2
java.lang.Integer 7
java.lang.Double 7
To amie zasad antysymetrii. Jeli x jest typu Employee, a y typu Manager, wywoanie
x.compareTo(y) nie spowoduje wyjtku obiekty zostan porwnane jako zwykli pracow-
nicy. Jednak odwrotne wywoanie y.compareTo(x) spowoduje wyjtek ClassCastException.
Jest to taka sama sytuacja jak w przypadku metody equals, ktr opisywalimy w roz-
dziale 5. Rozwizanie jest rwnie takie samo. Istniej dwa osobne scenariusze.
Jeli porwnywanie w podklasach opiera si na innych zasadach, naley zabroni porw-
nywania obiektw, ktre nale do innych klas. Kada metoda compareTo powinna si
zaczyna od nastpujcego testu:
if (getClass() != other.getClass()) throw new ClassCastException();
Jeli algorytmy porwnywania obu klas s takie same, naley utworzy tylko jedn metod
compareTo w nadklasie i zadeklarowa j jako finaln.
Przyjmijmy na przykad, e chcemy, aby kierownicy byli lepsi od zwykych pracownikw,
bez wzgldu na pensj. Co z pozostaymi klasami, jak Executive i Secretary? Aby ustali
porzdek dziobania, naley w klasie Employee zdefiniowa klas o nazwie np. rank.
Niech kada podklasa przesania metod rank i implementuje metod compareTo, ktra
bierze pod uwag wartoci rank.
Mimo e nie mona tworzy obiektw interfejsw, dopuszczalne jest deklarowanie ich
zmiennych.
Comparable x; // OK
Podobnie jak mona sprawdzi za pomoc operatora instanceof, czy obiekt naley do danej
klasy, mona przy uyciu niniejszego operatora sprawdzi, czy dany obiekt implementuje
okrelony interfejs:
if (anObject instanceof Comparable) { . . . }
Podobnie jak w przypadku klas, mona tworzy hierarchie interfejsw. W ten sposb mog
powstawa acuchy interfejsw przechodzce od najwyszego stopnia uoglnienia do najwy-
szego stopnia specjalizacji. Wyobramy sobie, e mamy interfejs o nazwie Moveable.
public interface Moveable
{
void move(double x, double y);
}
Wtedy nietrudno sobie wyobrazi sens istnienia interfejsu Powered, ktry by go rozszerza:
public interface Powered extends Moveable
{
double milesPerGallon();
}
Podczas gdy w interfejsie nie moe by pl obiektowych ani metod statycznych, mog by
stae. Na przykad:
public interface Powered extends Moveable
{
double milesPerGallon();
double SPEED_LIMIT = 95; // Statyczna finalna staa publiczna.
}
Podczas gdy klasa moe mie tylko jedn nadklas, moe implementowa kilka interfejsw.
Dziki temu programista zyskuje pen dowolno, jeli chodzi o definiowanie zachowa
klasy. Na przykad w jzyku Java istnieje bardzo wany interfejs o nazwie Cloneable
(szczegowy opis tego interfejsu znajduje si w kolejnym podrozdziale). Jeli okrelona
klasa implementuje ten interfejs, bdzie mona za pomoc metody clone klasy Object wyko-
na dokadn kopi obiektu tej klasy. Przypumy zatem, e chcemy, aby tworzona przez nas
klasa miaa zarwno funkcjonalno porwnywania, jak i klonowania. W takim przypadku
wystarczy zaimplementowa dwa opisane do tej pory interfejsy:
class Employee implements Cloneable, Comparable
Rozdzia 6. Interfejsy i klasy wewntrzne 279
Inne jzyki, np. C++, zezwalaj na dziedziczenie po wicej ni jednej klasie. Nazywa si to
dziedziczeniem wielokrotnym (ang. multiple inheritance). Projektanci Javy postanowili
zrezygnowa z tej funkcji, poniewa komplikuje ona jzyk (jak w C++) lub odbija si na
wydajnoci (jak w jzyku Eiffel).
Rysunek 6.1.
Kopiowanie
i klonowanie
Aby stworzona kopia bya cakiem nowym obiektem, ktrego stan pocztkowy jest taki sam
jak pierwowzoru, ale z czasem mogy pojawi si rnice, naley uy metody clone.
Employee copy = original.clone();
copy.raiseSalary(10); // Oryginalny obiekt pozostaje bez zmian.
Operacja ta nie jest jednak wcale prosta. Metoda clone jest metod chronion klasy Object,
a to oznacza, e we wasnym kodzie nie mona do niej uzyska w prosty sposb dostpu.
Tylko klasa Employee moe klonowa obiekty typu Employee. Ograniczenie to nie zostao
wprowadzone bez powodu. Przeanalizujmy, jak moe wyglda implementacja metody clone
w klasie Object. Nie ma ona adnych informacji na temat obiektw do sklonowania, a wic
jedyne wyjcie to robienie kopii pole po polu. Jeli jednak klonowany obiekt zawiera refe-
Rozdzia 6. Interfejsy i klasy wewntrzne 281
Czy takie pytkie kopiowanie w czym przeszkadza? To zaley. Jeli podobiekt wsp-
dzielony przez inny obiekt i jego klon jest niezmienialny, wspdzielenie nie ma adnych
negatywnych skutkw. Jest tak na pewno wtedy, gdy podobiekt naley do niezmienialnej
klasy, np. String. Podobiekt moe te pozosta nietknity przez cay okres ycia obiektu,
jeli adna metoda ustawiajca nie zostanie wywoana na jego rzecz ani adna metoda nie
utworzy do niego referencji.
Czsto jednak zdarza si, e podobiekt jest zmienialny. W takim przypadku konieczne jest
przedefiniowanie metody clone w taki sposb, aby wykonywaa kopiowanie gbokie
(ang. deep copying), polegajce na sklonowaniu take podobiektw. W naszym przykadzie
pole hireDay jest obiektem klasy Date, ktra jest zmienialna.
Trzecia z wymienionych opcji jest domylna. Aby mc wybra ktr z pozostaych, klasa musi:
1. Implementowa interfejs Cloneable.
2. Przedefiniowa metod clone, dodajc do niej modyfikator public.
Metoda clone w klasie Object jest chroniona, przez co nie mona w kodzie po pro-
stu umieci wywoania typu jakiObiekt.clone(). Ale czy metody chronione nie
s dostpne w podklasach i czy kada klasa nie jest podklas klasy Object? Na szczcie
zasady dotyczce dostpu chronionego s bardziej wyrafinowane (zobacz rozdzia 5.).
Podklasa moe wywoa chronion metod clone tylko w celu sklonowania swojego wa-
snego obiektu. Aby obiekty mogy by klonowane przez dowoln metod, metoda clone
musi by przedefiniowana jako publiczna.
W takim przypadku uycie interfejsu Cloneable nie ma nic wsplnego z normalnym zasto-
sowaniem interfejsw. W szczeglnoci nie okrela on metody clone metoda ta jest
dziedziczona po klasie Object. Interfejs jest tu jedynie znacznikiem informujcym, e pro-
jektant klasy wie, na czym polega klonowanie. Obiekty s tak wraliwe na punkcie klono-
wania, e generuj kontrolowany wyjtek, jeli tylko obiekt dajcy sklonowania nie imple-
mentuje interfejsu Cloneable.
Nawet jeli standardowa implementacja metody clone (kopiowanie pytkie) jest wystarczajca,
nadal konieczne jest zaimplementowanie interfejsu Cloneable, przedefiniowanie metody clone
na publiczn i wywoanie metody super.clone(). Spjrzmy na przykad:
class Employee implements Cloneable
{
// Zwikszenie widocznoci na public i zmiana typu zwrotnego.
public Employee clone() throws CloneNotSupportedException
{
return (Employee) super.clone();
}
. . .
}
Przed Java SE 5.0 metoda clone zawsze zwracaa typ Object. Obecnie kowariantne
typy zwrotne umoliwiaj okrelenie odpowiedniego typu dla metod clone.
Metoda clone, ktr ogldalimy przed chwil, nie rozszerza w aden sposb funkcjonalnoci
metody Object.clone. Jedyne jej zadanie to upublicznienie metody. Aby zrobi gbok kopi,
naley bardziej si postara, aby skopiowa zmienialne pola obiektowe.
Rozdzia 6. Interfejsy i klasy wewntrzne 283
// Klonowanie pl zmienialnych.
cloned.hireDay = (Date) hireDay.clone()
return cloned;
}
}
Takie podejcie jest odpowiednie dla klas finalnych. W pozostaych sytuacjach lepiej zostawi
specyfikator throws tam, gdzie jego miejsce.
Naley uwaa na klonowanie podklas. Jeli na przykad metoda clone zostaa zdefiniowana
dla klasy Employee, mona jej uy do klonowania obiektw klasy Manager. Czy metoda clone
klasy Employee poradzi sobie w takiej sytuacji? To zaley od tego, jakie pola zawiera klasa
Manager. W tym przypadku nie ma problemu, poniewa pole bonus jest typu podstawowego.
Jednak klasa Manager mogaby mie pola wymagajce gbokiego kopiowania lub takie, ktre
nie s klonowalne. Nie ma adnej gwarancji, e programista podklasy wnis odpowiednie
poprawki do metody clone, aby odpowiednio wykonywaa swoje zadanie. Z tego powodu
metoda clone w klasie Object jest chroniona. Nie moemy jednak skorzysta z tej wygody,
jeli chcemy, aby uytkownicy naszej klasy wywoywali metod clone.
Czy naley implementowa metod clone we wasnych klasach? Jeli uytkownicy musz
tworzy gbokie kopie ich obiektw, to tak. Niektrzy programici uwaaj, e naley w ogle
unika metody clone i zamiast niej implementowa inn, przeznaczon do tego samego celu.
Zgadzamy si, e metoda clone nie jest idealna, ale nie pozbdziemy si problemw z ni
284 Java. Podstawy
Program na listingach 6.3 i 6.4 klonuje obiekt klasy Employee, a nastpnie wywouje dwie
metody zmieniajce. Metoda raiseSalary zmienia warto pola salary, podczas gdy setHire
Day zmienia stan pola hireDay. adna ze zmian nie ma wpywu na oryginalny obiekt, ponie-
wa metoda clone wykonuje kopiowanie gbokie.
/**
* Ten program demonstruje klonowanie.
* @version 1.10 2002-07-01
* @author Cay Horstmann
*/
public class CloneTest
{
public static void main(String[] args)
{
try
{
Employee original = new Employee("Jan W. Kowalski", 50000);
original.setHireDay(2000, 1, 1);
Employee copy = original.clone();
copy.raiseSalary(10);
copy.setHireDay(2002, 12, 31);
System.out.println("original=" + original);
System.out.println("copy=" + copy);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
}
}
import java.util.Date;
import java.util.GregorianCalendar;
{
private String name;
private double salary;
private Date hireDay;
// Klonowanie pl zmienialnych.
cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
/**
* Ustawia dat zatrudnienia na podany dzie.
* @param year rok zatrudnienia
* @param month miesic zatrudnienia
* @param day dzie zatrudnienia
*/
public void setHireDay(int year, int month, int day)
{
Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();
Pakiet javax.swing zawiera klas Timer, ktr mona wykorzysta do odmierzania czasu. Jeli
na przykad program zawiera zegar, niniejsza klasa moe wysya co sekund sygna, kt-
rego programista uyje do aktualizacji tarczy zegara.
Konstruujc zegar, mona ustawi przedzia czasu oraz zdefiniowa dowolne dziaania, ktre
maj zosta wykonane po upyniciu kadego kolejnego przedziau.
Jak poinformowa zegar, co ma robi? W wielu jzykach programowania w tym celu podaje
si nazw funkcji, ktra ma by wywoywana. Jednak klasy w bibliotece standardowej Javy
s obiektowe. Trzeba przekaza obiekt jednej z tych klas, a zegar wywoa jedn z metod
takiego obiektu. Przekazywany obiekt ma t przewag nad funkcj, e moe zawiera dodat-
kowe dane.
Oczywicie zegar musi wiedzie, ktr metod ma wywoa. Dodatkowo obiekt przekazy-
wany do zegara musi nalee do klasy implementujcej interfejs ActionListener z pakietu
java.awt.event. Oto ten interfejs:
public interface ActionListener
{
void actionPerformed(ActionEvent event);
}
Wyobramy sobie, e chcemy, aby co dziesi sekund drukowany by napis Kiedy usyszysz
dwik, bdzie godzina. Aby wykona to zadanie, mona zdefiniowa klas implementujc
interfejs ActionListener i w metodzie o nazwie actionPerformed umieci wszystkie instruk-
cje, ktre maj zosta wykonane.
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
Rozdzia 6. Interfejsy i klasy wewntrzne 287
Toolkit.getDefaultToolkit().beep();
}
}
Pierwszy parametr konstruktora Timer okrela liczb milisekund, ktre maj dzieli kolejne
powiadomienia chcemy by powiadamiani co dziesi sekund. Drugi parametr to obiekt
nasuchujcy (ang. listener object).
/**
@version 1.00 2000-04-13
@author Cay Horstmann
*/
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
// Powyszy import ma na celu zapobiec konfliktowi z klas java.util.Timer.
t.start();
Po uruchomieniu tego programu trzeba cierpliwie odczeka dziesi sekund. Wprawdzie okno
dialogowe Zamkn program? pojawia si od razu, ale dwik jest odtwarzany po raz pierwszy
po upywie dziesiciu sekund.
javax.swing.JOptionPane 1.2
javax.swing.Timer 1.2
javax.awt.Toolkit 1.0
void beep()
Odtwarza krtki alarm dwikowy.
Poniewa temat ten jest nieco skomplikowany, rozbijemy go na kilka kolejno omwionych
czci:
prezentacj przykadu prostej klasy wewntrznej uzyskujcej dostp do pola
obiektowego swojej klasy zewntrznej;
reguy skadniowe klas wewntrznych;
omwienie klas wewntrznych i przekonanie si, w jaki sposb odbywa si ich
konwersja na zwyke klasy; zbyt wraliwi czytelnicy mog pomin t sekcj;
opis lokalnych klas wewntrznych (ang. local inner class), ktre maj dostp
do zmiennych lokalnych otaczajcych je klas;
wprowadzenie do anonimowych klas wewntrznych (ang. anonymous inner class)
oraz pokazanie, jak si ich uywa do implementacji metod zwrotnych;
przedstawienie sposobu tworzenia wewntrznych klas pomocniczych przy uyciu
statycznych klas wewntrznych.
Zagniedanie wyraa relacje midzy klasami, a nie obiektami. Obiekty klasy LinkedList
nie maj podobiektw typu Iterator lub Link.
Ma to dwie zalety dotyczce kontroli nazw i kontroli dostpu. Poniewa klasa Iterator
jest zagniedona w klasie LinkedList, na zewntrz nazywa si LinkedList::Iterator,
dziki czemu nie ma moliwoci wystpienia konfliktu z inn klas o nazwie Iterator.
W Javie ta zaleta nie ma wikszego znaczenia, poniewa podobnie dziaaj pakiety.
Zauwamy, e klasa Link nie jest prywatnym skadnikiem klasy LinkedList. Jest w peni
ukryta przed pozostaym kodem. Dziki temu mona bezpiecznie uywa w niej pl publicz-
nych. Dostp do nich mog uzyska metody klasy LinkedList (ktra musi mie do nich
dostp), a dla pozostaej czci programu s one niewidoczne. Przed wprowadzeniem
klas wewntrznych w Javie tego rodzaju kontrola nie bya moliwa.
Jednak klasy wewntrzne w Javie maj dodatkow funkcjonalno, ktrej brak klasom
zagniedonym w C++. Obiekt klasy wewntrznej zawiera niejawn referencj do obiektu
klasy zewntrznej, ktry go utworzy. Za pomoc tego wskanika obiekt ma dostp do
stanu obiektu zewntrznego. Szczegy tego mechanizmu s opisane w dalszej czci
tego rozdziau.
W Javie statyczne klasy wewntrzne nie maj tego wskanika. S odpowiednikiem klas
zagniedonych w C++.
Dzieje si co zaskakujcego. Klasa TimePrinter nie ma pola, czyli zmiennej o nazwie beep.
W zamian beep odnosi si do pola obiektu TalkingClock, ktry utworzy obiekt TimePrinter.
Jest to innowacyjne podejcie. Normalnie metoda odwoywaaby si do pl obiektu, ktry
j wywoa. Metody klas wewntrznych maj dostp zarwno do wasnych pl, jak i pl
obiektu zewntrznego.
Aby to dziaao, obiekt klasy wewntrznej zawsze ma niejawn referencj do obiektu, ktry
go utworzy (zobacz rysunek 6.3).
Rysunek 6.3.
Obiekt klasy
wewntrznej
zawiera
referencj
do obiektu klasy
zewntrznej
Ta referencja jest niewidoczna w definicji klasy wewntrznej. Dla jasnoci nazwiemy refe-
rencj do obiektu zewntrznego outer. W takiej sytuacji metoda actionPerformed jest rwno-
wana z ponisz:
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (outer.beep) Toolkit.getDefaultToolkit().beep();
}
Przypominamy jeszcze raz, e outer nie jest sowem kluczowym Javy, a suy nam tylko do
ilustracji mechanizmw rzdzcych klasami wewntrznymi.
Kiedy metoda start tworzy obiekt TimePrinter, kompilator przekazuje referencj this do
aktualnego obiektu TalkingClock konstruktorowi:
ActionListener listener = new TimePrinter(this); // Automatycznie dodany parametr.
Listing 6.6 przedstawia kompletny program testujcy nasz klas wewntrzn. Jeszcze raz
wrmy do kontroli dostpu. Gdyby klasa TimePrinter bya zwyk klas, musiaaby uzyska
dostp do znacznika beep za pomoc metody publicznej klasy TalkingClock. Uycie klasy
wewntrznej zmienia sytuacj na lepsze. Nie ma koniecznoci tworzenia akcesorw, ktrych
potrzebuje tylko jedna klasa.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
/**
* Ten program demonstruje sposb uycia klas wewntrznych.
* @version 1.10 2004-02-27
* @author Cay Horstmann
*/
public class InnerClassTest
{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
/**
* Zegar drukujcy informacje o czasie w rwnych odstpach czasu.
Rozdzia 6. Interfejsy i klasy wewntrzne 293
*/
class TalkingClock
{
private int interval;
private boolean beep;
/**
* Tworzy obiekt TalkingClock.
* @param interval odstp czasu pomidzy kolejnymi komunikatami (w milisekundach)
* @param beep warto true oznacza, e dwik ma by odtwarzany
*/
public TalkingClock(int interval, boolean beep)
{
this.interval = interval;
this.beep = beep;
}
/**
* Wczanie zegara.
*/
public void start()
{
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
Z drugiej strony konstruktor klasy wewntrznej mona napisa przy uyciu nastpujcej
skadni:
outerObject.new InnerClass(parametry konstrukcyjne)
Na przykad:
ActionListener listener = this.new TimePrinter();
W tym przypadku referencja do klasy zewntrznej nowo utworzonego obiektu klasy Time
Printer zostaje ustawiona na referencj this metody, ktra tworzy obiekt klasy wewntrznej.
Jest to najczciej spotykany przypadek. Kwalifikator .this jak zwykle nie jest konieczny.
Mona te jednak ustawi referencj do klasy zewntrznej na inny obiekt, jeli poda si bezpo-
rednio jego nazw. Poniewa klasa TimePrinter jest wewntrzn klas publiczn, mona na
przykad utworzy obiekt TimePrinter dla dowolnego obiektu TalkingClock:
TalkingClock jabberer = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
Czy dodanie funkcjonalnoci, ktra jest bardziej elegancka i interesujca ni potrzebna, jest
dla Javy pocztkiem drogi donikd, ktr przeszo tak wiele jzykw?
Przy podawaniu nazwy klasy w wierszu polece w systemie UNIX naley pamita,
aby symbol $ zastpi symbolem zastpczym. To znaczy program ReflectionTest
naley uruchomi w nastpujcy sposb:
java reflection.ReflectionTest klasaWewnetrzna.TalkingClock\$TimePrinter
a narzdzie javap nastpujco:
javap -private klasaWewnetrzna.TalkingClock\$TimePrinter
Jak wida, kompilator wygenerowa dodatkowe pole obiektowe this$0 dla referencji do klasy
zewntrznej (nazwa this$0 jest utworzona przez kompilator, a wic nie mona uywa jej
w kodzie programu). Jest te parametr konstruktora TalkingClock.
Skoro kompilator moe wykona tak transformacj, czy nie mona by byo zrobi tego wasno-
rcznie? Sprbujmy. Klas TimePrinter zdefiniowalibymy jako zwyk klas, na zewntrz
klasy TalkingClock. Podczas konstrukcji obiektu TimePrinter przekazujemy mu referencj this
obiektu, ktry go tworzy.
class TalkingClock
{
. . .
Dowodzi to, e klasy wewntrzne rzeczywicie maj wiksze moliwoci ni zwyke klasy,
poniewa maj wiksze prawa dostpu.
Niektrych moe ciekawi, w jaki sposb klasy wewntrzne uzyskuj swoje zwikszone prawa
dostpu, skoro s konwertowane na zwyke klasy o dziwnych nazwach maszyna wirtu-
alna nic o nich nie wie. Aby rozwiza t zagadk, zbadajmy jeszcze raz klas TalkingClock za
pomoc programu ReflectionTest:
class TalkingClock
{
private int interval;
private boolean beep;
Zwrmy uwag na statyczn metod access$0, ktr kompilator doda do klasy zewntrznej.
Zwraca ona pole beep obiektu, ktry jest przekazany jako parametr. (Nazwa metody moe
by troch inna, np. access$000 wszystko zaley od kompilatora).
Czy nie stanowi to zagroenia bezpieczestwa? Niestety tak. Wywoanie metody access$0
w celu odczytania wartoci prywatnego pola beep nie jest trudne. Oczywicie nazwa access$0
nie jest dozwolon nazw dla metody w Javie, ale haker znajcy dobrze struktur plikw klas
moe z atwoci utworzy taki plik zawierajcy instrukcje maszyny wirtualnej wywoujce
t metod. Mona do tego celu uy na przykad edytora heksadecymalnego. Poniewa ukryte
metody dostpu s widoczne w obrbie pakietu, kod hakera musiaby zosta umieszczony
w tym samym pakiecie co atakowana klasa.
Podsumowujc, jeli klasa wewntrzna ma dostp do prywatnego pola danych, to take inne
klasy dodane do pakietu klasy zewntrznej bd miay do niego dostp. Zrobienie tego
wymaga jednak determinacji i wiedzy. Programista nie moe przypadkowo uzyska dostpu.
Musi w tym celu utworzy lub zmodyfikowa specjalny plik klasy.
W definicjach klas lokalnych nie stosuje si specyfikatorw dostpu (public lub private).
Nie ma do nich dostpu spoza bloku, w ktrym zostay zdefiniowane.
Wielk zalet klas lokalnych jest to, e pozostaj ukryte przed wiatem zewntrznym. Nie
ma do nich dostpu nawet kod otaczajcej klasy. W omawianym przypadku tylko metoda
start wie o istnieniu klasy TimePrinter.
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
Naley zauway, e klasa TalkingClock nie musi ju zawiera pola beep. Odwouje si do
zmiennej parametrycznej beep metody start.
znajduje si w caoci w metodzie start, wic dlaczego nie powinien mie dostpu do wartoci
zmiennej beep?
Aby metoda actionPerformed dziaaa, klasa TimePrinter musiaa skopiowa warto pola beep
jako zmienn lokaln metody start przed znikniciem wartoci parametru beep. Wanie to si
stao. W powyszym przykadzie kompilator tworzy nazw TalkingClock$1TimePrinter dla
lokalnej klasy wewntrznej. Analiza klasy TalkingClock$1TimePrinter za pomoc programu
ReflectionTest da nastpujcy wynik:
class TalkingClock$1TimePrinter
{
TalkingClock$1TimePrinter(TalkingClock, boolean);
Z punktu widzenia programisty taki dostp do zmiennych lokalnych jest bardzo podanym
rozwizaniem. Umoliwia ono uproszczenie klas wewntrznych dziki redukcji liczby pl,
ktre trzeba zadeklarowa jawnie.
Wiemy ju, e metody klas lokalnych mog si odwoywa wycznie do finalnych zmiennych
lokalnych. Dlatego parametr beep w omawianym przykadzie zosta zadeklarowany jako final.
Zmienna lokalna ze sowem kluczowym final w deklaracji nie moe by modyfikowana po
tym, jak zostanie raz zainicjowana. Dziki temu mamy gwarancj, e zmienna lokalna i jej
kopia w klasie lokalnej maj zawsze t sam warto.
Zmienna counter nie moe by zadeklarowana jako finalna, poniewa musi by aktualizo-
wana. Nie mona jej zastpi typem Integer, poniewa obiekty tego typu s niezmienialne.
Rozwizanie polega na uyciu tablicy o rozmiarze 1:
final int[] counter = new int[1];
for (int i = 0; i < dates.length; i++)
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter[0]++;
300 Java. Podstawy
return super.compareTo(other);
}
};
Zmienna tablicowa jest zadeklarowana jako finalna, ale to jedynie oznacza, e nie moe si
odwoywa do innej tablicy. Elementy tablicy mona bez przeszkd zmienia.
Skadnia zastosowana powyej jest bez wtpienia bardzo enigmatyczna. Jej znaczenie jest
nastpujce: utworzenie nowego obiektu klasy implementujcej interfejs ActionListener,
ktrego wymagana metoda actionPerformed znajduje si w klamrach { }.
NadTyp w tym przypadku moe by interfejsem, np. ActionListener. Klasa wewntrzna imple-
mentuje wtedy ten interfejs. Jeli natomiast NadTyp jest klas, klasa wewntrzna j rozszerza.
Anonimowa klasa wewntrzna nie moe mie konstruktora, poniewa nazwa konstruktora
musi by taka sama jak nazwa klasy, a tak przecie nie jest. W zwizku z tym parametry
Rozdzia 6. Interfejsy i klasy wewntrzne 301
Aby zauway rnic pomidzy tworzeniem obiektu klasy a tworzeniem obiektu anoni-
mowej klasy wewntrznej rozszerzajcej t klas, trzeba uwanie si przyjrze.
Person queen = new Person("Maria");
// Obiekt klasy Person.
Person count = new Person("Dracula") { . . .};
// Obiekt klasy wewntrznej rozszerzajcej klas Person.
Listing 6.7 przedstawia peny kod rdowy programu z zegarem, w ktrym uyto anonimowej
klasy wewntrznej. W porwnaniu z programem z listingu 6.6 ta wersja jest znacznie krtsza,
a przy odrobinie praktyki rwnie atwa do zrozumienia.
package anonymousInnerClass;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
/**
* Ten program demonstruje zastosowanie anonimowych klas wewntrznych.
* @version 1.10 2004-02-27
* @author Cay Horstmann
*/
public class AnonymousInnerClassTest
{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock();
clock.start(1000, true);
}
}
/**
* Zegar drukujcy informacje o czasie w rwnych odstpach czasu.
*/
class TalkingClock
{
/**
* Tworzy obiekt TalkingClock.
* @param interval odstp czasu pomidzy kolejnymi komunikatami (w milisekundach)
* @param beep warto true oznacza, e dwik ma by odtwarzany
*/
public void start(int interval, final boolean beep)
{
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval, listener);
t.start();
}
}
Poniej przedstawiona jest sztuczka o nazwie inicjacja z podwjn klamr (ang. dou-
ble brace initialization), w ktrej wykorzystuje si skadni klas wewntrznych.
Przypumy, e chcemy utworzy list tablicow i przekaza j do metody:
ArrayList<String> friends = new ArrayList<>();
favorites.add("Henryk");
favorites.add("Tomasz");
invite(friends);
Jeli lista nie bdzie ju wicej potrzebna, dobrze by byo zdefiniowa j jako anoni-
mow. Jak wwczas jednak doda do niej elementy? Ano tak:
invite(new ArrayList<String>() {{ add("Henryk"); add("Tomasz"); }})
Zwr uwag na podwjne klamry. Zewntrzne su do utworzenia anonimowej pod-
klasy klasy ArrayList, a wewntrzne to blok konstrukcyjny obiektu (patrz rozdzia 4.).
Czsto wygodnie jest utworzy anonimow podklas, ktra jest niemal identyczna
jak jej nadklasa. Trzeba jednak wwczas uwaa na metod equals. W rozdziale 5.
zalecamy, aby w metodzie tej uywa nastpujcego testu:
if (getClass() != other.getClass()) return false;
W danych dziennika albo debugera czsto zamieszcza si nazw biecej klasy, np.:
Jednak w przypadku metody statycznej to si nie uda. Wywoanie getClass() jest tosa-
me z this.getClass(), a statyczne metody nie maj this. Dlatego w zamian naley uy
poniszego wyraenia:
new Object(){}.getClass().getEnclosingClass() // pobiera klas metody statycznej
Instrukcja new Object() {} tworzy anonimowy obiekt anonimowej podklasy klasy Object,
a metoda getEnclosingClass pobiera jego klas, czyli klas zawierajc statyczn
metod.
Jednak metoda musi zwrci dwie liczby. Problem ten mona rozwiza, definiujc klas
o nazwie Pair przechowujc dwie wartoci:
class Pair
{
private double first;
private double second;
class ArrayAlg
{
public static Pair minmax(double[] values)
{
. . .
return new Pair(min, max);
}
}
Oczywicie sowo Pair jest bardzo czsto uywane, przez co w duym projekcie moe si
zdarzy, e jaki inny programista rwnie wpadnie na doskonay pomys utworzenia klasy
o nazwie Pair, tylko e przechowujcej na przykad dwa acuchy. Aby unikn potencjal-
nego konfliktu nazw, Pair mona uczyni publiczn klas wewntrzn w klasie ArrayAlg.
Wtedy klasa Pair na zewntrz bdzie znana pod nazw ArrayAlg.Pair:
ArrayAlg.Pair p = ArrayAlg.minmax(d);
W tym jednak przypadku, w przeciwiestwie do poprzednich sytuacji, nie chcemy, aby obiekt
Pair zawiera referencje do jakichkolwiek innych obiektw. Tworzenie referencji mona
wyczy, stosujc sowo kluczowe static w deklaracji klasy wewntrznej:
class ArrayAlg
{
public static class Pair
{
. . .
}
. . .
}
Oczywicie tylko klasy wewntrzne mog by statyczne. Statyczna klasa wewntrzna rni
si od zwykej klasy wewntrznej tylko tym, e obiekt takiej klasy nie zawiera referencji do
obiektu klasy zewntrznej, ktry go wygenerowa. W omawianym przykadzie uycie sta-
tycznej metody wewntrznej byo konieczne, poniewa obiekt tej klasy jest tworzony wewntrz
metody statycznej:
public static Pair minmax(double[] d)
{
. . .
return new Pair(min, max);
}
Gdyby klasa Pair nie bya statyczna, kompilator zgosiby bd polegajcy na braku niejaw-
nego obiektu typu ArrayAlg do inicjacji obiektu klasy wewntrznej.
Wewntrznych klas statycznych naley uywa zawsze wtedy, kiedy klasa wewntrzna
nie wymaga dostpu do obiektu klasy zewntrznej. Niektrzy programici statyczne
klasy wewntrzne nazywaj klasami zagniedonymi.
Rozdzia 6. Interfejsy i klasy wewntrzne 305
Listing 6.8 przedstawia kompletny kod rdowy klasy ArrayAlg i klasy zagniedonej Pair.
/**
* Ten program demonstruje zastosowanie statycznych klas wewntrznych.
* @version 1.01 2004-02-27
* @author Cay Horstmann
*/
public class StaticInnerClassTest
{
public static void main(String[] args)
{
double[] d = new double[20];
for (int i = 0; i < d.length; i++)
d[i] = 100 * Math.random();
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
}
class ArrayAlg
{
/**
* Para liczb zmiennoprzecinkowych.
*/
public static class Pair
{
private double first;
private double second;
/**
* Tworzy par dwch liczb zmiennoprzecinkowych.
* @param f pierwsza liczba
* @param s druga liczba
*/
public Pair(double f, double s)
{
first = f;
second = s;
}
/**
* Zwraca pierwsz liczb z pary.
* @return pierwsza liczba
*/
public double getFirst()
{
return first;
}
306 Java. Podstawy
/**
* Zwraca drug liczb z pary.
* @return druga liczba
*/
public double getSecond()
{
return second;
}
}
/**
* Znajduje najwiksz i najmniejsz warto w tablicy.
* @param values tablica liczb zmiennoprzecinkowych
* @return para liczb, w ktrej pierwsza liczba okrela warto najmniejsz, a druga
* najwiksz
*/
public static Pair minmax(double[] values)
{
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min, max);
}
}
Wyobramy sobie, e chcemy utworzy obiekt klasy implementujcej jeden lub wicej inter-
fejsw, ktrych dokadnej charakterystyki w czasie kompilacji jeszcze nie znamy. Jest to trudny
do rozwizania problem. Do utworzenia samej klasy mona uy metody newInstance lub
wykorzysta refleksj w celu znalezienia konstruktora. Nie mona jednak utworzy obiektu
interfejsu. Trzeba utworzy now klas w dziaajcym programie.
Ale przecie nie mona w czasie dziaania programu napisa kodu dla nowych klas. W zamian
trzeba dostarczy obiekt obsugujcy wywoanie, czyli tzw. invocation handler. Jest to
obiekt dowolnej klasy, ktra implementuje interfejs InvocationHandler. Zawiera on tylko
jedn metod:
Object invoke(Object proxy, Method method, Object[] args)
Kiedy wywoywana jest jaka metoda na rzecz obiektu klasy proxy, nastpuje wywoanie
metody invoke obiektu obsugujcego wywoanie przy uyciu obiektu klasy Method i para-
metrw oryginalnego wywoania. Obiekt obsugujcy wywoanie musi si dowiedzie, jak
obsuy wywoanie.
Aby utworzy obiekt klasy proxy, naley uy metody newProxyInstance klasy Proxy.
Niniejsza metoda pobiera trzy parametry:
Mechanizm adowania klas (ang. class loader). Zgodnie z modelem zabezpiecze
Javy dozwolone jest uywanie rnych mechanizmw adowania klas systemowych,
klas pobranych z internetu itd. Wicej na temat tzw. loaderw klas piszemy
w rozdziale 9. w drugim tomie. Obecnie w miejsce tego parametru wstawiamy null,
aby uy domylnego loadera.
Tablica obiektw klasy Class po jednym dla kadego interfejsu, ktry ma by
zaimplementowany.
Obiekt obsugujcy wywoanie.
Dwa pytania pozostaj bez odpowiedzi. Jak zdefiniowa obiekt obsugujcy wywoanie?
Co mona zrobi z powstaym obiektem proxy? Odpowied na powysze pytania zaley
oczywicie od problemu, ktry chcemy rozwiza za pomoc mechanizmu klas proxy. Klasy
te mona wykorzysta do wielu celw, np.:
przesyanie wywoa metod do zdalnych serwerw;
czenie zdarze interfejsu uytkownika z akcjami w dziaajcym programie;
ledzenie wywoa metod w celu uatwienia lokalizacji bdw.
public TraceHandler(Object t)
{
308 Java. Podstawy
target = t;
}
Poniszy kod przedstawia sposb tworzenia obiektu proxy, ktry jest odpowiedzialny za
ledzenie metod:
Object value = . . .;
// Tworzenie osony.
InvocationHandler handler = new TraceHandler(value);
// Tworzenie obiektu proxy dla jednego lub wikszej liczby interfejsw.
Class[] interfaces = new Class[] { Comparable.class };
Object proxy = Proxy.newProxyInstance(null, interfaces, handler);
Dziki temu, jeli metoda ktrego z interfejsw zostanie wywoana na rzecz obiektu proxy,
zostanie wydrukowana jej nazwa i parametry oraz nastpi wywoanie tej metody na rzecz
obiektu value.
Klasa Integer implementuje interfejs Comparable. Obiekty proxy nale do klasy, ktra jest
definiowana w czasie dziaania programu (ma nazw typu $Proxy0). Ta klasa rwnie imple-
mentuje interfejs Comparable, jednak jej metoda compareTo wywouje metod invoke han-
dlera obiektu proxy.
Poniewa tablica zostaa zapeniona obiektami proxy, metoda compareTo wywouje metod
invoke klasy TraceHandler. Ta z kolei metoda drukuje nazw i parametry metody, a nastpnie
wywouje metod compareTo na rzecz opakowanego obiektu Integer.
import java.lang.reflect.*;
import java.util.*;
/**
* Ten program demonstruje uycie klas proxy.
* @version 1.00 2000-04-13
* @author Cay Horstmann
*/
public class ProxyTest
{
public static void main(String[] args)
{
Object[] elements = new Object[1000];
// Szukanie liczby.
int result = Arrays.binarySearch(elements, key);
/**
* Obiekt obsugujcy wywoanie, ktry drukuje nazw metody i parametry, a nastpnie
* wywouje oryginaln metod.
*/
class TraceHandler implements InvocationHandler
{
private Object target;
310 Java. Podstawy
/**
* Tworzy obiekt TraceHandler.
* @param t parametr niejawny wywoania metody
*/
public TraceHandler(Object t)
{
target = t;
}
Metoda println wywouje metod toString na rzecz obiektu proxy. Wywoanie to rwnie
jest przekierowywane do obiektu obsugujcego wywoanie.
Mona zaobserwowa, jak algorytm wyszukiwania binarnego namierza liczb, dzielc prze-
szukiwany obszar na dwie poowy na kadym etapie. Zauwamy, e metoda toString jest
w obiekcie proxy, mimo i nie naley do interfejsu Comparable w kolejnym podrozdziale
dowiemy si, e niektre metody klasy Object s zawsze w obiektach proxy.
Rozdzia 6. Interfejsy i klasy wewntrzne 311
Kada klasa proxy rozszerza klas Proxy. Klasa proxy ma tylko jedno pole obiektowe
obiekt obsugujcy wywoanie, ktre jest zdefiniowane w nadklasie Proxy. Wszystkie dodat-
kowe dane potrzebne do przeprowadzenia dziaa obiektu proxy musz by zapisane w obiek-
cie obsugi wywoania. Na przykad gdy obiekty Comparable uczynilimy obiektami proxy
w programie na listingu 6.9, obiekt TraceHandler opakowywa rzeczywiste obiekty.
Wszystkie klasy proxy przesaniaj metody toString, equals i hashCode klasy Object. Tak jak
wszystkie metody klas proxy, wywouj one tylko metod invoke obiektu obsugujcego wywo-
anie. Pozostae metody klasy Object (np. clone i getClass) nie s przedefiniowywane.
Nazwy klas proxy nie s zdefiniowane. Klasa Proxy w maszynie wirtualnej firmy Sun gene-
ruje nazwy zaczynajce si od przedrostka $Proxy.
Kady mechanizm adujcy klasy i uporzdkowany zestaw interfejsw ma tylko jedn klas
proxy. Jeli zatem wywoamy metod newProxyInstance dwa razy przy uyciu tego samego
mechanizmu adowania klas i tablicy interfejsw, otrzymamy dwa obiekty tej samej klasy.
Klas t mona take uzyska za pomoc metody getProxyClass:
Class proxyClass = Proxy.getProxyClass(null, interfaces);
Klasy proxy s zawsze publiczne i finalne. Jeli wszystkie interfejsy implementowane przez
klas proxy s publiczne, klasa taka nie naley do adnego pakietu. W przeciwnym przy-
padku wszystkie niepubliczne interfejsy musz nalee do tego samego pakietu. Wtedy klasa
proxy rwnie przynaley do tego pakietu.
Aby sprawdzi, czy okrelony obiekt Class reprezentuje klas proxy, naley wywoa metod
isProxyClass klasy Proxy.
java.lang.reflect.InvocationHandler 1.3
java.lang.reflect.Proxy 1.3
Na tym koczy si ostatni rozdzia dotyczcy podstaw jzyka Java. Z interfejsami i klasami
wewntrznymi bdziemy si spotyka bardzo czsto. Natomiast klasy proxy s zaawansowan
technik, ktra ley w sferze zainteresowa przede wszystkim twrcw narzdzi, a nie pro-
gramistw aplikacji. W rozdziale 7. zaczynamy nauk programowania grafiki i interfejsw
uytkownika.
7
Grafika
W tym rozdziale:
Wprowadzenie do biblioteki Swing
Tworzenie ramek
Pozycjonowanie ramek
Wywietlanie informacji w komponencie
Figury 2D
Kolory
Kroje czcionek
Wywietlanie obrazw
Do tej pory tworzylimy tylko programy, ktre pobieray dane z klawiatury, przetwarzay je
i wywietlay wyniki w konsoli. Wikszo uytkownikw oczekuje jednak nieco wicej.
Ani nowoczesne programy, ani strony internetowe nie dziaaj w ten sposb. W tym roz-
dziale zaczynamy nauk pisania programw z graficznym interfejsem uytkownika (GUI).
Nauczymy si przede wszystkim ustawia rozmiar i pooenie okien na ekranie, wywietla
tekst pisany rnymi krojami czcionki, wywietla obrazy itd. Cay zdobyty tu warsztat
przyda nam si w kolejnych rozdziaach, w ktrych bdziemy tworzy ciekawe projekty.
Dwa nastpne rozdziay opisuj przetwarzanie zdarze, takie jak wcinicie klawisza na kla-
wiaturze lub kliknicie przyciskiem myszy, oraz dodawanie do aplikacji takich elementw
interfejsu jak menu i przyciski. Po zapoznaniu si z tymi trzema rozdziaami bdziesz dys-
ponowa wiedz, ktra jest niezbdna do pisania aplikacji graficznych. Bardziej zaawanso-
wane techniki zwizane z programowaniem grafiki zostay opisane w drugim tomie.
W 1996 roku firma Netscape stworzya bibliotek GUI o nazwie IFC (ang. Internet Foundation
Classes), w ktrej zastosowano zupenie inne podejcie. Elementy interfejsu uytkownika,
jak przyciski, menu itd., byy rysowane w pustym oknie. Rola systemu polegaa tylko na
wywietlaniu okien i rysowaniu w nich. Dziki temu widgety firmy Netscape wyglday
i dziaay zawsze tak samo, bez wzgldu na platform. Firma Sun podja wspprac z
Netscape w celu udoskonalenia tej technologii, czego owocem bya biblioteka o nazwie kodo-
wej Swing. Biblioteka ta zostaa udostpniona w postaci dodatku w Java 1.1, a do biblioteki
standardowej wcielono j w Java SE 1.2.
Zgodnie z myl Dukea Ellingtona, e Nic nie ma znaczenia, jeli jest pozbawione swingu,
Swing sta si oficjaln nazw zestawu narzdzi do tworzenia GUI, niewykorzystujcego
systemowych odpowiednikw elementw. Swing wchodzi w skad Java Foundation Classes
(JFC). Biblioteka JFC jest bardzo dua i zawiera wiele innych narzdzi poza Swingiem. Zali-
czaj si do nich interfejsy API dostpnoci, grafiki dwuwymiarowej oraz obsugi operacji
przecigania i upuszczania.
Biblioteka Swing nie zastpia AWT, tylko zostaa zbudowana w oparciu o architek-
tur swojej poprzedniczki. Komponenty Swing oferuj znacznie wiksze moliwoci.
Uywajc biblioteki Swing, zawsze korzysta si z podstawowej biblioteki AWT, zwaszcza
przy obsudze zdarze. Od tej pory piszc Swing, mamy na myli klasy rysujce interfejs
uytkownika, a piszc AWT, mylimy o podstawowych mechanizmach okien, takich jak
obsuga zdarze.
Swing w niewielkim stopniu zaley od platformy, dziki czemu jest mniej podatny
na bdy zwizane z danym systemem.
Sposb dziaania i wygld elementw Swinga jest taki sam na rnych platformach.
Niemniej trzecia z wymienionych zalet moe by uwaana za wad jeli elementy inter-
fejsu uytkownika wygldaj tak samo na wszystkich platformach, to znaczy, e wygldaj
inaczej ni elementy natywne, co z kolei oznacza, e bd sabiej znane uytkownikom.
W pakiecie Swing problem ten zosta rozwizany w bardzo elegancki sposb. Programista
piszcy program przy uyciu klas Swing moe nada mu specyficzny charakter. Rysunki 7.1
i 7.2 przedstawiaj ten sam program w stylu systemu Windows i GTK.
Rysunek 7.1.
Styl systemu
Windows
Dodatkowo firma Sun opracowaa niezaleny od platformy styl o nazwie Metal, ktry p-
niej przez specjalistw od marketingu przechrzczono na Java look and feel. Jednak wik-
szo programistw nadal uywa okrelenia Metal i my rwnie trzymamy si tej konwencji
w tej ksice.
Ze wzgldu na krytyczne gosy kierowane pod adresem stylu Metal, ktry wedug niekt-
rych wydawa si zbyt ciki, w Java SE 5.0 nieco go odwieono (zobacz rysunek 7.3).
Obecnie styl Metal obsuguje wiele motyww rnych wersji kolorystycznych i zestaww
czcionek. Motyw domylny nazywa si Ocean.
316 Java. Podstawy
Rysunek 7.2.
Styl GTK
Rysunek 7.3.
Motyw Ocean
stylu Metal
W Java SE 6 poprawiono obsug natywnego stylu systemu Windows i GTK. Aktualnie apli-
kacje Swing obsuguj schematy kolorw i pulsujce przyciski oraz paski przewijania, ktre
stay si ostatnio modne.
W Java 7 dodano nowy styl o nazwie Nimbus (rysunek 7.4), ale nie jest on domylnie dostpny.
W stylu tym wykorzystywana jest grafika wektorowa zamiast bitmapowej, dziki czemu
interfejsy tworzone przy jego uyciu s niezalene od rozmiaru ekranu.
Niektrzy uytkownicy wol, aby aplikacje w Javie wyglday tak jak inne programy na danej
platformie, inni wol styl Metal, a jeszcze inni preferuj styl cakiem innego producenta. Jak
przekonamy si w rozdziale 8., umoliwienie uytkownikom wyboru dowolnego stylu jest
bardzo atwe.
Rozdzia 7. Grafika 317
Rysunek 7.4.
Styl Nimbus
Kady, kto pisa programy dla systemu Microsoft Windows w jzykach Visual Basic lub C#,
wie, jak duym uatwieniem s graficzne narzdzia do projektowania ukadu i edytory zaso-
bw. Narzdzia te umoliwiaj zaprojektowanie caej wizualnej strony aplikacji oraz automa-
tycznie generuj wikszo (czsto cao) kodu GUI. Dla Javy rwnie dostpne s narzdzia
318 Java. Podstawy
wspomagajce budowanie GUI, ale naszym zdaniem, aby si nimi sprawnie posugiwa,
trzeba najpierw nauczy si robi to samodzielnie. Pozostaa cz tego rozdziau zostaa
powicona opisowi technik wywietlania okien i rysowania w nich rnych elementw.
import java.awt.*;
import javax.swing.*;
/**
* @version 1.32 2007-06-12
* @author Cay Horstmann
*/
public class SimpleFrameTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
SimpleFrame frame = new SimpleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
Rozdzia 7. Grafika 319
Rysunek 7.5.
Najprostsza
widoczna ramka
public SimpleFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
}
Klasy Swing znajduj si w pakiecie javax.swing. Nazwa pakietu javax oznacza, e jest to
pakiet rozszerzajcy Javy, a nie podstawowy. Swing jest uznawany za rozszerzenie ze wzgl-
dw historycznych. Jest jednak dostpny w kadej wersji Java SE od 1.2.
Domylny rozmiar ramki 00 jest raczej mao atrakcyjny. Zdefiniowalimy podklas o nazwie
SimpleFrame, ktrej konstruktor ustawia rozmiar na 300200 pikseli. Jest to jedyna rnica
pomidzy klasami SimpleFrame i JFrame.
W metodzie main klasy SimpleFrameTest tworzony jest obiekt klasy SimpleFrame, ktry nastp-
nie zosta uwidoczniony.
Po drugie, okrelamy, co ma si sta, kiedy uytkownik zamknie ramk aplikacji. W tym przy-
padku chcemy, aby program zosta zamknity. Odpowiedzialna jest za to ponisza instrukcja:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Samo utworzenie ramki nie oznacza, e zostanie ona wywietlona. Ramki na pocztku swojego
istnienia s niewidoczne. Dziki temu programista moe doda do nich wszystkie kompo-
nenty, zanim uka si po raz pierwszy. Aby wywietli ramk, metoda main wywouje na jej
rzecz metod setVisible.
Przed Java SE 5.0 mona byo uywa metody show dziedziczonej przez klas JFrame
po nadklasie Window. Nadklas klasy Window jest Component, ktra take zawiera
metod show. Stosowanie metody Component.show zaczto odradza w Java SE 1.2.
W zamian, aby wywietli komponent, naley uy metody setVisible(true). Natomiast
metoda Window.show nie bya odradzana a do Java SE 1.4. Moliwo uwidocznienia
okna i przeniesienia go na przd bya nawet przydatna. Niestety metoda show dla okien
rwnie jest odradzana od Java SE 5.0.
Uruchomiony program przedstawia rysunek 7.5 jest to zwyke szare okno najwyszego
poziomu. Jak wida, pasek tytuu i pozostae dodatki, jak zaokrglone rogi suce do zmiany
rozmiaru okna, zostay narysowane przez system operacyjny, a nie klasy Swing. Elementy te
bd wyglday inaczej, jeli uruchomimy ten program w systemach Windows, GTK czy
Mac OS X. Biblioteka Swing rysuje wszystko, co znajduje si wewntrz ramki. W tym przy-
padku jej zadanie sprowadza si jedynie do wypenienia domylnym kolorem ta.
Rysunek 7.6.
Hierarchia
dziedziczenia
klas ramek
i komponentw
w pakietach
AWT i Swing
322 Java. Podstawy
Wedug dokumentacji API metody suce do zmieniania rozmiaru i ksztatu ramek znajduj
si w klasach Component (bdcej przodkiem wszystkich obiektw GUI) i Window (bdcej
nadklas klasy Frame). Na przykad do zmiany pooenia komponentu mona uy metody
setLocation z klasy Component. Wywoanie:
setLocation(x, y)
przed wywietleniem okna spowoduje, e system we wasnym zakresie okreli jego poo-
enie (ale nie rozmiar). Zazwyczaj nowe okno jest wywietlane z nieznacznym przesuni-
ciem wzgldem poprzedniego.
Taka para metod typu get-set nazywa si wasnoci (ang. property). Wasno ma nazw
i typ. Nazwa jest taka sama jak sowo uzyskane w wyniku opuszczenia czonu get i zamienie-
nia pierwszej litery powstaego sowa na ma. Na przykad klasa Frame ma wasno o nazwie
title i typie String.
Rozdzia 7. Grafika 323
Z zaoenia title jest wasnoci ramki. Ustawienie (set) niniejszej wasnoci powoduje
zmian tytuu na ekranie uytkownika. Pobranie jej (get) zwraca warto, ktra zostaa wcze-
niej ustawiona.
Nie wiemy (i nie obchodzi nas to), jak klasa Frame implementuje t wasno. Prawdopo-
dobnie wykorzystuje swj odpowiednik ramki do przechowywania tytuu. Moliwe, e ma
nastpujce pole:
private String title; // nie jest wymagane dla wasnoci
Jeli klasa zawiera pasujce pole, nie wiemy (lub nie obchodzi nas to), jak metody dost-
powe i ustawiajce s zaimplementowane. Prawdopodobnie po prostu odczytuj i ustawiaj
dane pole. Moliwe, e robi jeszcze co, np. powiadamiaj system o kadej zmianie tytuu.
Jest tylko jeden wyjtek od konwencji get-set: metody wasnoci typu logicznego zaczy-
naj si od przedrostka is. Na przykad dwie przedstawione poniej metody definiuj wasno
locationByPlatform:
public boolean isLocationByPlatform()
public void setLocationByPlatform(boolean b)
Aby sprawdzi rozmiar ekranu, naley wykona nastpujce dziaania: wywoaj statyczn
metod getDefaultToolkit klasy Toolkit w celu utworzenia obiektu typu Toolkit (klasa
Toolkit jest zbiornikiem rozmaitych metod, ktre wsppracuj z systemem). Nastpnie
wywoaj metod getScreenSize, ktra zwraca rozmiar ekranu w postaci obiektu Dimension.
Obiekt tego typu przechowuje wysoko i szeroko w zmiennych publicznych (!) o nazwach
width i height. Poniej przedstawiamy opisywany fragment programu:
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
Listing 7.2 przedstawia peny kod omawianego programu. Po jego uruchomieniu zwr
uwag na ikon Core Java.
import java.awt.*;
import javax.swing.*;
/**
* @version 1.32 2007-04-14
* @author Cay Horstmann
*/
public class SizedFrameTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new SizedFrame();
frame.setTitle("SizedFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true););
}
});
}
}
// Ustawienie szerokoci i wysokoci ramki oraz polecenie systemowi, aby ustali jej pooenie.
Rozdzia 7. Grafika 325
java.awt.Component 1.0
boolean isVisible()
void setVisible(boolean b)
Sprawdza lub ustawia wasno widocznoci. Komponenty s pocztkowo widoczne
z wyjtkiem komponentw najwyszego poziomu, jak JFrame.
void setSize(int width, int height) 1.1
Ustawia szeroko i wysoko komponentu.
void setLocation(int x, int y) 1.1
Przenosi komponent w inne miejsce. Wsprzdne x i y s wzgldne do kontenera
lub ekranu, jeli komponent jest komponentem najwyszego poziomu (np. JFrame).
void setBounds(int x, int y, int width, int height) 1.1
Przesuwa komponent i zmienia jego rozmiar.
Dimension getSize() 1.1
void setSize(Dimension d) 1.1
Pobiera lub ustawia wasno size komponentu.
326 Java. Podstawy
java.awt.Window 1.0
void toFront()
Przenosi okno przed wszystkie pozostae okna.
void toBack()
Przenosi okno na sam d stosu okien i odpowiednio przestawia pozostae
widoczne okna.
boolean isLocationByPlatform() 5.0
void setLocationByPlatform(boolean b) 5.0
Pobiera lub ustawia wasno locationByPlatform. Jeli zostanie ona ustawiona
przed wywietleniem okna, platforma wybierze odpowiedni lokalizacj.
java.awt.Frame 1.0
boolean isResizable()
void setResizable(boolean b)
Pobiera lub ustawia wasno resizable. Jeli jest ona ustawiona, uytkownik
moe zmienia rozmiar ramki.
String getTitle()
void setTitle(String s)
Pobiera lub ustawia wasno title okrelajc tekst na pasku tytuu.
Image getIconImage()
void setIconImage(Image image)
Pobiera lub ustawia wasno iconImage, ktra okrela ikon ramki. System moe
wywietli ikon jako dodatek w ramce lub w innym miejscu.
boolean isUndecorated() 1.4
void setUndecorated(boolean b) 1.4
Pobiera lub ustawia wasno undecorated. Jeli ta wasno jest ustawiona, ramka
nie zawiera adnych dodatkw, jak pasek tytuu czy przycisk zamykajcy. Ta metoda
musi by wywoana przed wywietleniem ramki.
int getExtendedState() 1.4
void setExtendedState(int state) 1.4
Pobiera lub ustawia stan rozszerzonego okna. Moliwe stany to:
Frame.NORMAL
Frame.ICONIFIED
Frame.MAXIMIZED_HORIZ
Frame.MAXIMIZED_VERT
Frame.MAXIMIZED_BOTH
Rozdzia 7. Grafika 327
java.awt.Toolkit 1.0
javax.swing.ImageIcon 1.2
ImageIcon(String filename)
Tworzy ikon, ktrej obraz jest przechowywany w pliku.
Image getImage()
Pobiera obraz ikony.
Rysunek 7.7.
Ramka
wywietlajca
tekst
Struktura ramki JFrame jest zadziwiajco skomplikowana. Rysunek 7.8 przedstawia schemat
budowy takiej ramki. Jak wida, ramka JFrame skada si z czterech warstw. Trzy z nich
podstawowa (ang. root pane), warstwowa (ang. layered pane) i przezroczysta (ang. glass
pane) nie s dla nas interesujce. Ich przeznaczeniem jest organizacja paska menu i war-
stwy z treci oraz implementacja stylu. Cz interesujca programistw Swing to warstwa
treci (ang. content pane). Podczas projektowania ramki komponenty dodaje si do warstwy
treci, stosujc kod podobny do poniszego:
Container contentPane = frame.getContentPane();
Component c = . . .;
contentPane.add(c);
328 Java. Podstawy
Do Java SE 1.4 metoda add klasy JFrame powodowaa wyjtek wywietlajcy komunikat
Do not use JFrame.add(). Use JFrame.getContentPane().add() instead. W Java SE 5.0
zrezygnowano z takiej edukacji programistw, czego wyrazem jest zezwolenie na wywoy-
wanie metody JFrame.add na rzecz warstwy z treci.
Tym razem chcemy doda do ramki jeden komponent, na ktrym narysujemy nasz komunikat.
Aby mc rysowa na komponencie, naley zdefiniowa klas rozszerzajc klas JComponent
i przesoni w niej metod paintComponent klasy nadrzdnej.
Metoda paintComponent przyjmuje jeden parametr typu Graphics. Obiekt typu Graphics zawiera
ustawienia dotyczce rysowania obrazw i tekstu, jak czcionka czy aktualny kolor. Ryso-
wanie w Javie zawsze odbywa si za porednictwem obiektu Graphics. Udostpnia on metody
rysujce wzory, obrazy i tekst.
Rysunek 7.8.
Wewntrzna
struktura
ramki JFrame
{
kod rysujcy
}
}
Za kadym razem, kiedy okno musi by ponownie narysowane, metoda obsugi zdarze
informuje o tym komponent. Powoduje to uruchomienie metod paintComponent wszystkich
komponentw.
Nigdy nie naley wywoywa metody paintComponent samodzielnie. Jest ona wywoywana
automatycznie, gdy trzeba ponownie narysowa jak cz aplikacji, i nie naley zaburza
tego automatycznego procesu.
Wywietlanie tekstu jest specjalnym rodzajem rysowania. Klasa Graphics udostpnia metod
drawString o nastpujcej skadni:
g.drawString(text, x, y)
Chcemy narysowa acuch: To nie jest program Witaj, wiecie w oryginalnym oknie
w odlegoci okoo jednej czwartej szerokoci od lewej krawdzi i poowy wysokoci od kra-
wdzi grnej. Mimo e nie potrafimy jeszcze mierzy dugoci acuchw, zaczniemy ryso-
wanie w punkcie o wsprzdnych (75, 100). Oznacza to, e pierwsza litera acucha jest
przesunita w prawo o 75 pikseli i w d o 100 pikseli (w rzeczywistoci o 100 pikseli w d
przesunita jest podstawowa linia pisma wicej na ten temat w dalszej czci rozdziau).
Kod opisanej metody paintComponent znajduje si poniej:
class NotHelloWorldComponent extends JComponent
{
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
}
. . .
}
Gdy umiecimy w ramce jakie komponenty i bdziemy chcieli uy ich preferowanych roz-
miarw, to zamiast setSize wywoamy metod pack:
class NotHelloWorldFrame extends JFrame
{
public NotHelloWorldFrame()
{
add(new NotHelloWorldComponent());
pack();
}
}
import javax.swing.*;
import java.awt.*;
/**
* @version 1.32 2007-06-12
Rozdzia 7. Grafika 331
/**
* Ramka zawierajca panel z komunikatem.
*/
class NotHelloWorldFrame extends JFrame
{
public NotHelloWorldFrame()
{
add(new NotHelloWorldComponent());
pack();
}
}
/**
* Panel wywietlajcy komunikat.
*/
class NotHelloWorldPanel extends JComponent
{
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
javax.swing.JFrame 1.2
Container getContentPane()
Zwraca obiekt ContentPane dla ramki JFrame.
332 Java. Podstawy
Component add(Component c)
Dodaje i zwraca dany komponent do warstwy treci ramki (przed Java SE 5.0
ta metoda powodowaa wyjtek).
java.awt.Component 1.0
void repaint()
Powoduje ponowne jak najszybsze narysowanie komponentu.
Dimension getPreferredSize()
Metoda, ktr naley przesoni, aby zwracaa preferowany rozmiar komponentu.
javax.swing.JComponent 1.2
void paintComponent(Graphics g)
Metoda, ktr naley przesoni w celu zdefiniowania sposobu rysowania
okrelonego komponentu.
java.awt.Window 1.0
void pack()
Zmienia rozmiar okna, biorc pod uwag preferowane rozmiary znajdujcych
si w nim komponentw.
7.5. Figury 2D
Od Java 1.0 klasa Graphics udostpnia metody rysujce linie, prostokty, elipsy itd. Ich mo-
liwoci s jednak bardzo ograniczone. Nie ma na przykad moliwoci ustawienia gruboci
linii ani obracania figur.
W Java 1.2 wprowadzono bibliotek Java2D udostpniajc szeroki wachlarz metod gra-
ficznych. W tym rozdziale opisujemy tylko podstawy tej biblioteki wicej bardziej zaawan-
sowanych informacji na ten temat znajduje si w rozdziale 7. w drugim tomie.
Aby narysowa figur biblioteki Java2D, trzeba utworzy obiekt klasy Graphics2D. Klasa ta
jest podklas klasy Graphics. Od Java SE 2 metody takie jak paintComponent automatycznie
odbieraj obiekty klasy Graphics2D. Wystarczy zastosowa rzutowanie:
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
. . .
}
Rozdzia 7. Grafika 333
Biblioteka Java2D obsuguje take bardziej skomplikowane figury, jak uki, krzywe
drugiego i trzeciego stopnia oraz trajektorie. Wicej informacji na ten temat znajduje
si w rozdziale 7. drugiego tomu.
Aby narysowa figur, naley najpierw utworzy obiekt klasy implementujcej interfejs Shape,
a nastpnie wywoa metod draw klasy Graphics2D. Na przykad:
Rectangle2D rect = . . .;
g2.draw(rect);
Czasami jednak obliczenia na liczbach typu float bywaj niewygodne, poniewa Java nie-
wzruszenie wymaga rzutowania, jeli niezbdna jest konwersja wartoci typu double na typ
float. Przeanalizujmy na przykad ponisz instrukcj:
float f = 1.2; // bd
Ta instrukcja spowoduje bd kompilacji, poniewa staa 1.2 jest typu double i kompilator
obawia si utraty danych. Rozwizaniem jest dodanie przyrostka F do staej zmiennoprze-
cinkowej:
float f = 1.2F; // OK
334 Java. Podstawy
Rysunek 7.9.
Klasy
prostoktw 2D
Oznacza to, e uycie klas wewntrznych jest konieczne tylko przy tworzeniu obiektw figur.
Metody klasy Rectangle2D przyjmuj parametry i zwracaj wartoci typu double. Na przykad
metoda getWidth zwraca warto typu double, nawet jeli szeroko jest zapisana w postaci
liczby typu float w obiekcie typu Rectangle2D.Float.
Aby cakowicie pozby si wartoci typu float, naley uywa klas typu Double.
Jednak w programach tworzcych wiele tysicy figur warto rozway uycie klas
Float ze wzgldu na oszczdno pamici.
Wszystko, co napisalimy do tej pory na temat klas Rectangle2D, dotyczy rwnie pozostaych
klas reprezentujcych figury. Dodatkowo istnieje klasa o nazwie Point2D, ktrej podklasy to
Point2D.Float i Point2D.Double. Poniszy fragment programu tworzy obiekt takiej klasy:
Point2D p = new Point2D.Double(10, 20);
Rysunek 7.10.
Prostokt
opisany
na elipsie
Klasa RectangularShape definiuje ponad 20 metod wsplnych dla tych figur. Zaliczaj si
do nich metody getWidth, getHeight, getCenterX i getCenterY (niestety w czasie pisania tej
ksiki nie byo metody getCenter zwracajcej obiekt typu Point2D).
Dodatkowo do hierarchii klas reprezentujcych figury dodano kilka starszych klas z Java 1.0.
Klasy Rectangle i Point, ktre przechowuj prostokt i punkt przy uyciu wsprzdnych
cakowitych, rozszerzaj klasy Rectangle2D i Point2D.
336 Java. Podstawy
Rysunek 7.11 przedstawia relacje pomidzy klasami figur. Klasy Double i Float zostay
pominite, a klasy spadkowe wyrniono szarym tem.
Tworzenie obiektw typu Rectangle2D i Ellipse2D jest prostym zadaniem. Naley poda:
wsprzdne x i y lewego grnego rogu,
wysoko i szeroko.
utworzy elips wpisan w prostokt, ktrego lewy grny rg znajduje si w punkcie o wsp-
rzdnych (150, 200) o szerokoci 100 i wysokoci 50.
Czasami jednak wsprzdne lewego grnego rogu nie s od razu dostpne. Czsto zdarza si,
e dostpne s dwa punkty lece naprzeciw siebie, ale nie s to rogi grny lewy i prawy dolny.
Nie mona utworzy prostokta w poniszy sposb:
Rectangle2D rect = new Rectangle2D.Double(px, py, qx - px, qy - py); // bd
Jeli p nie jest lewym grnym rogiem, jedna lub obie wsprzdne bd miay wartoci
ujemne i prostokt si nie pojawi. W takim przypadku naley najpierw utworzy pusty pro-
stokt i uy metody setFrameFromDiagonal:
Rectangle2D rect = new Rectangle2D.Double();
rect.setFrameFromDiagonal(px, py, qx, qy);
Rozdzia 7. Grafika 337
Jeszcze lepiej, jeli p i q s punktami rogw reprezentowanymi przez obiekty typu Point2D:
rect.setFrameFromDiagonal(p, q);
Przy tworzeniu elipsy zazwyczaj znane s rodek, szeroko i wysoko opisanego na niej
prostokta, a nie jego rogi (ktre nawet nie le na elipsie). Metoda setFrameFromCenter
przyjmuje punkt rodkowy, ale wymaga take jednego z czterech rogw. W zwizku z tym
elips zazwyczaj tworzy si nastpujco:
Ellipse2D ellipse = new Ellipse2D.Double(centerX - width / 2, centerY - height / 2,
width, height);
Aby utworzy lini, naley poda jej punkt pocztkowy i kocowy w postaci obiektw Point2D
lub par liczb:
Line2D line = new Line2D.Double(start, end);
lub
Line2D line = new Line2D.Double(startX, startY, endX, endY);
Program przedstawiony na listingu 7.4 rysuje prostokt, elips znajdujc si wewntrz tego
prostokta, przektn prostokta oraz koo o takim samym rodku jak prostokt. Rysunek 7.12
przedstawia wynik dziaania tego programu.
Rysunek 7.12.
Rysowanie figur
geometrycznych
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* @version 1.32 2007-04-14
* @author Cay Horstmann
*/
public class DrawTest
{
public static void main(String[] args)
{
338 Java. Podstawy
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new DrawFrame();
frame.setTitle("DrawTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca panel z rysunkami.
*/
class DrawFrame extends JFrame
{
public DrawFrame()
{
add(new DrawComponent());
pack();
}
}
/**
* Komponent wywietlajcy prostokty i elipsy.
*/
class DrawComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 400;
private static final int DEFAULT_HEIGHT = 400;
// Rysowanie prostokta.
// Rysowanie elipsy.
// Rysowanie przektnej.
java.awt.geom.RectangularShape 1.2
double getCenterX()
double getCenterY()
double getMinX()
double getMinY()
double getMaxX()
double getMaxY()
Zwraca wsprzdn x lub y punktu rodkowego, punktu o najmniejszych
lub najwikszych wsprzdnych prostokta.
double getWidth()
double getHeight()
Zwraca szeroko lub wysoko prostokta.
double getX()
double getY()
Zwraca wsprzdn x lub y lewego grnego rogu prostokta.
java.awt.geom.Rectangle2D.Double 1.2
java.awt.geom.Rectangle2D.Float 1.2
java.awt.geom.Ellipse2D.Double 1.2
java.awt.geom.Point2D.Double 1.2
Point2D.Double(double x, double y)
Rysuje punkt o podanych wsprzdnych.
java.awt.geom.Line2D.Double 1.2
7.6. Kolory
Metoda setPaint z klasy Graphics2D ustawia kolor, ktry jest stosowany we wszystkich
kolejnych rysunkach graficznych. Na przykad:
g2.setPaint(Color.RED);
g2.drawString("Uwaga!", 100, 100);
Figury zamknite (np. prostokt czy elipsa) mona w takiej sytuacji wypeni za pomoc
metody fill (zamiast draw):
Rectangle2D rect = . . .;
g2.setPaint(Color.RED);
g2.fill(rect); // Wypenienie prostokta rect kolorem czerwonym.
Aby zastosowa kilka kolorw, naley wybra kolor, zastosowa metod draw lub fill,
a nastpnie wybra inny kolor i ponownie zastosowa metod draw lub fill.
Metoda fill rysuje o jeden piksel mniej po prawej i na dole. Jeli na przykad nary-
sujemy prostokt new Rectangle2D.Double(0, 0, 10, 20), to rysunek bdzie obej-
mowa piksele o wsprzdnych x = 10 i y = 20. Jeli wypenimy ten prostokt kolorem,
piksele te nie zostan pokolorowane.
Do definiowania kolorw suy klasa java.awt.Color. W klasie tej dostpnych jest 13 nast-
pujcych predefiniowanych staych reprezentujcych kolory:
BLACK, BLUE, CYAN, DARK_GRAY, GRAY, GREEN, LIGHT_GRAY, MAGENTA, ORANGE, PINK, RED,
WHITE, YELLOW
Rozdzia 7. Grafika 341
Przed Java SE 1.4 stae okrelajce kolory byy pisane maymi literami, np.
Color.red. Byo to sprzeczne z przyjt konwencj pisania staych wielkimi lite-
rami. Obecnie nazwy tych staych mona pisa wielkimi lub, ze wzgldu na zgodno
wsteczn, maymi literami.
Niestandardowy kolor mona zdefiniowa, tworzc obiekt klasy Color i podajc wartoci
trzech skadowych: czerwonego, zielonego i niebieskiego. Warto kadego ze skadnikw
(zajmujcych po jednym bajcie) musi nalee do zbioru 0 255. Poniszy kod przedstawia
sposb wywoania konstruktora klasy Color z parametrami okrelajcymi stopie czerwieni,
niebieskiego i zieleni:
Color(int redness, int greenness, int blueness)
Nazwa Zastosowanie
desktop Kolor ta pulpitu
activeCaption Kolor belki tytuowej aktywnego okna
activeCaptionText Kolor tekstu na belce tytuowej
activeCaptionBorder Kolor obramowania aktywnej belki
inactiveCaption Kolor nieaktywnej belki
inactiveCaptionText Kolor tekstu nieaktywnej belki
inactiveCaptionBorder Kolor obramowania nieaktywnej belki
window To okna
windowBorder Kolor obramowania okna
windowText Kolor tekstu w oknie
menu To menu
menuText Kolor tekstu w menu
text Kolor ta tekstu
textText Kolor tekstu
textInactiveText Kolor tekstu nieaktywnych elementw sterujcych
textHighlight Kolor ta wyrnionego tekstu
textHighlightText Kolor wyrnionego tekstu
control Kolor ta elementw sterujcych
controlText Kolor tekstu w elementach sterujcych
controlLtHighlight Sabe wyrnienie elementw sterujcych
controlHighlight Silne wyrnienie elementw sterujcych
controlShadow Kolor cienia elementw sterujcych
controlDkShadow Ciemniejszy kolor cienia elementw sterujcych
scrollbar Kolor ta dla suwakw
info Kolor ta dla tekstu pomocy
infoText Kolor tekstu pomocy
java.awt.Color 1.0
java.awt.Graphics 1.0
Color getColor()
void setColor(Color c)
Pobiera lub ustawia kolor. Wszystkie nastpne rysunki bd miay ten kolor.
Parametry: c Nowy kolor
java.awt.Graphics2D 1.2
Paint getPaint()
void setPaint(Paint p)
Pobiera lub ustawia wasno paint danego kontekstu graficznego. Klasa Color
implementuje interfejs Paint. W zwizku z tym za pomoc tej metody mona
ustawi atrybut paint na jednolity kolor.
void fill(shape s)
Wypenia figur aktualnym kolorem.
java.awt.Component 1.0
Color getBackground()
void setBackground(Color c)
Pobiera lub ustawia kolor ta.
Parametry: c Nowy kolor ta
Color getForeground()
void setForeground(Color c)
Pobiera lub ustawia kolor frontu.
Parametry: c Nowy kolor frontu
7.7. Czcionki
Program przedstawiony na pocztku tego rozdziau wywietla acuch tekstu pisany domyln
czcionk. Czsto jednak zdarza si, e tekst musi by napisany inn czcionk. Identyfikatorem
czcionki jest jej nazwa. Nazwa czcionki skada si z nazwy rodziny czcionek, np. Helvetica,
i opcjonalnego przyrostka, np. Bold. Na przykad nazwy Helvetica i Helvetica Bold nale
do rodziny czcionek o nazwie Helvetica.
Aby sprawdzi, jakie czcionki s dostpne w danym komputerze, naley wywoa metod
getAvailableFontFamilyNames z klasy GraphicsEnvironment. Ta metoda zwraca tablic nazw
wszystkich dostpnych czcionek w postaci acuchw. Egzemplarz klasy GraphicsEnvi
ronment reprezentujcy rodowisko graficzne systemu uytkownika mona utworzy za
344 Java. Podstawy
Nazwy czcionek mog by znakami towarowymi, a ich projekty mog w niektrych jurys-
dykcjach podlega prawom autorskim. W zwizku z tym dystrybucja czcionek czsto wie
si z uiszczaniem opat licencyjnych ich wacicielom. Oczywicie, podobnie jak s tanie
podrbki drogich perfum, istniej te podrbki czcionek imitujce oryginay. Na przykad
imitacja czcionki Helvetica w systemie Windows nosi nazw Arial.
Dodatkowo pakiet SDK firmy Sun zawsze zawiera trzy rodziny czcionek: Lucida Sans,
Lucida Bright i Lucida Sans Typewriter.
Aby narysowa znak dan czcionk, najpierw trzeba utworzy obiekt klasy Font. Konieczne
jest podanie nazwy i stylu czcionki oraz rozmiaru w punktach drukarskich. Ponisza instrukcja
tworzy obiekt klasy Font:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
Rozdzia 7. Grafika 345
Trzeci argument okrela rozmiar w punktach. Jednostka ta jest powszechnie stosowana w typo-
grafii do okrelania rozmiaru czcionek. Jeden punkt jest rwny 1/72 cala, czyli okoo 0,35 mm.
W konstruktorze klasy Font mona uy logicznej nazwy czcionki zamiast nazwy fizycznej.
Styl (zwyky, pogrubiony, kursywa lub pogrubiona kursywa) okrela drugi argument kon-
struktora Font, ktry moe mie jedn z poniszych wartoci:
Font.PLAIN
Font.BOLD
Font.ITALIC
Font.BOLD + Font.ITALIC
Pliki czcionek mona wczytywa w formatach TrueType lub PostScript type 1. Potrzebny jest
do tego strumie wejciowy dla danej czcionki zazwyczaj z pliku lub adresu URL (wicej
informacji na temat strumieni znajduje si w rozdziale 1. drugiego tomu). Nastpnie naley
wywoa statyczn metod Font.createFont:
URL url = new URL("http://www.fonts.com/Wingbats.ttf");
InputStream in = url.openStream();
Font f1 = Font.createFont(Font.TRUETYPE_FONT, in);
Fonty Javy zawieraj symbole i znaki ASCII. Na przykad znak \u2297 fontu Dialog to
znak .Dostpne s tylko te symbole, ktre zdefiniowano w zestawie znakw Unicode.
Poniszy fragment programu wywietla napis Witaj, wiecie! standardow czcionk bezsze-
ryfow systemu z zastosowaniem pogrubienia i o rozmiarze 14 punktw:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
g2.setFont(sansbold14);
String message = "Witaj, wiecie!";
g2.drawString(message, 75, 100);
Interlinia (ang. leading) to odstp pomidzy wydueniem dolnym jednej linii a wydueniem
grnym nastpnej linii (termin pochodzi od paskw oowiu uywanych przez zecerw do
oddzielania linii). Wysoko (ang. height) czcionki to odlego pomidzy nastpujcymi po
sobie liniami bazowymi i jest rwna sumie wyduenia dolnego, leadingu i wyduenia grnego.
Aby sprawdzi wyduenie dolne lub leading, naley uy metody getLineMetrics klasy Font.
Zwraca ona obiekt klasy LineMetrics, dysponujcy metodami do sprawdzania wyduenia
dolnego i leadingu:
Rozdzia 7. Grafika 347
Aby uatwi sobie zrozumienie techniki wyrodkowywania tekstu, warto sobie uzmysowi,
e metoda getWidth() zwraca szeroko komponentu. Pewna cz tej przestrzeni, bounds.
getWidth(), jest zajmowana przez tekst. Reszta powinna by podzielona na dwie rwne czci,
rozmieszczone po obu stronach tekstu. Ten sam sposb rozumowania dotyczy wysokoci.
Kiedy konieczne jest obliczenie wymiarw ukadu bez uycia metody paintCom
ponent, nie mona uzyska obiektu obrazowania czcionki typu Graphics2D.
W zamian naley wywoa metod getFontMetrics klasy JComponent, a nastpnie metod
getFontRenderContext.
FontRenderContext context = getFontMetrics(f).getFontRenderContext();
Przykadowy program przedstawiony poniej nie tylko drukuje napis, ale take lini bazow
i prostokt otaczajcy napis. Rysunek 7.14 przedstawia wynik dziaania tego programu.
Listing 7.5 zawiera jego kod.
Rysunek 7.14.
Linia bazowa
i prostokt
otaczajcy
acuch
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* @version 1.33 2007-04-14
348 Java. Podstawy
/**
* Ramka z komponentem zawierajcym tekst.
*/
class FontFrame extends JFrame
{
public FontFrame()
{
add(new FontComponent());
pack();
}
}
/**
* Komponent z tekstem w ramce na rodku.
*/
class FontComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
// Rysowanie komunikatu.
g2.setPaint(Color.LIGHT_GRAY);
java.awt.Font 1.0
java.awt.font.LineMetrics 1.2
float getAscent()
Pobiera wyduenie grne czcionki odlego linii bazowej od wierzchokw
wielkich liter.
float getDescent()
Pobiera wyduenie dolne czcionki odlego linii bazowej od podstaw liter
sigajcych dolnej linii pisma.
float getLeading()
Pobiera leading czcionki odstp pomidzy spodem jednej linii tekstu
a wierzchokiem nastpnej.
float getHeight()
Pobiera cakowit wysoko czcionki odlego pomidzy dwiema liniami
bazowymi tekstu (wyduenie dolne + leading + wyduenie grne).
java.awt.Graphics 1.0
Font getFont()
void setFont(Font font)
Pobiera lub ustawia czcionk. Czcionka ta bdzie stosowana w kolejnych
operacjach rysowania tekstu.
Parametry: font Czcionka
java.awt.Graphics2D 1.2
FontRenderContext getFontRenderContext()
Pobiera kontekst wizualizacji czcionki, ktry okrela cechy czcionki w kontekcie
graficznym.
void drawString(String str, float x, float y)
Rysuje acuch przy zastosowaniu aktualnej czcionki i koloru.
Parametry: str acuch
x Wsprzdna x pocztku acucha
y Wsprzdna y linii bazowej acucha
javax.swing.JComponent 1.2
java.awt.FontMetrics 1.0
Obrazy zapisane w postaci plikw na dysku lub w internecie mona wczyta do aplikacji
w Javie i wywietli na obiektach Graphics. Obrazy mona wczytywa na wiele sposobw.
W poniszym przykadzie uyta jest znana nam ju klasa ImageIcon:
Image image = new ImageIcon(filename).getImage();
Program z listingu 7.6 robi nawet wicej, poniewa wypenia cae okno wieloma obrazami.
Rezultat tego wida na rysunku 7.15. Za to kaskadowe wypenienie odpowiedzialna jest
metoda paintComponent. Najpierw rysujemy jeden obraz w lewym grnym rogu, a nastpnie
zapeniamy cae okno za pomoc metody copyArea:
for (int i = 0; i * imageWidth <= getWidth(); i++)
for (int j = 0; j * imageHeight <= getHeight(); j++)
if (i + j > 0)
g.copyArea(0, 0, imageWidth, imageHeight, i * imageWidth, j * imageHeight);
Rysunek 7.15.
Okno wypenione
kopiami jednego
obrazu
package image;
import java.awt.*;
import javax.swing.*;
/**
* @version 1.33 2007-04-14
* @author Cay Horstmann
*/
public class ImageTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ImageFrame();
frame.setTitle("ImageTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca komponent obrazu.
*/
class ImageFrame extends JFrame
{
public ImageFrame()
{
add(new ImageComponent());
Rozdzia 7. Grafika 353
pack();
}
}
/**
* Komponent wywietlajcy powielony obraz.
*/
class ImageComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public ImageComponent()
{
image = new ImageIcon("blue-ball.gif").getImage();
}
g.drawImage(image, 0, 0, null);
// Powielenie obrazu w obrbie komponentu.
java.awt.Graphics 1.0
void copyArea(int x, int y, int width, int height, int dx, int dy)
Kopiuje obszar ekranu.
Parametry: x Wsprzdna x lewego grnego rogu obszaru
rdowego
y Wsprzdna y lewego grnego rogu obszaru
rdowego
width Szeroko obszaru rdowego
height Wysoko obszaru rdowego
dx Odlego w poziomie od obszaru rdowego
do obszaru docelowego
dy Odlego w pionie od obszaru rdowego
do obszaru docelowego
W tym rozdziale:
Podstawy obsugi zdarze
Akcje
Zdarzenia generowane przez mysz
Hierarchia zdarze AWT
rda zdarze dysponuj metodami, w ktrych mona rejestrowa suchaczy zdarze. Kiedy
ma miejsce okrelone zdarzenie, rdo wysya powiadomienie o nim do wszystkich obiektw
nasuchujcych, ktre zostay dla niego zarejestrowane.
Jak mona si spodziewa, informacje o zdarzeniu w jzyku obiektowym, takim jak Java, s
pakowane w obiekcie zdarze (ang. event object). W Javie wszystkie obiekty zdarze na-
le do klasy java.util.EventObject. Oczywicie istniej te podklasy reprezentujce kady
typ zdarzenia, takie jak ActionEvent czy WindowEvent.
Rne rda zdarze mog dostarcza rnego rodzaju zdarze. Na przykad przycisk moe
wysya obiekty ActionEvent, podczas gdy okno wysya obiekty WindowEvent.
Rysunek 8.1.
Relacje pomidzy
rdami zdarze
a suchaczami
Od tej pory obiekt listener bdzie powiadamiany o kadym zdarzeniu akcji w przycisku.
Jak si mona domyli, zdarzenie akcji w przypadku przycisku to jego kliknicie.
Kiedy uytkownik kliknie przycisk, obiekt typu JButton tworzy obiekt typu ActionEvent
i wywouje metod listener.actionPerformed(event), przekazujc do niej ten obiekt zda-
rzenia. rdo zdarze, takie jak przycisk, moe mie kilka suchaczy. W takim przypadku
kliknicie przycisku przez uytkownika powoduje wywoanie metod actionPerformed wszyst-
kich suchaczy.
Rysunek 8.2 przedstawia relacje pomidzy rdem zdarze, suchaczem zdarze i obiek-
tem zdarze.
W tym przypadku za kadym razem, gdy uytkownik kliknie jeden z przyciskw na panelu,
skojarzony z tym przyciskiem obiekt odbierze obiekt typu ActionEvent oznaczajcy kliknicie
przycisku. W odpowiedzi obiekt nasuchujcy zmieni kolor ta panelu.
Przed przejciem do programu, ktry nasuchuje klikni przyciskw, musimy najpierw zapo-
zna si z technik tworzenia i dodawania przyciskw do panelu (wicej informacji na temat
elementw GUI znajduje si w rozdziale 9.).
358 Java. Podstawy
Rysunek 8.2.
Powiadamianie
o zdarzeniach
buttonPanel.add(yellowButton);
buttonPanel.add(blueButton);
buttonPanel.add(redButton);
Nastpnie konieczne jest dodanie procedur nasuchujcych tych przyciskw. Do tego potrzebne
s klasy implementujce interfejs ActionListener, ktry jak ju wspominalimy zawiera
tylko jedn metod: actionPerformed. Sygnatura tej metody jest nastpujca:
public void actionPerformed(ActionEvent event)
Rozdzia 8. Obsuga zdarze 359
Rysunek 8.3.
Panel
z przyciskami
public ColorAction(Color c)
{
backgroundColor = c;
}
Nastpnie dla kadego koloru tworzymy osobny obiekt i kady z nich rejestrujemy jako sucha-
cza przycisku.
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);
360 Java. Podstawy
Jeli uytkownik kliknie na przykad przycisk z napisem ty, zostanie wywoana metoda
actionPerformed obiektu yellowAction. Pole backgroundColor tego obiektu ma warto color.
YELLOW.
Zosta jeszcze tylko jeden problem do rozwizania. Obiekt typu ColorAction nie ma dostpu
do zmiennej buttonPanel. Mona to rozwiza na jeden z dwch sposobw. Mona zapisa
panel w obiekcie ColorAction i skonstruowa go w konstruktorze ColorAction. Wygodniej
jednak byoby, gdyby ColorAction bya klas wewntrzn klasy ButtonFrame. Dziki temu
jej metody miayby automatycznie dostp do zewntrznego panelu (wicej informacji na
temat klas wewntrznych znajduje si w rozdziale 6.).
Jest to bardzo czsto spotykana sytuacja. Obiekty nasuchu zdarze czsto musz wykonywa
dziaania, ktre maj wpyw na inne obiekty. Klas nasuchujc czsto mona umieci
w strategicznym miejscu wewntrz klasy, ktrej obiekt ma mie zmieniony stan.
package button;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z panelem zawierajcym przyciski
*/
public class ButtonFrame extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = 300;
Rozdzia 8. Obsuga zdarze 361
public ButtonFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Tworzenie przyciskw
JButton yellowButton = new JButton("ty");
JButton blueButton = new JButton("Niebieski");
JButton redButton = new JButton("Czerwony");
/**
* Suchacz akcji ustawiajcy kolor ta panelu.
*/
private class ColorAction implements ActionListener
{
private Color backgroundColor;
public ColorAction(Color c)
{
backgroundColor = c;
}
javax.swing.JButton 1.2
JButton(String label)
JButton(Icon icon)
362 Java. Podstawy
java.awt.Container 1.0
Component add(Component c)
Dodaje komponent c do kontenera.
Poniej przedstawiamy dobry przykad tego, jak anonimowe klasy wewntrzne mog upro-
ci kod programu. W programie na listingu 8.1 z kadym przyciskiem zwizane s takie
same dziaania:
1. Utworzenie przycisku z etykiet.
2. Dodanie przycisku do panelu.
Moliwe s dalsze uproszczenia. Zauwamy, e klasa ColorAction jest potrzebna tylko jeden
raz w metodzie makeButton. A zatem mona j przerobi na klas anonimow:
Rozdzia 8. Obsuga zdarze 363
Kod suchacza akcji sta si znacznie prostszy. Metoda actionPerformed odwouje si do zmien-
nej parametrycznej backgroundColor (podobnie jak w przypadku wszystkich zmiennych lokal-
nych wykorzystywanych w klasie wewntrznej, parametr ten musi by finalny).
Nie jest potrzebny aden jawny konstruktor. Jak widzielimy w rozdziale 6., mechanizm klas
wewntrznych automatycznie generuje konstruktor zapisujcy wszystkie finalne zmienne
lokalne, ktre s uywane w jednej z metod klasy wewntrznej.
java.util.EventObject 1.1
Object setSource()
Zwraca referencj do obiektu, w ktrym wystpio zdarzenie.
java.awt.event.ActionEvent 1.1
String getActionCommand()
Zwraca acuch polecenia skojarzonego z danym zdarzeniem akcji. Jeli zdarzenie
pochodzi od przycisku, acuch polecenia jest taki sam jak etykieta przycisku,
chyba e zosta zmieniony za pomoc metody setActionCommand.
364 Java. Podstawy
W tej sytuacji aden z trzech przyciskw nie ma osobnego suchacza. Dysponuj one
wsplnym obiektem, ktrym jest ramka przycisku. W zwizku z tym metoda actionPer
formed musi sprawdzi, ktry przycisk zosta kliknity.
class ButtonFrame extends JFrame implements ActionListener
{
. . .
public void actionPerformed(ActionEvent event)
{
Object source = event.getSource();
if (source == yellowButton) . . .
else if (source == blueButton) . . .
else if (source == redButton ) . . .
else . . .
}
}
Jak wida, metoda ta jest nieco zagmatwana, przez co nie zalecamy jej stosowania.
Jednak klasa EventHandler moe utworzy takiego suchacza automatycznie za pomoc nast-
pujcego wywoania:
EventHandler.create(ActionListener.class, frame, "loadData")
Rozdzia 8. Obsuga zdarze 365
Jeli suchacz wywouje metod z jednym parametrem, ktry mona uzyska z parametru
zdarzenia, mona uy innego rodzaju metody create. Na przykad wywoanie:
EventHandler.create(ActionListener.class, frame, "loadData", "source.text")
jest rwnoznaczne z:
new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
frame.loadData(((JTextField) event.getSource()).getText());
}
}
java.beans.EventHandler 1.4
Zauwamy, e styl Metal jest zlokalizowany w pakiecie javax.swing. Pozostae style znaj-
duj si w pakiecie com.sun.java i nie musz by obecne w kadej implementacji Javy.
Obecnie ze wzgldu na prawa autorskie pakiety stylw systemw Windows i Mac OS X s
dostpne wycznie z wersjami rodowiska uruchomieniowego Javy przeznaczonymi dla
tych systemw.
Drugi sposb polega na dynamicznej zmianie stylu. Naley wywoa statyczn metod
UIManager.setLookAndFeel oraz przekaza do niej nazw klasy wybranego stylu. Nastpnie
wywoujemy statyczn metod SwingUtilities.updateComponentTreeUI w celu odwieenia
caego zbioru komponentw. Metodzie tej wystarczy przekaza tylko jeden komponent,
a pozostae znajdzie ona samodzielnie. Metoda UIManager.setLookAndFeel moe spowodo-
wa kilka wyjtkw, jeli nie znajdzie danego stylu lub jeli wystpi bd podczas adowania
stylu. Jak zwykle nie zgbiamy kodu obsugujcego wyjtki, poniewa szczegowo zajmiemy
si tym w rozdziale 11.
W takiej sytuacji nazw kadego stylu i jego klasy mona uzyska nastpujco:
String name = infos[i].getName();
String className = infos[i].getClassName();
Rozdzia 8. Obsuga zdarze 367
Listing 8.2 przedstawia peny kod programu demonstrujcego przeczanie stylw (zobacz
rysunek 8.4). Program ten jest podobny do programu z listingu 8.1. Idc za rad z poprzed-
niej sekcji, akcj przycisku, polegajc na zmianie stylu, okrelilimy za pomoc metody
pomocniczej makeButton i anonimowej klasy wewntrznej.
package plaf;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z panelem zawierajcym przyciski zmieniajce styl.
*/
public class PlafFrame extends JFrame
{
private JPanel buttonPanel;
public PlafFrame()
{
buttonPanel = new JPanel();
UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
for (UIManager.LookAndFeelInfo info : infos)
makeButton(info.getName(), info.getClassName());
add(buttonPanel);
pack();
}
/**
* Tworzy przycisk zmieniajcy styl.
* @param name nazwa przycisku
* @param plafName nazwa klasy stylu
*/
void makeButton(String name, final String plafName)
{
// Dodanie przycisku do panelu.
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Akcja przycisku przeczenie na nowy styl.
try
{
UIManager.setLookAndFeel(plafName);
SwingUtilities.updateComponentTreeUI(PlafFrame.this);
pack();
}
catch (Exception e)
{
368 Java. Podstawy
Rysunek 8.4.
Zmienianie stylu
e.printStackTrace();
}
}
});
}
}
javax.swing.UIManager 1.2
javax.swing.UIManager.LookAndFeelInfo 1.2
String getName()
Zwraca nazw stylu.
String getClassName()
Zwraca nazw klasy implementujcej dany styl.
Rozdzia 8. Obsuga zdarze 369
Kiedy uytkownik zamyka okno, obiekt klasy JFrame jest rdem zdarzenia WindowEvent.
Aby przechwyci to zdarzenie, konieczny jest odpowiedni obiekt nasuchujcy, ktry naley
doda do listy suchaczy okna ramki.
WindowListener listener = . . .;
frame.addWindowListener(listener);
W Javie klasa, ktra implementuje dany interfejs, musi definiowa wszystkie jego metody.
W tym przypadku oznacza to implementacj siedmiu metod. Przypomnijmy jednak, e inte-
resuje nas tylko jedna z nich, o nazwie windowClosing.
Oczywicie nic nie stoi na przeszkodzie, aby zaimplementowa ten interfejs, wstawi wywo-
anie System.exit(0) do metody windowClosing i napisa sze nicnierobicych funkcji dla
pozostaych metod:
class Terminator implements WindowListener
{
public void windowClosing(WindowEvent e)
{
if (uytkownik potwierdza)
System.exit(0);
}
public void windowOpened(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
370 Java. Podstawy
Pisanie szeciu metod, ktre nic nie robi, jest tym rodzajem pracy, ktrej nikt nie lubi. Zada-
nie to uatwiaj klasy adaptacyjne (ang. adapter class) dostpne z kadym interfejsem nasu-
chujcym w bibliotece AWT, ktry ma wicej ni jedn metod. Klasy te implementuj
wszystkie metody interfejsw, ktrym odpowiadaj, ale metody te nic nie robi. Na przy-
kad klasa WindowAdapter zawiera definicje siedmiu nicnierobicych metod. Oznacza to, e
klasa adaptacyjna automatycznie spenia wymagania techniczne stawiane przez Jav, a doty-
czce implementacji odpowiadajcego jej interfejsu nasuchujcego. Klas adaptacyjn mona
rozszerzy, definiujc w podklasie metody odpowiadajce niektrym, ale nie wszystkim typom
zdarze interfejsu (interfejsy, ktre maj tylko jedn metod, np. ActionListener, nie potrzebuj
metod adaptacyjnych).
Kade zdarzenie okna wygenerowane przez ramk jest przekazywane do obiektu listener za
pomoc wywoania jednej z jego siedmiu metod (zobacz rysunek 8.5). Sze z nich nie robi nic,
a metoda windowClosing wywouje metod System.exit(0), zamykajc tym samym aplikacj.
Utworzenie klasy rozszerzajcej klas adaptacyjn WindowAdapter jest krokiem naprzd, ale
mona posun si jeszcze dalej. Nie ma potrzeby nadawa obiektowi listener nazwy.
Wystarczy napisa:
frame.addWindowListener(new Terminator());
Ale czemu poprzestawa na tym? Klasa nasuchujca moe by anonimow klas wewntrzn
ramki.
Rozdzia 8. Obsuga zdarze 371
Rysunek 8.5.
Obiekt
nasuchujcy
zdarze
dotyczcych okna
frame.addWindowListener(new
WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
if (uytkownik potwierdza)
System.exit(0);
}
});
java.awt.event.WindowListener 1.1
void windowOpened(WindowEvent e)
Jest wywoywana po otwarciu okna.
void windowClosing(WindowEvent e)
Jest wywoywana, kiedy uytkownik wyda polecenie menedera okien,
aby zamkn okno. Okno zostanie zamknite tylko wtedy, gdy zostanie
wywoana jego metoda hide lub dispose.
void windowClosed(WindowEvent e)
Jest wywoywana po zamkniciu okna.
void windowIconified(WindowEvent e)
Jest wywoywana po zminimalizowaniu okna.
void windowDeiconified(WindowEvent e)
Jest wywoywana po przywrceniu okna.
void windowActivated(WindowEvent e)
Jest wywoywana po uaktywnieniu okna. Aktywna moe by tylko ramka
lub okno dialogowe. Z reguy meneder okien zaznacza w jaki sposb aktywne
okno, np. podwietlajc pasek tytuu.
void WindowDeactivated(WindowEvent e)
Jest wywoywana po dezaktywowaniu okna.
java.awt.event.WindowStateListener 1.4
java.awt.event.WindowEvent 1.1
8.2. Akcje
Czsto jedn opcj mona wybra na kilka rnych sposobw. Uytkownik moe wybra
odpowiedni funkcj w menu, nacisn okrelony klawisz lub przycisk na pasku narzdzi.
Zaprogramowanie takiej funkcjonalnoci w modelu zdarze AWT jest proste naley
wszystkie zdarzenia zwiza z tym samym obiektem nasuchujcym. Wyobramy sobie, e
blueAction jest obiektem nasuchujcym akcji, ktrego metoda actionPerformed zmienia
kolor ta na niebieski. Jeden obiekt mona zwiza jako suchacza z kilkoma rdami zda-
rze:
przyciskiem paska narzdzi z etykiet Niebieski;
elementem menu z etykiet Niebieski;
skrtem klawiszowym Ctrl+N.
Dziki temu zmiana koloru bdzie wykonywana zawsze w taki sam sposb, bez znaczenia,
czy wywoa j kliknicie przycisku, wybr elementu menu, czy nacinicie klawisza.
W pakiecie Swing dostpna jest niezwykle przydatna struktura opakowujca polecenia i wi-
ca je z rnymi rdami zdarze interfejs Action. Akcja to obiekt, ktry opakowuje:
opis polecenia (acuch tekstowy i opcjonalna ikona),
parametry niezbdne do wykonania polecenia (w naszym przypadku
wymagany kolor).
Pierwsza z tych metod jest ju nam znana z interfejsu ActionListener. Naley doda, e inter-
fejs Action rozszerza interfejs ActionListener. W zwizku z tym wszdzie, gdzie powinien
si znale obiekt ActionListener, mona uy obiektu Action.
Dwie kolejne metody wczaj i wyczaj akcj oraz sprawdzaj, czy akcja jest aktualnie
wczona. Kiedy akcja jest zwizana z menu lub paskiem narzdzi i jest wyczona, odpo-
wiadajca jej opcja ma kolor szary.
Metody putValue i getValue zapisuj i pobieraj pary nazwa warto z obiektu akcji. Nazwy
akcji i ikony s zapisywane w obiektach akcji za pomoc dwch predefiniowanych acu-
chw: Action.NAME i Action.SMALL_ICON:
action.putValue(Action.NAME, "Niebieski");
action.putValue(Action.SMALL_ICON, new ImageIcon("blue-ball.gif"));
Nazwa Warto
NAME Nazwa akcji wywietlana na przyciskach i elementach menu
SMALL_ICON Maa ikona moe by wywietlana na przyciskach, pasku narzdzi
lub elementach menu
SHORT_DESCRIPTION Krtki opis ikony wywietlany w etykiecie narzdzia
LONG_DESCRIPTION Dugi opis ikony do uytku w pomocy internetowej; aden komponent Swinga
nie uywa tej wartoci
MNEMONIC_KEY Skrt akcji wywietlany na elementach menu (zobacz rozdzia 9.)
ACCELERATOR_KEY Skrt klawiaturowy; aden komponent Swinga nie uywa tej wartoci
ACTION_COMMAND_KEY Uywana w przestarzaej ju metodzie registerKeyboardAction
DEFAULT Wasno pasujca do wszystkiego; aden komponent Swinga nie uywa tej wartoci
Jeli obiekt akcji jest dodawany do menu lub paska narzdzi, jego nazwa i ikona s automatycz-
nie pobierane i wywietlane w menu lub na pasku narzdzi. Warto wasnoci SHORT_DES
CRIPTION zamienia si w dymek opisujcy narzdzie.
Pozostae dwie metody interfejsu Action umoliwiaj powiadamianie innych obiektw, zwasz-
cza menu i paskw narzdzi, ktre s rdem akcji, o zmianach wasnoci obiektu akcji.
Jeli na przykad menu jest dodawane jako obiekt nasuchujcy zmian wasnoci obiektu
akcji i obiekt ten zostanie nastpnie wyczony, menu zostanie wywoane, a nazwa akcji bdzie
szara. Obiekty nasuchu zmian wasnoci s ogln konstrukcj stanowic cz modelu
komponentw JavaBean. Wicej informacji na temat Beanw i ich wasnoci znajduje si
w drugim tomie.
Nie naley zapomina, e Action to interfejs, a nie klasa. Kada klasa implementujca go
musi definiowa wszystkie siedem metod, ktre opisalimy. Na szczcie jaki dobry czo-
wiek napisa klas o nazwie AbstractAction, ktra implementuje wszystkie te metody z wyjt-
kiem actionPerformed. Klasa ta zajmuje si zapisywaniem par nazwa warto i zarzdza-
niem obiektami nasuchujcymi zmian wasnoci. Wystarczy rozszerzy klas AbstractAction
i zdefiniowa metod actionPerformed.
Utworzymy obiekt wykonujcy polecenia zmiany koloru. Zapiszemy nazw polecenia, ikon
i dany kolor. Kolor zapiszemy w tablicy par nazwa warto dostarczanej przez klas
AbstractAction. Poniej znajduje si kod rdowy klasy ColorAction. Konstruktor ustawia
pary nazwa warto, a metoda actionPerformed wykonuje akcj zmiany koloru.
public class ColorAction extends AbstractAction
{
public ColorAction(String name, Icon icon, Color c)
{
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);
putValue("color", c);
putValue(Action.SHORT_DESCRIPTION, "Ustaw kolor panelu na " + name.toLowerCase());
}
Rozdzia 8. Obsuga zdarze 375
Teraz konieczne jest zwizanie akcji z przyciskiem. Jest to atwe, poniewa moemy uy
konstruktora JButton, ktry przyjmuje obiekt typu Action.
JButton blueButton = new JButton(blueAction);
Konstruktor odczytuje nazw i ikon z akcji, ustawia krtki opis jako etykiet oraz ustawia
akcj jako suchacza. Ikony i etykiet przedstawia rysunek 8.6.
Rysunek 8.6.
Przyciski
zawieraj ikony
z obiektw akcji
W kolejnym rozdziale wykaemy, e rwnie atwe jest dodawanie tej samej akcji do menu.
Jest to bardzo powszechny problem. Jednak projektanci biblioteki Swing znaleli dla niego
proste rozwizanie. Kady JComponent posiada trzy mapy wejcia (ang. input maps), z kt-
rych kada odwzorowuje obiekty KeyStroke na zwizane z nimi akcje. Mapy te odpowia-
daj trzem rnym sytuacjom (zobacz tabela 8.2).
Warunek WHEN_FOCUSED powoduje, e ta mapa bdzie sprawdzana, gdy komponent jest aktywny.
Nam potrzebna jest inna mapa. Aktywny jest jeden z przyciskw, nie panel. Do wstawienia
skrtw klawiszy zmieniajcych kolor nadaje si jedna z pozostaych dwch map. W naszym
przykadowym programie uyjemy mapy WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
Klasa InputMap nie odwzorowuje bezporednio obiektw KeyStroke w postaci obiektw Action.
W zamian odwzorowuje w postaci dowolnych obiektw, a druga mapa, zaimplementowana
w klasie ActionMap, mapuje obiekty na akcje. Dziki temu atwiej jest wspdzieli te same
akcje przez skrty klawiaturowe pochodzce z rnych map wejcia.
A zatem kady komponent posiada trzy mapy wejcia i jedn map akcji (ang. action map).
Aby je powiza, trzeba wymyli nazwy dla akcji. Klawisz mona powiza z akcj w nast-
pujcy sposb:
Rozdzia 8. Obsuga zdarze 377
Dokumentacja JDK zaleca stosowanie jako klucza akcji jej nazwy. Naszym zdaniem
nie jest to dobre rozwizanie. Nazwa akcji jest wywietlana na przyciskach i ele-
mentach menu, w zwizku z czym moe si zmienia w zalenoci od kaprysu projek-
tanta interfejsu oraz moe by przetumaczona na wiele jzykw. Takie niestae acuchy
nie s dobrym wyborem w przypadku klawiszy wyszukiwania. Zalecamy wymylenie nazw
akcji niezalenych od wywietlanych nazw.
Poniej znajduje si zestawienie dziaa, ktre trzeba wykona, aby wywoa to samo dziaanie
w odpowiedzi na zdarzenie nacinicia przycisku, wyboru elementu z menu lub nacinicia
klawisza:
1. Utwrz podklas klasy AbstractAction. Mona uy tej samej klasy dla wielu
spokrewnionych akcji.
2. Utwrz obiekt powyszej klasy akcji.
3. Utwrz przycisk lub element menu z obiektu powyszej klasy akcji. Konstruktor
odczyta etykiet i ikon z tego obiektu.
4. W przypadku akcji uruchamianych przez nacinicie klawisza konieczne jest
wykonanie dodatkowych czynnoci. Najpierw naley zlokalizowa komponent
najwyszego poziomu w oknie, np. panel zawierajcy wszystkie pozostae elementy.
5. Pobierz map WHEN_ANCESTOR_OF_FOCUSED_COMPONENT komponentu najwyszego
poziomu. Utwrz obiekt klasy KeyStroke reprezentujcy odpowiedni skrt
klawiaturowy. Utwrz obiekt bdcy kluczem dziaania, np. acuch opisujcy
akcj. Wstaw t par danych (klawisz, klucz dziaania) do mapy wejcia.
6. Pobierz map akcji komponentu najwyszego poziomu. Dodaj par klucz
akcji obiekt akcji do tej mapy.
Listing 8.3 przedstawia kompletny kod programu mapujcego przyciski i klawisze na obiekty
akcji. Mona go wyprbowa kliknicie jednego z przyciskw lub nacinicie kombinacji
klawiszy Ctrl+Z, Ctrl+N lub Ctrl+C spowoduje zmian koloru panelu.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z panelem, ktry demonstruje akcje zmiany koloru.
*/
378 Java. Podstawy
public ActionFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Definicje akcji
Action yellowAction = new ColorAction("ty",
new ImageIcon("yellow-ball.gif"),
Color.YELLOW);
Action blueAction = new ColorAction("Niebieski",
new ImageIcon("blue-ball.gif"), Color.BLUE);
Action redAction = new ColorAction("Czerwony",
new ImageIcon("red-ball.gif"), Color.RED);
putValue("color", c);
}
javax.swing.Action 1.2
boolean isEnabled()
void setEnabled(boolean b)
Pobiera lub ustawia wasno enabled akcji.
void putValue(String key, Object value)
Wstawia par nazwa warto do obiektu akcji.
Parametry: key Nazwa wasnoci, ktra ma zosta zapisana
z obiektem akcji. Moe to by dowolny acuch,
ale jest kilka nazw o z gry zdefiniowanym
znaczeniu zobacz tabel 8.1.
value Obiekt powizany z nazw.
javax.swing.KeyStroke 1.2
javax.swing.JComponent 1.2
Rysunek 8.7.
Program
obsugujcy
zdarzenia myszy
Kiedy uytkownik nacinie przycisk myszy, wywoywane s trzy metody nasuchujce: mouse
Pressed po naciniciu przycisku, mouseReleased po zwolnieniu przycisku myszy i mouse
Clicked. Jeli w sferze zainteresowa le wycznie pene kliknicia, pierwsze dwie z wy-
mienionych metod mona pomin. Wywoujc metody getX i getY na rzecz obiektu klasy
MouseEvent, mona sprawdzi wsprzdne x i y wskanika myszy w chwili kliknicia.
Do rozrnienia pojedynczych, podwjnych i potrjnych (!) klikni suy metoda getClic
kCount.
Aby sprawdzi, ktre modyfikatory zostay ustawione, naley uy maski bitowej. W ory-
ginalnym API dwie maski przyciskw s rwnowane z maskami klawiszy specjalnych,
mianowicie:
BUTTON2_MASK == ALT_MASK
BUTTON3_MASK == META_MASK
Zrobiono tak, aby uytkownicy posiadajcy myszk z jednym przyciskiem mogli naladowa
pozostae przyciski za pomoc klawiszy specjalnych (ang. modifier keys). Od Java SE 1.4
zaproponowano jednak inn metod. Od tej pory istniej nastpujce maski:
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK
Rozdzia 8. Obsuga zdarze 381
SHIFT_DOWN_MASK
CTRL_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
META_DOWN_MASK
Kiedy kursor myszy przesuwa si nad oknem, odbiera ono stay strumie zdarze ruchu
myszy. Zauwamy, e s osobne interfejsy MouseListener i MouseMotionListener. Wyr-
niono je z chci zwikszenia efektywnoci. Kiedy uytkownik przesuwa mysz, powstaje caa
masa zdarze dotyczcych tej czynnoci. Obiekt nasuchujcy, ktry oczekuje na kliknicia,
nie jest zajmowany przez nieinteresujce go zdarzenia ruchu.
Nasz testowy program przechwytuje zdarzenia ruchu i w odpowiedzi na nie zmienia wygld
kursora (na krzyyk). Odpowiedzialna jest za to metoda getPredefinedCursor z klasy Cursor.
Tabela 8.3 przedstawia stae podawane jako argument wspomnianej funkcji oraz reprezento-
wane przez nie kursory w systemie Windows.
CROSSHAIR_CURSOR E_RESIZE_CURSOR
HAND_CURSOR SE_RESIZE_CURSOR
MOVE_CURSOR S_RESIZE_CURSOR
TEXT_CURSOR SW_RESIZE_CURSOR
WAIT_CURSOR W_RESIZE_CURSOR
N_RESIZE_CURSOR NW_RESIZE_CURSOR
Jeli w czasie przesuwania myszy uytkownik kliknie jej przycisk, generowane s wywoa-
nia metody mouseDragged zamiast mouseMoved. Nasz przykadowy program zezwala na prze-
ciganie kwadratw pod kursorem. Efekt ten uzyskalimy, aktualizujc pooenie przeci-
ganego kwadratu, tak aby jego rodek znajdowa si w tym samym miejscu co punkt
centralny myszki. Nastpnie ponownie rysujemy obszar roboczy, aby ukaza nowe pooe-
nie kursora myszy.
public void mouseDragged(MouseEvent event)
{
if (current != null)
{
int x = event.getX();
int y = event.getY();
Rozdzia 8. Obsuga zdarze 383
Metoda mouseMoved jest wywoywana tylko wtedy, gdy kursor znajduje si w obrbie
komponentu. Natomiast metoda mouseDragged jest wywoywana nawet wtedy, gdy
kursor opuci komponent.
Istniej jeszcze dwie inne metody obsugujce zdarzenia myszy: mouseEntered i mouseExited.
S one wywoywane, gdy kursor myszy wchodzi do komponentu lub go opuszcza.
W naszym programie interesuj nas oba rodzaje zdarze generowanych przez mysz. Zdefi-
niowalimy dwie klasy wewntrzne o nazwach MouseHandler i MouseMotionHandler. Pierwsza
z nich jest podklas klasy MouseAdapter, poniewa definiuje tylko dwie z piciu metod interfejsu
MouseListener. Klasa MouseMotionHandler implementuje interfejs MouseMotionListener, co
znaczy, e zawiera definicje obu jego metod. Listingi 8.4 i 8.5 przedstawiaj kod rdowy
omawianego programu.
import javax.swing.*;
/**
* Ramka zawierajca okienko do testowania myszy
*/
public class MouseFrame extends JFrame
{
public MouseFrame()
{
add(new MouseComponent());
pack();
}
}
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
384 Java. Podstawy
/**
* Komponent z dziaaniami myszy, do ktrego mona dodawa (lub z ktrego mona usuwa) kwadraty.
*/
public class MouseComponent extends JComponent
{
private static final int SIDELENGTH = 10;
private ArrayList<Rectangle2D> squares;
private Rectangle2D current;
public MouseComponent()
{
squares = new ArrayList<>();
current = null;
addMouseListener(new MouseHandler());
addMouseMotionListener(new MouseMotionHandler());
}
/**
* Znajduje pierwszy kwadrat zawierajcy punkt.
* @param p punkt
* @return pierwszy kwadrat zawierajcy punkt p
*/
public Rectangle2D find(Point2D p)
{
for (Rectangle2D r : squares)
{
if (r.contains(p)) return r;
}
return null;
}
/**
* Dodaje kwadrat do zbioru.
* @param p rodek kwadratu
*/
public void add(Point2D p)
{
double x = p.getX();
double y = p.getY();
/**
* Usuwa kwadrat ze zbioru.
* @param s kwadrat, ktry ma by usunity
*/
public void remove(Rectangle2D s)
{
if (s == null) return;
if (s == current) current = null;
squares.remove(s);
repaint();
}
// Kwadrat zawierajcy kursor
private class MouseHandler extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
// Dodanie nowego kwadratu, jeli kursor nie jest wewntrz innego kwadratu
current = find(event.getPoint());
if (current == null) add(event.getPoint());
}
java.awt.event.MouseEvent 1.1
int getX()
int getY()
Point getPoint()
Zwraca wsprzdne x (pozioma) i y (pionowa) lub punkt, w ktrym miao miejsce
zdarzenie, mierzc od lewego grnego rogu komponentu bdcego rdem
zdarzenia.
int getClickCount()
Zwraca liczb kolejnych klikni przyciskiem myszy zwizanych z danym
zdarzeniem (odstp czasu oddzielajcy zdarzenia okrelane jako kolejne zaley
od systemu).
java.awt.event.InputEvent 1.1
java.awt.Toolkit 1.0
Klasa EventObject posiada podklas AWTEvent bdc nadklas wszystkich klas zdarzenio-
wych AWT. Rysunek 8.8 przedstawia diagram dziedziczenia zdarze AWT.
Niektre komponenty Swing generuj obiekty zdarzeniowe jeszcze innych typw zdarze.
Rozszerzaj one bezporednio klas EventObject, a nie AWTEvent.
Niektre klasy zdarzeniowe AWT s dla programisty Javy bezuyteczne. Na przykad biblio-
teka AWT wstawia do kolejki zdarze obiekty PaintEvent, ale obiekty te nie s dostarczane
do suchaczy. Programici Javy nie nasuchuj zdarze rysowania. Przesaniaj metod paint
Component, aby mc kontrolowa ponowne rysowanie. Ponadto AWT generuje pewne
zdarzenia, ktre s potrzebne tylko programistom systemowym. Nie opisujemy tych spe-
cjalnych typw zdarze.
ActionListener
AdjustmentListener
FocusListener
ItemListener
KeyListener
MouseListener
MouseMotionListener
MouseWheelListener
WindowListener
WindowFocusListener
WindowStateListener
Tabela 8.4 przedstawia najwaniejsze interfejsy nasuchowe, zdarzenia i rda zdarze biblio-
teki AWT.
Parametry/ Zdarzenia
Interfejs Metody
metody dostpu generowane przez
ActionListener actionPerformed ActionEvent AbstractButton
getActionCommand JComboBox
getModifiers
JTextField
Timer
AdjustmentListener adjustmentValueChanged AdjustmentEvent JScrollbar
getAdjustable
getAdjustmentType
getValue
ItemListener itemStateChanged ItemEvent AbstractButton
getItem JComboBox
getItemSelectable
getStateChange
FocusListener focusGained FocusEvent Component
focusLost
isTemporary
KeyListener keyPressed KeyEvent Component
keyReleased getKeyChar
keyTyped getKeyCode
getKeyModifiersText
getKeyText
isActionText
390 Java. Podstawy
Parametry/ Zdarzenia
Interfejs Metody
metody dostpu generowane przez
MouseListener mousePressed MouseEvent Component
mouseReleased getClickCount
mouseEntered getX
mouseExited
getY
mouseClicked
getPoint
translatePoint
MouseMotionListener mouseDragged MouseEvent Component
mouseMoved
MouseWheelListener MouseWheelMoved MouseWheelEvent Component
getWheelRotation
getScrollAmount
WindowListener windowClosing WindowEvent Window
windowOpened getWindow
windowIconified
windowDeiconified
windowClosed
windowActivated
windowDeactivated
WindowFocusListener windowGainedFocus WindowEvent Window
windowLostFocus getOppositeWindow
WindowStateListener windowStateChanged WindowEvent Window
getOldState
getNewState
Na tym zakoczymy opis technik obsugi zdarze AWT. W nastpnym rozdziale nauczymy
si wykorzystywa najpopularniejsze komponenty Swinga oraz szczegowo przeanalizujemy
generowane przez nie zdarzenia.
9
Komponenty Swing
interfejsu uytkownika
W tym rozdziale:
Swing a wzorzec projektowy Model-View-Controller
Wprowadzenie do zarzdzania rozkadem
Wprowadzanie tekstu
Komponenty wyboru
Menu
Zaawansowane techniki zarzdzania rozkadem
Okna dialogowe
Ten rozdzia opisuje podstawowe komponenty, takie jak komponenty tekstowe, przyciski
i suwaki. S to najczciej uywane komponenty, niezbdne w wikszoci interfejsw. Bar-
dziej zaawansowane komponenty zostay opisane w drugim tomie.
392 Java. Podstawy
Rysunek 9.1.
Miejsce
przy oknie
Rozdzia 9. Komponenty Swing interfejsu uytkownika 393
We wzorcu miejsca przy oknie kontekstem jest pokj, w ktrym spdzamy jak cz
dnia. Przeciwstawne siy to ch usadowienia si w wygodnym miejscu i przyciganie do
wiata. Rozwizanie polega na wygospodarowaniu miejsca przy oknie.
Wzorzec MVC nie jest jedynym wzorcem, ktrego uyto przy projektowaniu bibliotek
AWT i Swing. Oto kilka innych przykadw:
Kontenery i komponenty s przykadami wzorca Composite.
Panel przewijany (ScrollPane) to przykad wzorca Decorator.
Zarzdcy ukadu reprezentuj wzorzec Strategy.
Wan cech wzorcw projektowych jest to, e przenikaj one do kultury. Programici na
caym wiecie wiedz, o co nam chodzi, kiedy mwimy o wzorcu MVC lub Decorator.
Dziki temu wzorce stay si doskonaym narzdziem do opisu problemw zwizanych
z projektowaniem.
Tre np. stan przycisku (wcinity lub nie) lub tekst w polu tekstowym.
Wygld kolor, rozmiar itd.
Zachowanie reakcje na zdarzenia.
Nawet na pierwszy rzut oka taki prosty komponent jak przycisk wykazuje w miar zoone
interakcje pomidzy tymi cechami. Oczywicie wygld przycisku zaley od stylu. Przycisk
w stylu Metal wyglda inaczej ni Windows lub Motif. Dodatkowo na jego wygld ma
wpyw jego stan. Wcinicie przycisku oznacza konieczno ponownego narysowania go
ze zmienionym wygldem. Stan zaley od zdarze odbieranych przez przycisk. Kiedy
uytkownik nacinie przycisk myszy po uprzednim umieszczeniu kursora w obrbie przyci-
sku na ekranie, przycisk ten zostanie wcinity.
Oczywicie uywajc przycisku w programie, nikt nie rozwaa dogbnie jego wewntrz-
nych mechanizmw i cech. To jest przecie zadanie programisty, ktry ten przycisk im-
plementowa. Natomiast programici implementujcy przyciski musz bardziej si nad nimi
skupi. Ich zadanie polega przecie na takim zaimplementowaniu przyciskw i innych
komponentw, aby dziaay bez zarzutw w kadym stylu.
Wzorzec precyzyjnie okrela interakcje pomidzy tymi trzema obiektami. Model przecho-
wuje tre i nie posiada interfejsu uytkownika. W przypadku przycisku nie ma tej treci
duo. Stanowi j tylko niewielki zestaw znacznikw okrelajcych, czy przycisk jest wci-
nity, czy nie, czy jest aktywny, czy nie itd. Bardziej interesujca jest tre w przypadku
pola tekstowego. Jest to obiekt acuchowy przechowujcy aktualny tekst. Nie jest to jed-
nak to samo co widok treci jeli treci jest wicej, ni moe pomieci pole tekstowe,
uytkownik zobaczy tylko cz tekstu (rysunek 9.2).
Rysunek 9.2.
Model i widok
pola tekstowego
Model musi zawiera metody zmieniajce i sprawdzajce tre. Na przykad model teksto-
wy posiada metody dodajce lub usuwajce znaki z aktualnego tekstu i zwracajce ten tekst
w postaci acucha. Nie zapomnijmy, e model nie ma charakteru wizualnego. Rysowanie
danych przechowywanych w modelu naley do obowizkw widoku.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 395
Jedn z zalet wzorca MVC jest to, e model mona przedstawia na rne sposoby, za ka-
dym razem pokazujc inn cz caoci. Na przykad edytor HTML moe oferowa dwa
rwnoczesne widoki treci: widok strony, jakby bya wywietlona w przegldarce (tryb
WYSIWYG), oraz widok rda (rysunek 9.3). Kiedy model jest aktualizowany za pored-
nictwem kontrolera jednego z widokw, oba widoki s informowane o tej zmianie. W momen-
cie odebrania powiadomienia widoki aktualizuj si automatycznie. Oczywicie dla takich
prostych komponentw jak przycisk nie tworzy si wielu widokw tego samego modelu.
Rysunek 9.3.
Dwa oddzielne
widoki tego
samego modelu
Rysunek 9.4.
Relacje pomidzy
modelem,
widokiem
i kontrolerem
Poza byciem waciwym narzdziem do wykonania danego zadania wzorzec MVC by atrak-
cyjny dla projektantw biblioteki Swing z jeszcze jednego powodu pozwala na implemen-
tacj obieralnego wygldu, czyli stylu (ang. pluggable look and feel). Model przycisku czy
pola tekstowego jest niezaleny od stylu, ale oczywicie reprezentacja wizualna jest cakowicie
zalena od projektu interfejsu uytkownika w konkretnym stylu. Kontroler moe zachowy-
wa si rnie. Na przykad w urzdzeniu sterowanym gosem musi obsugiwa cakiem inne
zdarzenia ni na standardowym komputerze z klawiatur i mysz. Dziki oddzieleniu podsta-
wowego modelu od interfejsu uytkownika projektanci biblioteki Swing mog wielokrotnie
wykorzystywa kod w modelach, a nawet przecza styl w trakcie dziaania programu.
Oczywicie wzorce to tylko zestawy wskazwek, a nie cisy zbir zasad. adnego wzorca
nie mona zastosowa we wszystkich sytuacjach. Na przykad wzorzec dotyczcy miejsca przy
Rozdzia 9. Komponenty Swing interfejsu uytkownika 397
Pogld na temat tego, jakiego rodzaju dane s przechowywane przez model przycisku, daje
przedstawiona poniej tabela 9.1, zawierajca wykaz i opis wasnoci interfejsu ButtonModel.
pressed true, jeli przycisk zosta nacinity, a przycisk myszy nie zosta jeszcze zwolniony
selected true, jeli przycisk zosta wczony (uywana w przypadku pl wyboru i przecznikw)
Kady obiekt typu JButton przechowuje obiekt modelu przycisku, ktry mona z niego
wydoby.
JButton button = new JButton("Niebieski");
ButtonModel model = button.getModel();
Przyjrzyjmy si jeszcze raz interfejsowi ButtonModel, aby sprawdzi, czego w nim nie ma.
Model ten nie przechowuje etykiety ani ikony przycisku. Nie ma moliwoci sprawdzenia,
co znajduje si na froncie przycisku, patrzc tylko na jego model (w podrozdziale 9.4.2 o prze-
cznikach przekonamy si, e czysto projektu jest rdem problemw dla programisty).
Warto doda, e ten sam model (czyli DefaultButtonModel) jest uywany dla przyciskw,
przecznikw, pl tekstowych, a nawet elementw menu. Oczywicie kady z tych typw
przyciskw posiada inny widok i kontroler. W stylu Metal przycisk JButton uywa klasy
o nazwie BasicButtonUI do reprezentacji widoku i klasy ButtonUIListener jako kontrolera.
Oglnie z kadym komponentem Swing zwizany jest obiekt widoku, ktrego nazwa koczy
si skrtem UI. Jednak nie kady komponent Swing posiada dedykowany obiekt kontrolera.
Rysunek 9.5.
Panel z trzema
przyciskami
Przyciski te znajduj si na panelu JPanel i podlegaj zarzdcy rozkadu cigego (ang. flow
layout manager), czyli domylnemu zarzdcy rozkadu panelu. Rysunek 9.6 pokazuje, co si
dzieje, kiedy do panelu dodamy wicej przyciskw. Jak wida, kiedy nie ma ju miejsca,
nastpuje przejcie do nowego wiersza.
Ponadto przyciski zostaj na rodku, nawet jeli rozmiar okna si zmienia (rysunek 9.7).
Rozdzia 9. Komponenty Swing interfejsu uytkownika 399
Rysunek 9.6.
Panel
z szecioma
przyciskami
zarzdzanymi
przez zarzdc
rozkadu
Rysunek 9.7.
Zmiana rozmiaru
panelu powoduje
automatyczne
przegrupowanie
przyciskw
Przyciski, pola tekstowe i inne elementy interfejsu uytkownika rozszerzaj klas Component.
Komponenty mog si znajdowa w takich kontenerach jak panele. Poniewa panele same
mog by umieszczane w innych kontenerach, klasa Container dziedziczy po klasie Component.
Rysunek 9.8 przedstawia hierarchi dziedziczenia klasy Component.
Kady kontener posiada domylnego zarzdc rozkadu, ale mona utworzy te wasny.
Na przykad ponisza instrukcja rozmieszcza komponenty w panelu za pomoc klasy Grid
Layout:
panel.setLayout(new GridLayout(4, 4));
Programista dodaje komponenty do kontenera. Metoda add tego kontenera przekazuje kom-
ponent i dane dotyczce jego umiejscowienia do zarzdcy rozkadu.
java.awt.Container 1.0
void setLayout(LayoutManager m)
Ustawia zarzdc rozkadu dla kontenera.
Component add(Component c)
Component add(Component c, Object constraints) 1.1
Dodaje komponent do kontenera i zwraca referencj do tego komponentu.
400 Java. Podstawy
java.awt.FlowLayout 1.0
FlowLayout()
FlowLayout(int align)
FlowLayout(int align, int hgap, int vgap)
Parametry: align Jedna z trzech wartoci: LEFT, CENTER, RIGHT.
hgap Przerwa w poziomie w pikselach (wartoci ujemne
powoduj nachodzenie na siebie elementw).
vgap Przerwa w pionie w pikselach (wartoci ujemne
powoduj nachodzenie na siebie elementw).
Na przykad:
frame.add(component, BorderLayout.SOUTH);
Rozdzia 9. Komponenty Swing interfejsu uytkownika 401
Rysunek 9.9.
Rozkad brzegowy
Rysunek 9.10.
Jeden przycisk
w rozkadzie
brzegowym
Rysunek 9.11.
Panel
umieszczony
w poudniowej
czci ramki
Aby osign tak konfiguracj, najpierw naley utworzy obiekt JPanel, a nastpnie doda
do niego wszystkie przyciski. Domylnym zarzdc rozkadu w panelu jest FlowLayout, ktry
w tym przypadku stanowi dobry wybr. Poszczeglne przyciski naley dodawa do panelu
za pomoc omawianej ju metody add. Pooenie i rozmiar przyciskw s kontrolowane
przez zarzdc rozkadu FlowLayout. Dziki temu przyciski bd si znajdoway na rodku
panelu i nie bd si rozciga na cay dostpny obszar. Na kocu naley doda panel do
panelu z treci ramki.
JPanel panel = new JPanel();
panel.add(yellowButton);
panel.add(blueButton);
panel.add(redButton);
frame.add(panel, BorderLayout.SOUTH);
java.awt.BorderLayout 1.0
BorderLayout()
BorderLayout(int hgap, int vgap)
Tworzy nowy BorderLayout.
Parametry: hgap Przerwa w poziomie w pikselach (wartoci ujemne
powoduj nachodzenie na siebie elementw).
vgap Przerwa w pionie w pikselach (wartoci ujemne
powoduj nachodzenie na siebie elementw).
W konstruktorze obiektu rozkadu siatkowego naley okreli, ile wierszy i kolumn ma zosta
utworzonych.
panel.setLayout(new GridLayout(4, 4));
Rozdzia 9. Komponenty Swing interfejsu uytkownika 403
Rysunek 9.12.
Kalkulator
Listing 9.1 przedstawia kod rdowy tego kalkulatora. Jest to zwyky kalkulator, a nie wersja
z odwrcon notacj polsk, ktra nie wiedzie czemu zyskaa sobie tak du popu-
larno w publikacjach na temat Javy. W tym programie po dodaniu komponentu do ramki
nastpuje wywoanie metody pack. Metoda ta oblicza wysoko i szeroko ramki, wyko-
rzystujc preferowane rozmiary wszystkich komponentw.
package calculator;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Panel z przyciskami kalkulatora i wywietlaczem wyniku.
*/
public class CalculatorPanel extends JPanel
{
private JButton display;
private JPanel panel;
private double result;
private String lastCommand;
private boolean start;
public CalculatorPanel()
{
setLayout(new BorderLayout());
result = 0;
lastCommand = "=";
start = true;
// Dodanie wywietlacza
addButton("7", insert);
addButton("8", insert);
addButton("9", insert);
addButton("/", command);
addButton("4", insert);
addButton("5", insert);
addButton("6", insert);
addButton("*", command);
addButton("1", insert);
addButton("2", insert);
addButton("3", insert);
addButton("-", command);
addButton("0", insert);
addButton(".", insert);
addButton("=", command);
addButton("+", command);
add(panel, BorderLayout.CENTER);
}
/**
* Dodaje przycisk do panelu centralnego.
* @param label etykieta przycisku
* @param listener suchacz przyciskw
*/
/**
* Ta akcja wstawia acuch akcji przycisku na kocu tekstu do wywietlenia.
*/
/**
* Ta akcja wykonuje polecenia okrelone przez akcj przycisku.
*/
if (start)
{
if (command.equals("-"))
{
display.setText(command);
start = false;
}
else lastCommand = command;
}
else
{
calculate(Double.parseDouble(display.getText()));
lastCommand = command;
start = true;
}
}
}
/**
* Wykonuje oczekujce dziaania.
* @param x warto, ktra ma by poczona z poprzednim wynikiem.
*/
Oczywicie niewiele programw ma tak regularny interfejs jak kalkulator. W praktyce wyko-
rzystywane s niewielkie siatki (zazwyczaj skadajce si z jednego wiersza lub jednej
kolumny), za pomoc ktrych ustawia si niektre obszary okna. Na przykad panel z roz-
kadem siatkowym mona wykorzysta do utworzenia szeregu przyciskw o takich samych
rozmiarach.
java.awt.GridLayout 1.0
Tworzy nowy obiekt GridLayout. Parametr rows lub columns (ale nie oba naraz)
moe mie warto zero, co oznacza dowoln liczb komponentw w wierszu
lub kolumnie.
Parametry: rows Liczba wierszy siatki.
columns Liczba kolumn siatki.
hgap Przerwa w poziomie w pikselach (wartoci ujemne
powoduj nachodzenie na siebie elementw).
vgap Przerwa w pionie w pikselach (wartoci ujemne
powoduj nachodzenie na siebie elementw).
Wszystkie trzy wymienione klasy dziedzicz po klasie JTextComponent. Obiektu samej tej
klasy nie mona utworzy, poniewa jest to klasa abstrakcyjna. Z drugiej strony, jak to cz-
sto bywa w Javie, podczas przeszukiwania API poszukiwane metody mog si znajdowa
w nadklasie JTextComponent, a nie w jednej z jej podklas. Na przykad metody pobierajce
i ustawiajce tekst w polu tekstowym i obszarze tekstowym nale do klasy JTextComponent.
javax.swing.text.JTextComponent 1.2
String getText()
void setText(String text)
Pobiera lub ustawia tekst komponentu.
boolean isEditable()
void setEditable(boolean b)
Pobiera lub ustawia wasno editable, ktra okrela, czy uytkownik moe
edytowa zawarto komponentu tekstowego.
Powyszy fragment programu dodaje pole tekstowe i inicjuje je, wstawiajc do niego acuch
acuch testowy. Drugi parametr tego konstruktora ustawia szeroko pola. W tym przy-
padku jest to 20 kolumn. Niestety kolumna nie naley do precyzyjnych jednostek miary.
Jedna kolumna ma szeroko jednego znaku fontu uytego do napisania tekstu. Dziki temu,
jeli spodziewanych jest n znakw tekstu lub mniej, szeroko kolumny mona ustawi na n.
Metoda ta nie sprawdza si jednak dobrze w praktyce. Dla pewnoci naley zawsze doda
1 lub 2 do maksymalnej dugoci danych wejciowych. Ponadto naley pamita, e liczba
kolumn jest tylko wskazwk dla AWT, ktra okrela preferowany rozmiar. Jeli zarzdca
rozkadu jest zmuszony zwikszy lub zmniejszy pole tekstowe, moe odpowiednio dosto-
sowa jego rozmiar. Szeroko kolumny ustawiona w konstruktorze JTextField nie stanowi
grnego limitu znakw, ktre moe wprowadzi uytkownik. Moliwe jest wpisanie du-
szego acucha, ktry po przekroczeniu szerokoci pola bdzie mona przewija. Uytkowni-
cy nie lubi przewijanych pl tekstowych, a wic nie naley skpi dla nich miejsca. Liczb
kolumn mona ustawi ponownie w czasie dziaania programu za pomoc metody setCo
lumns.
Tekst w polu tekstowym mona zmieni w dowolnej chwili za pomoc metody setText z klasy
JTextComponent, o ktrej bya mowa wczeniej. Na przykad:
textField.setText("Dzie dobry!");
Do ustawiania kroju czcionki tekstu wprowadzanego przez uytkownika suy metoda setFont.
javax.swing.JTextField 1.2
JTextField(int cols)
Tworzy puste pole JTextField o szerokoci okrelonej przez podan liczb kolumn.
JTextField(String text, int cols)
Tworzy pole tekstowe zawierajce okrelony acuch znakw i majce okrelon
szeroko w kolumnach.
int getColumns()
void setColumns(int cols)
Pobiera lub ustawia liczb kolumn pola tekstowego.
javax.swing.JComponent 1.2
void revalidate()
Wymusza ponowne ustalenie pooenia i rozmiaru komponentu.
void setFont(Font f)
Ustawia krj czcionki dla komponentu.
java.awt.Component 1.0
void validate()
Ponownie ustala pooenie i rozmiar komponentu. Jeli komponent jest kontenerem,
ponownie ustalane s pooenie i rozmiar zawartych w nim komponentw.
Font getFont()
Pobiera nazw kroju czcionki komponentu.
Konstruktor klasy JLabel pozwala okreli pocztkowy tekst lub ikon oraz opcjonalnie
wyrwnanie treci. Do wyrwnania naley uywa staych z interfejsu SwingConstants.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 409
Interfejs ten definiuje kilka bardzo przydatnych staych, jak LEFT, RIGHT, CENTER, NORTH,
EAST itd. Klasa JLabel jest jedn z wielu klas Swing, ktre implementuj ten interfejs.
W zwizku z tym etykiet z wyrwnaniem do prawej mona utworzy na dwa sposoby:
JLabel label = new JLabel("Nazwa uytkownika: ", SwingConstants.RIGHT);
lub
JLabel label = new JLabel("Nazwa uytkownika: ", JLabel.RIGHT);
Metody setText i setIcon umoliwiaj ustawienie tekstu i ikony etykiety w czasie dziaania
programu.
Etykiety mona pozycjonowa w kontenerze tak samo jak inne komponenty. Oznacza to, e
aby umieci etykiet w podanym miejscu, mona zastosowa opisane wczeniej techniki.
javax.swing.JLabel 1.2
JLabel(String text)
JLabel(Icon icon)
JLabel(String text, int align)
JLabel(String text, Icon icon, int align)
Tworzy etykiet.
Parametry: text Tekst etykiety
icon Ikona etykiety
align Jedna ze staych interfejsu SwingConstants: LEFT
(domylna), CENTER lub RIGHT
String getText()
void setText(String text)
Pobiera lub ustawia tekst etykiety.
Icon getIcon()
void setIcon(Icon icon)
Pobiera lub ustawia ikon etykiety.
410 Java. Podstawy
Pole hasa stanowi jeszcze jeden dowd na potwierdzenie bardzo duych moliwoci wzorca
MVC. Pole hasa wykorzystuje do przechowywania danych te same mechanizmy co zwyke
pole tekstowe, ale jego widok zosta zmieniony w taki sposb, e zamiast wpisywanych zna-
kw pojawiaj si znaki zastpcze.
javax.swing.JPasswordField 1.2
Parametr columns ma takie samo przeznaczenie jak poprzednio. Nadal trzeba pamita o doda-
niu kilku dodatkowych kolumn. Podobnie jak wczeniej, liczba wierszy i kolumn nie ogra-
nicza moliwoci uytkownika. Jeli tekst jest zbyt dugi, pojawi si paski przewijania. Do
zmiany liczby wierszy i kolumn su, podobnie jak wczeniej, metody setRows i setColumns.
Liczby te okrelaj tylko preferowany rozmiar zarzdca rozkadu moe zmniejszy lub
zwikszy rozmiar pola tekstowego.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 411
Rysunek 9.13.
Komponenty
tekstowe
Jeli tekst nie mieci si w obszarze tekstowym, cz tekstu zostaje obcita. Aby tego unikn,
mona wczy zawijanie wierszy:
textArea.setLineWrap(true); // Wczono zawijanie wierszy.
Zawijanie wierszy jest wycznie efektem wizualnym. Nie powoduje ono wstawiania zna-
kw \n.
Ten oglny mechanizm dziaa we wszystkich komponentach, nie tylko obszarach tekstowych.
Aby doda do komponentu paski przewijania, naley umieci go w panelu przewijanym.
Listing 9.2 przedstawia rne komponenty tekstowe. Program wywietla pole tekstowe, pole
hasa oraz obszar tekstowy z paskami przewijania. Pole tekstowe i hasa maj etykiety. Klikni-
cie przycisku Wstaw powoduje wstawienie zawartoci pl tekstowych do obszaru tekstowego.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z przykadowymi komponentami tekstowymi.
*/
412 Java. Podstawy
public TextComponentFrame()
{
final JTextField textField = new JTextField();
final JPasswordField passwordField = new JPasswordField();
add(northPanel, BorderLayout.NORTH);
add(scrollPane, BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
pack();
}
}
javax.swing.JTextArea 1.2
JTextArea()
JTextArea(int rows, int cols)
Rozdzia 9. Komponenty Swing interfejsu uytkownika 413
javax.swing.JScrollPane 1.2
JScrollPane(Component c)
Tworzy panel przewijany wywietlajcy zawarto okrelonego komponentu.
Paski przewijania pojawiaj si, kiedy komponent jest wikszy ni widok.
Rysunek 9.14 przedstawia prosty program zawierajcy dwa pola wyboru. Jedno z nich wcza
lub wycza atrybut kursywy czcionki, a drugie robi to samo z atrybutem pogrubienia.
Zauwamy, e pole po prawej stronie jest aktywne, na co wskazuje prostoktna obwdka.
Kiedy uytkownik kliknie jedno z pl wyboru, nastpuje odwieenie ekranu i zastosowanie
nowych atrybutw czcionki.
Rysunek 9.14.
Pola wyboru
Obok pola wyboru musi si znajdowa etykieta identyfikacyjna. Jej tekst ustala si w kon-
struktorze.
bold = new JCheckBox("Pogrubienie");
Metoda setSelected sprawdza aktualny stan kadego pola wyboru. Jest to false, jeli pole
wyboru nie jest zaznaczone, a true, jeli jest zaznaczone.
Kliknicie pola wyboru przez uytkownika uruchamia akcj. Z polem wyboru jak zaw-
sze wiemy suchacza akcji. W omawianym programie oba pola wspdziel jednego
suchacza.
ActionListener listener = . . .
bold.addActionListener(listener);
italic.addActionListener(listener);
import java.awt.*;
import javax.swing.*;
Rozdzia 9. Komponenty Swing interfejsu uytkownika 415
/**
* @version 1.33 2007-06-12
* @author Cay Horstmann
*/
public class CheckBoxTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new CheckBoxFrame();
frame.setTitle("CheckBoxTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
javax.swing.JCheckBox 1.2
JCheckBox(String label)
JCheckBox(String label, Icon icon)
Tworzy pole wyboru, ktre nie jest pocztkowo zaznaczone.
JCheckBox(String label, boolean state)
Tworzy pole wyboru z podan etykiet o okrelonym stanie pocztkowym.
boolean isSelected ()
void setSelected(boolean state)
Pobiera lub ustawia stan pola wyboru.
9.4.2. Przeczniki
W poprzednim programie mona byo zaznaczy jedno z pl, oba lub nie zaznaczy adnego.
Istnieje wiele sytuacji, w ktrych chcemy, aby uytkownik mg wybra tylko jedn z kilku
opcji. Zaznaczenie jednego pola powoduje automatyczne usunicie zaznaczenia innego.
Grupy tego typu pl s te czsto nazywane grupami przyciskw radiowych, poniewa
dziaaniem przypominaj przyciski wyboru w starym radiu. Wcinicie jednego przycisku
powoduje, e wczeniej wcinity przycisk wyskakuje. Rysunek 9.15 przedstawia typowy
przykad ich zastosowania. Uytkownik ma do wyboru cztery rozmiary czcionki: Maa,
rednia, Dua i Bardzo dua oczywicie moliwy jest wybr tylko jednej opcji naraz.
Implementacja grup przyciskw radiowych w Swingu jest atwa. Dla kadej grupy naley
utworzy obiekt typu ButtonGroup. Nastpnie do tego obiektu dodaje si obiekty typu JRadio
Button. Zadaniem obiektu grupy przyciskw jest wyczanie wczeniej wczonego przyci-
sku w odpowiedzi na wczenie innego.
416 Java. Podstawy
Rysunek 9.15.
Grupa przyciskw
radiowych
(przecznikw)
Wracajc do rysunkw 9.14 i 9.15, mona zauway, e przyciski radiowe wygldaj ina-
czej ni pola wyboru. Te drugie s prostoktne i po zaznaczeniu pokazuje si w nich haczyk.
Przyciski radiowe s okrge, a kiedy si je kliknie, pojawia si w nich kropka.
Listing 9.4 przedstawia kompletny kod programu ustawiajcego rozmiar czcionki za pomoc
przecznikw.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z przykadow etykiet tekstow i przecznikami sucymi do wyboru rozmiaru czcionki
*/
public class RadioButtonFrame extends JFrame
{
private JPanel buttonPanel;
private ButtonGroup group;
private JLabel label;
private static final int DEFAULT_SIZE = 36;
public RadioButtonFrame()
{
// Dodanie przykadowej etykiety tekstowej
// Dodanie przecznikw
addRadioButton("Maa", 8);
addRadioButton("rednia", 12);
addRadioButton("Dua", 18);
addRadioButton("Bardzo dua", 36);
418 Java. Podstawy
add(buttonPanel, BorderLayout.SOUTH);
pack();
}
/**
* Tworzy przecznik ustawiajcy rozmiar czcionki przykadowego tekstu.
* @param name acuch identyfikujcy przecznik
* @param size rozmiar czcionki ustawiany przez ten przecznik
*/
button.addActionListener(listener);
}
}
javax.swing.JRadioButton 1.2
javax.swing.ButtonGroup 1.2
void add(AbstractButton b)
Dodaje przycisk do grupy.
ButtonModel getSelection()
Zwraca model przycisku.
javax.swing.ButtonModel 1.2
String getActionCommand()
Zwraca polecenie akcji modelu przycisku.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 419
javax.swing.AbstractButton 1.2
void setActionCommand(String s)
Ustawia polecenie akcji dla przycisku i jego modelu.
9.4.3. Obramowanie
Jeli jedno okno zawiera kilka grup przecznikw, naley w jaki sposb te grupy oznaczy.
Do tego celu mona uy obramowania Swing. Obramowanie mona zastosowa do kadego
komponentu, ktry rozszerza klas JComponent. Najczciej obramowanie stosuje si wok
panelu, ktry zawiera elementy interfejsu uytkownika, jak przeczniki.
Do wyboru jest kilka rodzajw obramowa, ale sposb ich uycia jest taki sam dla wszystkich.
1. Wywoaj statyczn metod klasy BorderFactory, ktra tworzy obramowanie.
Do wyboru s nastpujce style (zobacz rysunek 9.16):
LoweredBevel (ukos dolny),
RaisedBevel (ukos grny),
Etched (wgbienie),
Line (linia),
Matte (linia) umoliwia okrelenie gruboci poszczeglnych krawdzi,
Empty (pusta) tworzy obramowanie, ktre w ogle nie zajmuje miejsca.
Rysunek 9.16.
Rodzaje
obramowa
Poniszy fragment programu tworzy obramowanie w stylu Etched z tytuem dla panelu:
Border etched = BorderFactory.createEtchedBorder()
Border titled = BorderFactory.createTitledBorder(etched, "Tytu");
panel.setBorder(titled);
Aby sprawdzi, jak wygldaj poszczeglne style obramowa, uruchom program z listingu 9.5.
420 Java. Podstawy
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
/**
* Ramka z przecznikami sucymi do wyboru stylu obramowania.
*/
public class BorderFrame extends JFrame
{
private JPanel demoPanel;
private JPanel buttonPanel;
private ButtonGroup group;
public BorderFrame()
{
demoPanel = new JPanel();
buttonPanel = new JPanel();
group = new ButtonGroup();
Rne obramowania maj rne opcje suce do ustawiania szerokoci i koloru. Szczeg-
owe informacje na ten temat znajduj si w wycigach z API. Wielbicieli obramowa ucieszy
fakt, e istnieje klasa SoftBevelBorder, suca do tworzenia obramowa o mniej ostrych
rogach, oraz e obramowanie LineBorder moe mie take zaokrglone rogi. Wymienione
obramowania mona tworzy wycznie za pomoc konstruktorw klas nie istnieje dla
nich metoda BorderFactory.
javax.swing.BorderFactory 1.2
javax.swing.border.SoftBevelBorder 1.2
SoftBevelBorder(int type)
SoftBevelBorder(int type, Color highlight, Color shadow)
Tworzy obramowanie ukone o mniej ostrych rogach.
Parametry: highlight, shadow Kolory dla efektu trjwymiarowego
type Warto EtchedBorder.RAISED
lub EtchedBorder.LOWERED
javax.swing.border.LineBorder 1.2
javax.swing.JComponent 1.2
Rysunek 9.17.
Lista rozwijalna
Jeli pole listy rozwijalnej jest edytowalne (editable), aktualnie wybran opcj mona edyto-
wa, tak jakby bya polem tekstowym. Dlatego komponent ten jest te nazywany polem
typu kombi czy w sobie elastyczno pl tekstowych z zestawem ustalonych z gry
opcji. Do tworzenia tego typu pl suy klasa JComboBox.
Aby lista rozwijalna bya edytowalna, naley uy metody setEditable. Naley pamita,
e edytowanie ma wpyw wycznie na wybrany element. Nie powoduje to zmiany listy opcji
do wyboru.
Biecy wybr, ktry mg zosta zmodyfikowany, jeli pole jest edytowalne, mona pobra
za pomoc metody getSelectedItem. Jednak w edytowalnej licie element ten moe by
kadego typu, w zalenoci od tego, jaki edytor odbiera dane edytowane przez uytkownika
i zamienia wyniki na obiekty (opis edytorw znajduje si w rozdziale 6. drugiego tomu).
Jeli pole nie jest edytowalne, lepiej zastosowa wywoanie:
combo.getItemAt(combo.getSelectedIndex())
W przedstawionym programie uytkownik moe wybra z listy jeden rodzaj czcionki (Serif,
SansSerif, Monospaced itd.). Moe take wpisa nazw innego fontu.
Ta metoda dodaje acuch na kocu listy. Elementy mona take dodawa w dowolnym
miejscu listy za pomoc metody InsertItemAt:
faceCombo.insertItemAt("Monospaced", 0); // Dodanie opcji na pocztku listy.
Dodawane elementy mog by dowolnego typu lista rozwijalna wywouje metod toString
przed wywietleniem kadego elementu.
Metoda addItem nie dziaa wydajnie przy dodawaniu duej liczby elementw do
listy rozwijalnej. Zamiast niej lepiej utworzy obiekt DefaultComboBoxModel, zapeni
go za pomoc metody addElement, a nastpnie wywoa metod setModel z klasy
JComboBox.
Kiedy uytkownik wybiera element z listy, generowana jest akcja. Aby sprawdzi, ktry
element zosta wybrany, naley wywoa metod getSource na rzecz parametru zdarzenia
w celu uzyskania referencji do listy rozwijalnej, ktra wysaa to zdarzenie. Nastpnie naley
wywoa metod getSelectedItem sprawdzajc, ktry element jest aktualnie wybrany. Zwr-
con warto trzeba rzutowa na odpowiedni typ, zazwyczaj String.
public void actionPerformed(ActionEvent event)
{
label.setFont(new Font(
faceCombo.getItemAt(faceCombo.setSelectedIndex()),
Font.PLAIN,
DEFAULT_SIZE));
}
package comboBox;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z przykadow etykiet tekstow i list rozwijaln umoliwiajc wybr kroju czcionki.
*/
Rozdzia 9. Komponenty Swing interfejsu uytkownika 425
public ComboBoxFrame()
{
// Dodanie tekstu etykiety
// Suchacz listy rozwijalnej zmienia krj pisma etykiety na czcionk wybran przez uytkownika
faceCombo.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
label.setFont(new
Font(faceCombo.getItemAt(faceCombo.getSelectedIndex()), Font.PLAIN,
DEFAULT_SIZE));
}
});
Do tworzenia list opcji widocznych cay czas suy komponent JList. Zosta on
opisany w rozdziale 6. drugiego tomu.
javax.swing.JComboBox 1.2
boolean isEditable()
void setEditable(boolean b)
Pobiera lub ustawia wasno editable listy rozwijalnej.
426 Java. Podstawy
9.4.5. Suwaki
Listy rozwijalne pozwalaj na wybr jednej z kilku opcji. Suwaki natomiast umoliwiaj
wybr opcji z szerszego spektrum wartoci, na przykad jednej ze stu.
Jeli parametry okrelajce warto minimaln, maksymaln i pocztkow nie zostan podane,
bd miay wartoci odpowiednio 0, 100 i 50.
Przedstawione konstruktory tworz zwyke suwaki, jak pierwszy na rysunku 9.18. Nieba-
wem nauczymy si ozdabia suwaki rozmaitymi dodatkami.
Rysunek 9.18.
Suwaki
Suwak mona przyozdobi podziak (ang. ticks). W omawianym programie dla drugiego
suwaka zastosowano nastpujce ustawienia:
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
Dugie i krtkie kreski s wzajemnie niezalene. Mona na przykad ustawi dug kresk
co 20 jednostek i krtk co siedem, ale taka skala nie byaby zbyt klarowna.
Gak suwaka mona zmusi, aby przyklejaa si do kresek podziaki. Kiedy uytkownik
przecignie gak suwaka i j puci, zostanie ona natychmiast dosunita do najbliszej kreski.
Za aktywowanie tego trybu odpowiada ponisza procedura:
slider.setSnapToTicks(true);
Funkcja dosuwania nie dziaa tak dobrze, jak mona by byo sobie tego yczy.
Dopki gaka suwaka rzeczywicie nie zostanie dosunita, obiekt nasuchujcy
zmian raportuje wartoci, ktre nie odpowiadaj kreskom podziaki. Ponadto kliknicie
obok gaki takiego suwaka (czynno ta w innych suwakach powoduje przesunicie gaki
w stron kliknicia) nie powoduje jej przesunicia do kolejnej kreski.
Istnieje te moliwo zastosowania innych znacznikw, takich jak acuchy lub ikony
(rysunek 9.18). Czynnoci z tym zwizane s nieco skomplikowane. Trzeba zapeni tablic
mieszajc (ang. hash table) kluczami typu Integer i wartociami typu Component. Nastp-
nie wywouje si metod setLabelTable. Komponenty zostan umieszczone pod kreskami.
Zazwyczaj w takim przypadku uywane s obiekty typu JLabel. Poniszy fragment programu
ustawia etykiety A, B, C, D, E, F:
Hashtable<Integer, Component> labelTable = new Hashtable<Integer, Component>();
labelTable.put(0, new JLabel("A"));
labelTable.put(20, new JLabel("B"));
. . .
labelTable.put(100, new JLabel("F"));
slider.setLabelTable(labelTable);
Program przedstawiony na listingu 9.7 zawiera take suwaki z ikonami jako etykietami kre-
sek podziaki.
Jeli nie wida etykiet lub kresek podziaki, naley si upewni, czy zostay wywo-
ane metody setPaintTicks(true) i setPaintLabels(true).
Czwarty suwak na rysunku 9.18 jest pozbawiony prowadnicy. Za jej usunicie odpowie-
dzialna jest ponisza procedura:
slider.setPaintTrack(false);
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
/**
* Ramka zawierajca kilka suwakw oraz pole tekstowe pokazujce wartoci ustawiane za ich pomoc
*/
public class SliderFrame extends JFrame
{
private JPanel sliderPanel;
private JTextField textField;
private ChangeListener listener;
public SliderFrame()
{
Rozdzia 9. Komponenty Swing interfejsu uytkownika 429
// Zwyky suwak
// Suwak z podziak
slider.setPaintLabels(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Etykiety");
slider.setLabelTable(labelTable);
addSlider(slider, "Niestandardowe etykiety");
slider.setLabelTable(labelTable);
addSlider(slider, "Ikony");
/**
* Dodaje suwak do panelu suwakw i wie suchacza.
* @param s suwak
Rozdzia 9. Komponenty Swing interfejsu uytkownika 431
javax.swing.JSlider 1.2
JSlider()
JSlider(int direction)
JSlider(int min, int max)
JSlider(int min, int max, int initialValue)
JSlider(int direction, int min, int max, int initialValue)
Tworzy poziomy suwak o okrelonym kierunku oraz wartociach minimalnej,
maksymalnej i pocztkowej.
Parametry: direction SwingConstants.HORIZONTAL
lub SwingConstants.VERTICAL domylna
jest pierwsza z wymienionych wartoci.
min, max Najmniejsza i najwiksza warto suwaka.
Domylne wartoci to 0 i 100.
initialValue Warto pocztkowa suwaka domylnie 50.
void setPaintTicks(boolean b)
Jeli parametr b ma warto true, wywietla podziak.
void setMajorTickSpacing(int units)
void setMinorTickSpacing(int units)
Wstawia dugie i krtkie kreski podziaki co okrelon liczb jednostek.
void setPaintLabels(boolean b)
Jeli parametr b ma warto true, wywietla etykiety kresek podziaki.
void setLabelTable(Dictionary table)
Ustawia komponenty stanowice etykiety kresek. Kada para klucz warto
w tabeli ma posta new Integer(warto)/komponent.
432 Java. Podstawy
void setSnapToTicks(boolean b)
Jeli parametr b ma warto true, gaka suwaka dosuwa si do najbliszej kreski
podziaki.
void setPaintTrack(boolean b)
Jeli parametr b ma warto true, wywietlana jest prowadnica, po ktrej przesuwa
si gaka suwaka.
9.5. Menu
Ten rozdzia zaczlimy od opisu najczciej uywanych komponentw, takich jak przyciski,
pola tekstowe i listy rozwijalne. W Swingu mona te tworzy inny rodzaj elementw inter-
fejsu uytkownika znane z aplikacji posiadajcych graficzny interfejs menu rozwijalne.
Pasek menu znajdujcy si na grze okna zawiera nazwy rozwijalnych menu. Kliknicie jednej
z tych nazw powoduje otwarcie odpowiadajcego jej menu, ktre zawiera rne elementy
menu oraz podmenu. Kiedy uytkownik klika element menu, wszystkie menu zostaj zam-
knite, a do programu wysyany jest komunikat. Rysunek 9.19 przedstawia typowe menu
z podmenu.
Rysunek 9.19.
Menu z podmenu
Pasek menu jest zwykym komponentem, ktry mona wstawi w dowolnym miejscu. Zazwy-
czaj jest on umieszczany na samej grze ramki za pomoc metody setJMenuBar:
frame.setJMenuBar(menuBar);
Na rysunku 9.19 separatory znajduj si pod elementami menu Wklej oraz Tylko do odczytu.
Kiedy uytkownik klika menu, uruchamia akcj. Kady element menu musi posiada obiekt
nasuchujcy akcji:
ActionListener listener = . . .;
pasteItem.addActionListener(listener);
Metoda add zwraca utworzony element menu, ktry mona przej w celu dodania dla niego
suchacza:
JMenuItem pasteItem = editMenu.add("Wklej");
pasteItem.addActionListener(listener);
Ta procedura dodaje element do menu, wykorzystujc do tego celu nazw akcji. Obiekt akcji
staje si jej suchaczem. Jest to skrcona forma zapisu poniszego fragmentu programu:
JMenuItem exitItem = new JMenuItem(exitAction);
fileMenu.add(exitItem);
javax.swing.JMenu 1.2
JMenu(String label)
Tworzy menu z okrelon etykiet.
434 Java. Podstawy
javax.swing.JMenuItem 1.2
JMenuItem(String label)
Tworzy element menu z dan etykiet.
JMenuItem(Action a) 1.3
Tworzy element menu dla danej akcji.
javax.swing.AbstractButton 1.2
javax.swing.JFrame 1.2
Na rysunku 9.19 ikony znajduj si obok kilku elementw menu. Przy standardowych usta-
wieniach tekst jest umieszczany po prawej stronie ikony elementu menu. Aby tekst pojawi
si po lewej stronie ikony, naley uy metody setHorizontalTextPosition, ktr klasa
JMenuItem dziedziczy po klasie AbstractButton. Na przykad ponisza instrukcja ustawia
tekst etykiety elementu menu po lewej stronie ikony:
cutItem.setHorizontalTextPosition(SwingConstants.LEFT);
Podczas konstruowania elementu menu z akcji warto Action.NAME staje si etykiet tego
elementu, a warto Action.SMALL_ICON ikon.
javax.swing.JMenuItem 1.2
javax.swing.AbstractButton 1.2
javax.swing.AbstractAction 1.2
Elementy te traktuje si tak samo jak wszystkie inne elementy menu. Poniszy fragment kodu
tworzy element menu w postaci pola wyboru:
JCheckBoxMenuItem readonlyItem = new JCheckBoxMenuItem("Tylko do odczytu");
optionsMenu.add(readonlyItem);
Przeczniki w elementach menu dziaaj tak samo jak zwyke przeczniki. Musz nalee
do grupy przyciskw, w ktrej zaznaczenie jednego elementu powoduje usunicie zazna-
czenia uprzednio wybranego.
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem = new JRadioButtonMenuItem("Wstawianie");
insertItem.setSelected(true);
JRadioButtonMenuItem overtypeItem = new JRadioButtonMenuItem("Nadpisywanie");
group.add(insertItem);
group.add(overtypeItem);
optionsMenu.add(insertItem);
optionsMenu.add(overtypeItem);
W przypadku tych elementw programista nie musi koniecznie wiedzie, kiedy dokadnie
nastpi wybr elementu. Moe natomiast sprawdzi jego stan za pomoc metody isSelected
(to oczywicie oznacza konieczno przechowywania referencji do tego elementu menu w polu
obiektowym). Do ustawiania stanu suy metoda setSelected.
javax.swing.JCheckBoxMenuItem 1.2
JCheckBoxMenuItem(String label)
Tworzy element menu w postaci pola wyboru o okrelonej etykiecie.
JCheckBoxMenuItem(String label, boolean state)
Tworzy element menu w postaci pola wyboru o okrelonej etykiecie i okrelonym
stanie pocztkowym (true oznacza zaznaczony).
javax.swing.JRadioButtonMenuItem 1.2
JRadioButtonMenuItem(String label)
Tworzy element menu w postaci przecznika o okrelonej etykiecie.
JRadioButtonMenuItem(String label, boolean state)
Tworzy element menu w postaci przecznika o okrelonej etykiecie i okrelonym
stanie pocztkowym (true oznacza zaznaczony).
javax.swing.AbstractButton 1.2
boolean isSelected()
Rozdzia 9. Komponenty Swing interfejsu uytkownika 437
Rysunek 9.20.
Menu podrczne
Proces tworzenia menu podrcznego wyglda podobnie jak w przypadku zwykego menu, z tym
wyjtkiem, e nie nadaje mu si tytuu.
JPopupMenu popup = new JPopupMenu();
W przeciwiestwie do paska menu, ktry zawsze znajduje si na samej grze ramki, menu
podrczne musi by wywietlane za pomoc metody show. Naley w niej okreli kompo-
nent nadrzdny menu oraz jego lokalizacj za pomoc systemu wsprzdnych komponentu
nadrzdnego. Na przykad:
popup.show(panel, x, y);
Czasami do komponentu posiadajcego menu kontekstowe moe zosta wstawiony inny kom-
ponent, ktry rwnie posiada takie menu. Komponent podrzdny moe odziedziczy menu
kontekstowe elementu nadrzdnego dziki poniszej instrukcji:
child.setInheritsPopupMenu(true);
javax.swing.JPopupMenu 1.2
java.awt.event.MouseEvent 1.1
boolean isPopupTrigger()
Zwraca warto true, jeli zdarzenie myszy powoduje pojawienie si menu
kontekstowego.
javax.swing.JComponent 1.2
Czasami programista nie chce, aby podkrelona zostaa pierwsza litera pasujca do mnemo-
niku. Jeli mamy na przykad mnemonik A dla elementu menu Zapisz jako, moemy sprawi,
aby zostaa podkrelona litera a w drugim wyrazie (Zapisz jako). W Java SE 1.4 wprowadzono
moliwo okrelania, ktra litera ma zosta podkrelona. Suy do tego metoda setDisplayed
MnemonicIndex.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 439
Rysunek 9.21.
Mnemoniki
Majc obiekt Action, mnemonik mona doda jako warto klucza Action.MNEMONIC_KEY:
cutAction.putValue(Action.MNEMONIC_KEY, new Integer('O'));
Aby przej do menu najwyszego poziomu na pasku menu, naley nacisn klawisz Alt
i liter mnemoniku. Aby na przykad przej do menu Pomoc, naley nacisn kombinacj
klawiszy Alt+P.
Za pomoc mnemonikw mona aktywowa element aktualnie otwartego menu lub jego pod-
menu. Natomiast akceleratory (ang. accelerators) to skrty klawiszowe, ktre daj dostp do
elementw menu bez jego otwierania. Na przykad w wielu programach akceleratory Ctrl+O
i Ctrl+S odpowiadaj elementom Otwrz i Zapisz w menu Plik. Do wizania elementw menu
z klawiszami skrtu (akceleratorami) suy metoda setAccelerator. Przyjmuje ona obiekt typu
Keystroke. Na przykad ponisza instrukcja wie skrt klawiszowy Ctrl+O z elementem
menu openItem:
openItem.setAccelerator(KeyStroke.getKeyStroke("ctrl O"));
Akceleratory mona wiza wycznie z elementami menu, nie z samymi menu. Akceleratory
nie otwieraj adnego menu bezporednio uruchamiaj akcj zwizan z danym menu.
Teoretycznie tworzenie akceleratora dla elementu menu jest technik podobn do dodawania
akceleratora do komponentu Swing (technik t opisalimy w rozdziale 8.). Jednak kombi-
nacja klawiszy akceleratora zwizanego z elementem menu jest automatycznie widoczna
w menu (rysunek 9.22).
Rysunek 9.22.
Akceleratory
440 Java. Podstawy
javax.swing.JMenuItem 1.2
javax.swing.AbstractButton 1.2
Rysunek 9.23.
Dezaktywowane
elementy menu
Istniej dwie strategie aktywowania i dezaktywowania elementw menu. Przy kadej zmianie
sytuacji mona wywoywa metod setEnabled na rzecz odpowiednich elementw menu lub
akcji. Na przykad w odpowiedzi na przejcie w tryb tylko do odczytu mona zlokalizowa
elementy menu Zapisz i Zapisz jako w celu ich dezaktywacji. Inna metoda polega na wy-
czaniu elementw menu chwil przed wywietleniem tego menu. W takim przypadku
konieczna jest rejestracja suchacza zdarzenia wybrania menu. Pakiet javax.swing.event
zawiera definicj interfejsu MenuListener z trzema metodami:
void menuSelected(MenuEvent event)
void menuDeselected(MenuEvent event)
void menuCanceled(MenuEvent event)
Metoda menuSelected jest wywoywana przed wywietleniem menu, a zatem mona jej uy-
wa do aktywacji i dezaktywacji elementw menu. Poniszy fragment programu dezaktywuje
polecenia Zapisz i Zapisz jako w odpowiedzi na zaznaczenie pola wyboru o nazwie Tylko do
odczytu:
public void menuSelected(MenuEvent event)
{
saveAction.setEnabled(!readonlyItem.isSelected());
saveAsAction.setEnabled(!readonlyItem.isSelected());
}
javax.swing.JMenuItem 1.2
void setEnabled(boolean b)
Aktywuje lub dezaktywuje element menu.
javax.swing.event.MenuListener 1.2
void menuSelected(MenuEvent e)
Wywoywana po wybraniu przez uytkownika menu, ale przed jego otwarciem.
void menuDeselected(MenuEvent e)
Wywoywana po dezaktywacji menu, ale przed jego zamkniciem.
void menuCanceled(MenuEvent e)
Wywoywana po anulowaniu wyboru menu, np. spowodowanym klikniciem
poza jego obrbem.
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z paskiem menu
*/
public class MenuFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private Action saveAction;
private Action saveAsAction;
private JCheckBoxMenuItem readonlyItem;
private JPopupMenu popup;
/**
* Przykadowa akcja, ktra drukuje nazw akcji do wyjcia System.out
*/
class TestAction extends AbstractAction
{
public TestAction(String name)
{
super(name);
}
public MenuFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Akceleratory
fileMenu.addSeparator();
fileMenu.add(new AbstractAction("Zakocz")
Rozdzia 9. Komponenty Swing interfejsu uytkownika 443
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
group.add(insertItem);
group.add(overtypeItem);
// Ikony
// Zagniedone menu
optionMenu.add(readonlyItem);
optionMenu.addSeparator();
optionMenu.add(insertItem);
optionMenu.add(overtypeItem);
editMenu.addSeparator();
editMenu.add(optionMenu);
// Mnemoniki
helpMenu.setMnemonic('P');
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(helpMenu);
// Menu kontekstowe
Rysunek 9.24.
Pasek narzdzi
Cech wyrniajc paski narzdzi jest ich zdolno do przenoszenia si w rne miejsca.
Mona za pomoc przecigania umieszcza je przy jednej z czterech krawdzi ramki (rysu-
nek 9.25). Po zwolnieniu przycisku myszy pasek narzdzi pozostaje w nowej lokalizacji
(rysunek 9.26).
Rozdzia 9. Komponenty Swing interfejsu uytkownika 445
Rysunek 9.25.
Przeciganie
paska narzdzi
Rysunek 9.26.
Pasek narzdzi
w nowej lokalizacji
Pasek narzdzi mona nawet cakiem oddzieli od ramki. Wtedy znajduje si on we wasnej
ramce (rysunek 9.27). Kiedy ramka zawierajca odczony pasek narzdzi zostanie zamknita,
pasek ten wraca do swojej pierwotnej ramki.
Rysunek 9.27.
Odczony
pasek narzdzi
Programowanie paskw narzdzi jest atwym zadaniem. Poniej do paska dodawany jest
element:
JToolBar bar = new JToolBar();
bar.add(blueButton);
Klasa JToolBar posiada take metod suc do dodawania obiektw Action. Wstawianie
obiektw typu Action do paska narzdzi wyglda nastpujco:
bar.add(blueAction);
Mona take okreli tytu paska narzdzi, ktry bdzie widoczny po jego odczeniu:
bar = new JToolBar(titleString);
Domylnie paski narzdzi s uoone poziomo. Aby pasek narzdzi mia pionowe pooe-
nie pocztkowe, naley zastosowa jedn z poniszych metod:
bar = new JToolBar(SwingConstants.VERTICAL)
lub
bar = new JToolBar(titleString, SwingConstants.VERTICAL)
Mimo e na paskach narzdzi najczciej spotyka si przyciski, mog si tam znale wszyst-
kie inne komponenty na przykad lista rozwijalna.
9.5.8. Dymki
Wad paskw narzdzi jest to, e mae ikony niewiele mwi uytkownikowi o swoim
przeznaczeniu. Rozwizaniem tego problemu s dymki (ang. tooltips). Dymek pojawia si,
kiedy kursor myszy zatrzyma si na chwil nad przyciskiem. Tekst dymka jest wywietlany
w prostokcie z wypenieniem w jakim kolorze. Kiedy kursor myszy zostanie zabrany
znad przycisku, dymek znika (rysunek 9.28).
Rysunek 9.28.
Dymek
Listing 9.9 demonstruje wstawianie tych samych obiektw typu Action do menu i paska
narzdzi. Naley zauway, e nazwy akcji pokazuj si jako nazwy elementw w menu
oraz jako krtkie opisy w chmurkach przyciskw na pasku narzdzi.
import java.awt.*;
Rozdzia 9. Komponenty Swing interfejsu uytkownika 447
import javax.swing.*;
/**
* @version 1.13 2007-06-12
* @author Cay Horstmann
*/
public class ToolBarTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
ToolBarFrame frame = new ToolBarFrame();
frame.setTitle("ToolBarTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
javax.swing.JToolBar 1.2
JToolBar()
JToolBar(String titleString)
JToolBar(int orientation)
JToolBar(String titleString, int orientation)
Tworzy pasek narzdzi z okrelonym tytuem i pooeniem. Pooenie moe by
poziome SwingConstants.HORIZONTAL (domylne) lub pionowe
SwingConstants.VERTICAL.
JButton add(Action a)
Tworzy przycisk w pasku narzdzi z nazw, ikon, krtkim opisem i wywoaniem
zwrotnym akcji oraz dodaje ten przycisk na kocu paska.
void addSeparator()
Wstawia separator na kocu paska narzdzi.
javax.swing.JComponent 1.2
Programici Windowsa mog si dziwi, e w Javie tak duo uwagi powicono zarzdcom
rozkadu. W systemie Windows to nic wielkiego najpierw w edytorze okien dialogowych
przeciga si i upuszcza wybrane komponenty okna, a nastpnie za pomoc odpowiednich
narzdzi ustawia si je zgodnie z wymaganiami. Programici pracujcy w duych zespoach
czsto w ogle nie zajmuj si ukadem komponentw, poniewa robi to za nich wykwali-
fikowani projektanci interfejsw uytkownika.
Wad takiego podejcia jest to, e powstay ukad trzeba rcznie modyfikowa, jeli zmieni
si rozmiar komponentw. Ale dlaczego komponenty miayby zmienia rozmiar? Moe to
mie miejsce z dwch powodw. Po pierwsze, uytkownik moe uy wikszej czcionki na
przyciskach i dla tekstu w oknach dialogowych. Nietrudno si przekona, e wiele aplikacji
w systemie Windows bardzo le znosi takie modyfikacje. Przyciski nie powikszaj si,
przez co tekst jest upychany na takiej samej powierzchni jak wczeniej. Po drugie, podobny
problem moe wystpi przy lokalizacji programu. Na przykad polecenie Anuluj po
niemiecku brzmi Abbrechen. Jeli w projekcie przycisku przewidziano tylko tyle miejsca,
ile potrzeba dla sowa Anuluj, jego niemiecki odpowiednik bdzie czciowo obcity.
Dlaczego przyciski w systemie Windows nie powikszaj si, aby pomieci etykiety? Ponie-
wa projektant interfejsu uytkownika nie da adnych instrukcji, w ktrym kierunku powinny
one rosn. Po zakoczeniu przecigania, upuszczania i ustawiania edytor okien dialogowych
pamita tylko pooenie i rozmiar kadego komponentu. Nie dysponuje informacjami, dla-
czego te komponenty zostay ustawione w taki sposb.
Zarzdcy rozkadu w Javie oferuj znacznie lepsze podejcie do zagadnienia rozkadu kom-
ponentw. Dziki nim w rozkadzie dostpne s informacje dotyczce powiza midzy kom-
ponentami. Miao to szczeglne znaczenie w pierwotnej bibliotece AWT, ktra korzystaa
z rodzimych elementw interfejsu. Rozmiar przycisku moe znacznie rni si w stylu
Motif, w systemach Windows i Mac OS X, a program czy aplet nie wie z gry, gdzie bdzie
uruchamiany. Zrnicowanie w pewnym stopniu znikno wraz z pojawieniem si biblioteki
Swing. Jeli aplikacja wymusza okrelony styl, np. Metal, to wyglda identycznie na wszyst-
kich platformach. Jeli jednak programista zezwoli uytkownikom na wybr odpowiadajcego
im stylu, musi przy aranacji komponentw polega na elastycznoci zarzdcw rozkadu.
Od Java 1.0 biblioteka AWT udostpnia rozkad GridBagLayout, ktry ukada komponenty
w wierszach i kolumnach. Rozmiary kolumn i wierszy mog si zmienia, a komponenty mog
zajmowa po kilka z nich. Ten rozkad jest bardzo elastyczny, ale nie mniej skompliko-
wany. Sam dwik sw GridBagLayout jest znany z tego, e przepenia lkiem serca progra-
mistw Javy.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 449
W Java SE 1.4 podjto jeszcze jedn prb zastpienia rozkadu GridBagLayout, ktrej owo-
cem jest SpringLayout. Komponenty w kontenerze cz umowne spryny (ang. springs),
ktre rozcigaj si lub kurcz w odpowiedzi na zmiany rozmiaru kontenera, dostosowujc
pooenie komponentw. Brzmi to niezbyt zachcajco i rzeczywicie rozkad sprynowy
(ang. spring layout) szybko poszed w zapomnienie.
W 2005 roku zesp pracujcy nad projektem NetBeans opracowa technologi Matisse,
ktra stanowi poczenie narzdzia do opracowywania rozkadu i zarzdcy rozkadu. Pro-
jektant interfejsu uytkownika upuszcza komponenty w kontenerze i okrela, ktre z nich
powinny si znajdowa w jednej linii. Narzdzie konwertuje zamierzenia projektanta
na instrukcje dla zarzdcy rozkadu grupowego (ang. group layout manager). Jest to o wiele
wygodniejsze podejcie ni wasnorczne pisanie caego kodu zarzdcy rozkadu. Zarzdca
rozkadu grupowego wchodzi obecnie w skad Java SE 6. Zalecamy uywanie narzdzi do
budowy GUI rodowiska NetBeans nawet tym, ktrzy na co dzie korzystaj z innego IDE.
Mona zaprojektowa GUI w NetBeans, a wygenerowany kod przenie do dowolnego
wybranego IDE.
Rysunek 9.29.
Opcje czcionki
Podzielmy teraz cay kontener na siatk komrek, jak na rysunku 9.30 (wiersze i kolumny nie
musz mie takich samych rozmiarw). Kade pole wyboru zajmuje dwie kolumny, a obszar
tekstowy zajmuje cztery wiersze.
Rysunek 9.30.
Siatka uyta
do zaprojektowania
okna dialogowego
Wartoci wsprzdnych siatki zaczynaj si od 0, tzn. punkt gridx = 0 i gridy = 0 jest lewym
grnym rogiem. Na przykad wsprzdne obszaru tekstowego na rysunku to gridx = 2
i gridy = 0, poniewa zaczyna si on w kolumnie numer 2 (czyli trzeciej) wiersza o nume-
rze 0. Szeroko tego obszaru wynosi gridwidth = 1 i gridheight = 4, czyli rwna si jednej
kolumnie i czterem wierszom.
Naley pamita, e wartoci weight nie okrelaj wzgldnych rozmiarw kolumn. Okrelaj
tylko, jaka cz wolnej przestrzeni ma by przydzielona kademu obszarowi, jeli kontener
przekroczy preferowany rozmiar. Takie dziaanie trudno opanowa intuicyjnie. Zalecamy
ustawienie wszystkich parametrw weight na 100. Nastpnie trzeba uruchomi program, aby
sprawdzi, jak wyglda. Zmniejszajc i zwikszajc okno, mona sprawdzi, jak dostosowuj
452 Java. Podstawy
si poszczeglne wiersze i kolumny. Jeli wyjdzie, e ktry wiersz lub ktra kolumna nie
powinna si powiksza, naley ustawi parametry weight wszystkich znajdujcych si w niej
komponentw na 0. Mona wyprbowa take inne wartoci weight, ale zazwyczaj nie przy-
nosi to dobrego rezultatu.
Jeli komponent nie zajmuje caego dostpnego miejsca, mona okreli jego pooenie
w tym obszarze za pomoc pola anchor. Dostpne wartoci to: GridBagConstraints.CENTER
(domylna), GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST oraz GridBag
Constraints.EAST.
9.6.1.4. Dopenienie
Komponent mona otoczy dodatkow pust przestrzeni, odpowiednio ustawiajc pole insets
obiektu GridBagConstraints. W tym celu naley odpowiednio ustawi wartoci left, top,
right i bottom obiektu typu Insets. Jest to tak zwane dopenienie zewntrzne (ang. exter-
nal padding).
Wartoci ipadx i ipady okrelaj dopenienie wewntrzne (ang. internal padding). Wartoci
te s dodawane do minimalnej szerokoci i wysokoci komponentu. Stanowi to zabezpie-
czenie przed skurczeniem si komponentu do minimalnych rozmiarw.
Wydaje si, e opisywana metoda daje dobre rezultaty. Niezbyt rozsdne wydaje si jednak
ukrywanie rzeczywistych informacji o pooeniu przed zarzdc ukadu w nadziei, e zdoa
on pniej je odzyska.
Wszystko to wydaje si nieco skomplikowane, ale ponisza strategia dziaania znacznie uatwia
opanowanie rozkadu GridBagLayout:
Rozdzia 9. Komponenty Swing interfejsu uytkownika 453
Niektre rodowiska do budowy GUI udostpniaj nawet wizualne narzdzia suce do okre-
lania ogranicze. Rysunek 9.31 przedstawia okno dialogowe konfiguracji w NetBeans.
Rysunek 9.31.
Okrelanie
ogranicze
rozkadu
GridBagLayout
w rodowisku
NetBeans
Listing 9.10 przedstawia kompletny kod programu do zmiany wasnoci czcionek. Klasa GBC
znajduje si na listingu 9.11. Poniszy kod dodaje komponenty do siatki:
add(faceLabel, new GBC(0, 0).setAnchor(GBC.EAST));
add(face, new GBC(1, 0).setFill(GBC.HORIZONTAL).setWeight(100, 0).setInsets(1));
add(sizeLabel, new GBC(0, 1).setAnchor(GBC.EAST));
add(size, new GBC(1, 1).setFill(GBC.HORIZONTAL).setWeight(100, 0).setInsets(1));
add(bold, new GBC(0, 2, 2, 1).setAnchor(GBC.CENTER).setWeight(100, 100));
add(italic, new GBC(0, 3, 2, 1).setAnchor(GBC.CENTER).setWeight(100, 100));
add(sample, new GBC(2, 0, 1, 4).setFill(GBC.BOTH).setWeight(100, 100));
Dla osb, ktre opanoway ograniczenia siatki, kod tego typu jest atwy do odczytania i debu-
gowania.
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
Rozdzia 9. Komponenty Swing interfejsu uytkownika 455
/**
* Ramka zawierajca komponenty ustawiajce wasnoci czcionki w rozkadzie GridBagLayout
*/
public class FontFrame extends JFrame
{
public static final int TEXT_ROWS = 10;
public static final int TEXT_COLUMNS = 20;
public FontFrame()
{
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
// Tworzenie komponentw
face.addActionListener(listener);
size = new JComboBox<>(new Integer[] { 8, 10, 12, 15, 18, 24, 36, 48 });
size.addActionListener(listener);
import java.awt.*;
/**
* Ta klasa upraszcza korzystanie z klasy GridBagConstraints.
* @version 1.01 2004-05-06
* @author Cay Horstmann
*/
public class GBC extends GridBagConstraints
{
/**
* Tworzy obiekt typu GBC z podanymi wartociami gridx i gridy oraz wszystkimi pozostaymi
* parametrami ustawionymi na wartoci domylne.
* @param gridx wsprzdna gridx
* @param gridy wsprzdna gridy
*/
public GBC(int gridx, int gridy)
{
this.gridx = gridx;
this.gridy = gridy;
}
/**
* Tworzy obiekt typu GBC z podanymi wartociami gridx, gridy, gridwidth i gridheight oraz
* wszystkimi pozostaymi parametrami ustawionymi na wartoci domylne.
* @param gridx wsprzdna gridx
* @param gridy wsprzdna gridy
* @param gridwidth liczba zajmowanych komrek w poziomie
* @param gridheight liczba zajmowanych komrek w pionie
*/
public GBC(int gridx, int gridy, int gridwidth, int gridheight)
{
this.gridx = gridx;
this.gridy = gridy;
this.gridwidth = gridwidth;
this.gridheight = gridheight;
Rozdzia 9. Komponenty Swing interfejsu uytkownika 457
/**
* Ustawia parametr anchor.
* @param anchor warto parametru anchor
* @return this obiekt do dalszej modyfikacji
*/
public GBC setAnchor(int anchor)
{
this.anchor = anchor;
return this;
}
/**
* Ustawia kierunek zapeniania.
* @param fill kierunek zapeniania
* @return this obiekt do dalszej modyfikacji
*/
public GBC setFill(int fill)
{
this.fill = fill;
return this;
}
/**
* Ustawia parametry weight komrek.
* @param weightx parametr weight w poziomie
* @param weighty parametr weight w pionie
* @return this obiekt do dalszej modyfikacji
*/
public GBC setWeight(double weightx, double weighty)
{
this.weightx = weightx;
this.weighty = weighty;
return this;
}
/**
* Ustawia dodatkow pust przestrze w komrce.
* @param distance dopenienie we wszystkich kierunkach
* @return this obiekt do dalszej modyfikacji
*/
public GBC setInsets(int distance)
{
this.insets = new Insets(distance, distance, distance, distance);
return this;
}
/**
* Ustawia dopenienia w komrce.
* @param top odstp od grnej krawdzi
* @param left odstp od lewej krawdzi
* @param bottom odstp od dolnej krawdzi
* @param right odstp od prawej krawdzi
* @return obiekt do dalszej modyfikacji
*/
public GBC setInsets(int top, int left, int bottom, int right)
{
458 Java. Podstawy
/**
* Ustawia dopenienie wewntrzne.
* @param ipadx dopenienie wewntrzne poziome
* @param ipady dopenienie wewntrzne pionowe
* @return obiekt do dalszej modyfikacji
*/
public GBC setIpad(int ipadx, int ipady)
{
this.ipadx = ipadx;
this.ipady = ipady;
return this;
}
}
java.awt.GridBagConstraints 1.0
Przecignij pole tekstowe, aby jego linia bazowa wyrwnaa si z lini bazow pierwszej
etykiety. Ponownie zwr uwag na linie pomocnicze.
460 Java. Podstawy
Na zakoczenie ustaw pole hasa w jednej linii z doln etykiet i kolumn z polem znajdu-
jcym si na grze.
Wyglda to do strasznie, ale na szczcie nie trzeba pisa tego kodu wasnorcznie. Znajo-
mo podstaw dotyczcych akcji rozkadu jest jednak przydatna, poniewa umoliwia znaj-
dywanie bdw. Przeanalizujemy podstawow struktur tego kodu. W wycigach z API
znajdujcych si na kocu tego podrozdziau zostao wyjanione przeznaczenie wszystkich
uytych tu klas i metod.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 461
Jak wida w przykadowym kodzie, rozkad grupowy oddziela obliczenia zwizane z uoe-
niem w pionie i poziomie.
Uoenie w poziomie mona sobie wyobrazi jako komponenty o wysokoci rwnej 0, jak na
poniszym rysunku.
Ale to przecie nie moe dziaa prawidowo. Skoro etykiety maj rne dugoci, pole tek-
stowe i pole hasa nie mog by wyrwnane w jednej linii.
Musimy poinformowa program Matisse, e pola maj by wyrwnane. Zaznacz oba pola,
kliknij prawym przyciskiem myszy i wybierz opcj Align/Left to Column. Wyrwnaj te
etykiety (rysunek 9.32).
Rysunek 9.32.
Wyrwnywanie
etykiet i pl
tekstowych
w Matisse
Aby wszystko byo jasne, przyjrzymy si take pionowym obliczeniom. Tym razem kompo-
nenty naley traktowa tak, jakby nie miay szerokoci. Jest jedna grupa sekwencyjna zawiera-
jca dwie rwnolege grupy, rozdzielone pustymi przestrzeniami.
Jak wida w kodzie, komponenty zostay wyrwnane wzgldem linii bazowych (linia bazowa
to linia, na ktrej opiera si tekst komponentu).
Mona wymusi, aby kilka komponentw miao taki sam rozmiar. Na przykad mona
sprawi, aby pole tekstowe i pole hasa miay dokadnie takie same szerokoci. W tym celu
w Matisse naley klikn prawym przyciskiem myszy i wybra opcj Same Size/Same
Width (rysunek 9.33).
Kod na listingu 9.12 przedstawia rozkad programu z poprzedniego podrozdziau przy uyciu
klasy GroupLayout zamiast GridBagLayout. Kod moe nie wydawa si ani troch prostszy ni
przedstawiony na listingu 9.10, ale tego nie musielimy pisa. Komponenty rozmiecilimy
za pomoc Matisse, a pniej nieco oczycilimy wygenerowany kod.
import java.awt.*;
464 Java. Podstawy
Rysunek 9.33.
Wymuszanie
tej samej
szerokoci
dla dwch
komponentw
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
/**
* Ramka, ktrej komponenty zostay uoone za pomoc zarzdcy GroupLayout
*/
public class FontFrame extends JFrame
{
public static final int TEXT_ROWS = 10;
public static final int TEXT_COLUMNS = 20;
public FontFrame()
{
ActionListener listener = EventHandler.create(ActionListener.class, this,
"updateSample");
// Tworzenie komponentw
face.addActionListener(listener);
size = new JComboBox<>(new Integer[] { 8, 10, 12, 15, 18, 24, 36, 48 });
size.addActionListener(listener);
layout.setVerticalGroup(layout.
createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup().addContainerGap().addGroup(
layout.createParallelGroup(GroupLayout.
Alignment.LEADING).addComponent(
pane, GroupLayout.Alignment.TRAILING).addGroup(
layout.createSequentialGroup().addGroup(
layout.createParallelGroup(GroupLayout.
Alignment.BASELINE)
.addComponent(face).
addComponent(faceLabel))
.addPreferredGap(LayoutStyle.
ComponentPlacement.RELATED)
.addGroup(
layout.createParallelGroup(
466 Java. Podstawy
GroupLayout.Alignment.
BASELINE).addComponent(size)
.addComponent(sizeLabel)).
addPreferredGap(
LayoutStyle.ComponentPlacement.
RELATED).addComponent(
italic, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(LayoutStyle.
ComponentPlacement.RELATED)
.addComponent(bold, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, Short.
MAX_VALUE)))
.addContainerGap()));
pack();
}
javax.swing.GroupLayout 6
GroupLayout(Container host)
Tworzy obiekt GroupLayout sucy do rozkadu komponentw w kontenerze
host. Uwaga: konieczne jest wywoanie metody setLayout na rzecz obiektu
kontenera.
void setHorizontalGroup(GroupLayout.Group g)
void setVerticalGroup(GroupLayout.Group g)
Ustawia grup odpowiedzialn za rozkad w poziomie lub pionie.
void linkSize(Component... components)
void linkSize(int axis, Component... component)
Wymusza taki sam rozmiar komponentw lub taki sam rozmiar wzgldem tylko
jednej z osi (SwingConstants.HORIZONTAL lub SwingConstants.VERTICAL).
GroupLayout.SequentialGroup createSequentialGroup()
Tworzy grup, ktra ukada swoich potomkw sekwencyjnie.
GroupLayout.ParallelGroup createParallelGroup()
GroupLayout.ParallelGroup createParallelGroup(GroupLayout.Alignment align)
Rozdzia 9. Komponenty Swing interfejsu uytkownika 467
boolean getHonorsVisibility()
void setHonorsVisibility(boolean b)
Pobiera lub ustawia wasno honorsVisibility. Warto true oznacza,
e komponenty niewidoczne nie bd brane pod uwag w rozkadzie. Warto
false oznacza traktowanie ich jak elementy widoczne. Opcje te pozwalaj
chwilowo ukry niektre komponenty bez zmiany ukadu.
boolean getAutoCreateGaps()
void setAutoCreateGaps(boolean b)
boolean getAutoCreateContainerGaps()
void setAutoCreateContainerGaps(boolean b)
Pobiera i ustawia wasnoci autoCreateGaps i autoCreateContainerGaps. Warto
true oznacza automatyczne dodawanie przerw pomidzy komponentami
lub pomidzy krawdziami kontenera a komponentami do nich przylegajcymi.
Warto domylna to false. Warto true jest przydatna podczas rcznego
tworzenia rozkadu GroupLayout.
javax.swing.GroupLayout.Group
GroupLayout.Group addComponent(Component c)
GroupLayout.Group addComponent(Component c, int minimumSize,
int preferredSize, int maximumSize)
Dodaje komponent do grupy. Wartoci parametrw okrelajcych rozmiar mog by
nieujemne lub staymi GroupLayout.DEFAULT_SIZE albo GroupLayout.PREFERRED_SIZE.
Uycie staej DEFAULT_SIZE powoduje wywoanie metody komponentu
getMinimumSize, getPreferredSize lub getMaximumSize. Staa PREFERRED_SIZE
powoduje wywoanie metody getPreferredSize.
GroupLayout.Group addGap(int size)
GroupLayout.Group addGap(int minimumSize, int preferredSize,
int maximumSize)
Tworzy przerw o podanym staym lub elastycznym rozmiarze.
GroupLayout.Group addGroup(GroupLayout.Group g)
Dodaje okrelon grup do grupy.
468 Java. Podstawy
javax.swing.GroupLayout.ParallelGroup
javax.swing.GroupLayout.SequentialGroup
GroupLayout.SequentialGroup addContainerGap()
GroupLayout.SequentialGroup addContainerGap(int preferredSize,
int maximumSize)
Tworzy luk oddzielajc komponent od krawdzi kontenera.
GroupLayout.SequentialGroup addPreferredGap(LayoutStyle.
ComponentPlacement type)
Tworzy luk rozdzielajc komponenty. Parametr type moe przyj warto
LayoutStyle.ComponentPlacement.RELATED lub LayoutStyle.ComponentPlacement.
UNRELATED.
java.awt.Component 1.0
Rysunek 9.34.
Rozkad koowy
Listing 9.13 przedstawia kod bezuytecznego zarzdcy CircleLayout, ktry ukada kompo-
nenty na krawdzi koa. Klasa ramowa tego programu jest przedstawiona na listingu 9.14.
import java.awt.*;
470 Java. Podstawy
/**
* Ramka zawierajca komponenty uoone w kko
*/
public class CircleLayout implements LayoutManager
{
private int minWidth = 0;
private int minHeight = 0;
private int preferredWidth = 0;
private int preferredHeight = 0;
private boolean sizesSet = false;
private int maxComponentWidth = 0;
private int maxComponentHeight = 0;
preferredWidth = 0;
preferredHeight = 0;
minWidth = 0;
minHeight = 0;
maxComponentWidth = 0;
maxComponentHeight = 0;
int n = parent.getComponentCount();
for (int i = 0; i < n; i++)
{
Component c = parent.getComponent(i);
if (c.isVisible())
{
double angle = 2 * Math.PI * i / n;
// rodek komponentu
int x = xcenter + (int) (Math.cos(angle) * radius);
int y = ycenter + (int) (Math.sin(angle) * radius);
import javax.swing.*;
/**
* Ramka zawierajca przyciski uoone na obwodzie okrgu
*/
public class CircleLayoutFrame extends JFrame
{
public CircleLayoutFrame()
{
setLayout(new CircleLayout());
add(new JButton("ty"));
add(new JButton("Niebieski"));
add(new JButton("Czerwony"));
add(new JButton("Zielony"));
add(new JButton("Pomaraczowy"));
add(new JButton("Fuksja"));
add(new JButton("Bkit"));
pack();
}
}
java.awt.LayoutManager 1.0
Rysunek 9.35.
Kolejno
dostpu
Sytuacja komplikuje si, jeli kontener zawiera inne kontenery. Kiedy aktywowany jest inny
kontener, aktywny staje si komponent znajdujcy si w jego lewym grnym rogu, a nastpnie
aktywowane s kolejne komponenty w tym kontenerze. W kocu aktywowany jest kompo-
nent znajdujcy si za wspomnianym kontenerem.
Cech t mona obrci na swoj korzy, grupujc powizane elementy w dodatkowym kon-
tenerze, np. panelu.
Niemodalne okna dialogowe nie blokuj dostpu do reszty aplikacji w trakcie podawania
informacji przez uytkownika. Oknem tego typu jest pasek narzdzi. Pasek ten pozostaje
widoczny tyle czasu, ile potrzeba, i nie przeszkadza to uytkownikowi w korzystaniu w tym
czasie z innych okien aplikacji.
Rysunek 9.36 przedstawia typowe okno dialogowe. Skada si ono z nastpujcych kompo-
nentw:
ikona,
komunikat,
dwa przyciski opcji.
Rysunek 9.36.
Okno dialogowe
opcji
Okno dialogowe przyjmujce dane wejciowe (ang. input dialog) zawiera dodatkowy kom-
ponent sucy do odbierania danych od uytkownika. Moe to by pole tekstowe, w ktrym
uytkownik wpisuje dowolny acuch tekstowy, albo lista rozwijalna z kilkoma opcjami do
wyboru.
Szczegy wygldu tych okien dialogowych oraz dobr ikon dla standardowych typw
komunikatw zale od stylu.
Typ PLAIN_MESSAGE nie ma adnej ikony. Kady rodzaj okna dialogowego posiada take metod,
za pomoc ktrej mona wstawi wasn ikon.
Kady typ okna dialogowego pozwala na podanie komunikatu. Moe to by acuch tekstu,
ikona, komponent interfejsu uytkownika lub dowolny inny obiekt. Obiekt komunikatu jest
wywietlany nastpujco:
String Rysuje acuch.
Icon Wywietla ikon.
Component Wywietla komponent.
Object[] Wywietla wszystkie obiekty w tablicy jeden nad drugim.
Dowolny inny obiekt Stosuje metod toString i wywietla uzyskany acuch.
Przyciski na dole zale od typu okna dialogowego i typu opcji. Metody showMessageDialog
i showInputDialog dostarczaj tylko standardowe przyciski (odpowiednio OK i OK/Cancel).
Metoda showConfirmDialog przyjmuje jeden z czterech typw opcji:
DEFAULT_OPTION
YES_NO_OPTION
YES_NO_CANCEL_OPTION
OK_CANCEL_OPTION
Na pierwszy rzut oka wydaje si, e opcji jest bardzo duo, ale w praktyce opanowanie ich
jest bardzo proste. Naley postpowa zgodnie z poniszymi wskazwkami:
1. Wybierz rodzaj okna dialogowego (komunikat, potwierdzenie, opcje, dane
wejciowe).
2. Wybierz ikon (bd, informacja, ostrzeenie, pytanie, brak lub wasna).
5. W przypadku okna dialogowego opcji wybierz opcje (acuchy, ikony lub wasne
komponenty) i opcj domyln.
6. W przypadku okna dialogowego danych wejciowych zdecyduj, czy wybra pole
tekstowe, czy list rozwijaln.
7. Zlokalizuj odpowiedni metod do wywoania w API JOptionPane.
acuch komunikatu moe zawiera znaki nowego wiersza (\n), ktre powoduj,
e acuch zostanie podzielony na kilka wierszy.
Program, ktrego klasa ramowa jest przedstawiona na listingu 9.15, wywietla sze sekcji
z przecznikami (rysunek 9.37). Klasa tworzca te komponenty znajduje si na listingu 9.16.
Nacinicie przycisku Poka powoduje wywietlenie odpowiedniego okna dialogowego.
Rysunek 9.37.
Program
OptionDialogTest
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
478 Java. Podstawy
import java.util.*;
import javax.swing.*;
/**
* Ramka zawierajca ustawienia dotyczce wyboru rnych okien dialogowych opcji
*/
public class OptionDialogFrame extends JFrame
{
private ButtonPanel typePanel;
private ButtonPanel messagePanel;
private ButtonPanel messageTypePanel;
private ButtonPanel optionTypePanel;
private ButtonPanel optionsPanel;
private ButtonPanel inputPanel;
private String messageString = "Komunikat";
private Icon messageIcon = new ImageIcon("blue-ball.gif");
private Object messageObject = new Date();
private Component messageComponent = new SampleComponent();
public OptionDialogFrame()
{
JPanel gridPanel = new JPanel();
gridPanel.setLayout(new GridLayout(2, 3));
gridPanel.add(typePanel);
gridPanel.add(messageTypePanel);
gridPanel.add(messagePanel);
gridPanel.add(optionTypePanel);
gridPanel.add(optionsPanel);
gridPanel.add(inputPanel);
add(gridPanel, BorderLayout.CENTER);
add(showPanel, BorderLayout.SOUTH);
pack();
}
/**
* Pobiera aktualnie wybrany komunikat.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 479
* @return acuch, ikona, komponent lub tablica obiektw, w zalenoci od wyboru w panelu Komunikat
*/
public Object getMessage()
{
String s = messagePanel.getSelection();
if (s.equals("acuch")) return messageString;
else if (s.equals("Ikona")) return messageIcon;
else if (s.equals("Komponent")) return messageComponent;
else if (s.equals("Object[]")) return new Object[] { messageString,
messageIcon,
messageComponent, messageObject };
else if (s.equals("Inny")) return messageObject;
else return null;
}
/**
* Pobiera aktualnie wybrane opcje.
* @return tablica acuchw, ikon lub obiektw, w zalenoci od wyboru w panelu Opcja
*/
public Object[] getOptions()
{
String s = optionsPanel.getSelection();
if (s.equals("String[]")) return new String[] { "ty", "Niebieski",
"Czerwony" };
else if (s.equals("Icon[]")) return new Icon[] { new ImageIcon("yellow-
ball.gif"),
new ImageIcon("blue-ball.gif"), new ImageIcon("red-ball.gif") };
else if (s.equals("Object[]")) return new Object[] { messageString,
messageIcon,
messageComponent, messageObject };
else return null;
}
/**
* Pobiera wybrany komunikat lub typ opcji.
* @param panel Typ komunikatu lub panel Potwierdzenie
* @return wybrana staa XXX_MESSAGE lub XXX_OPTION z klasy JOptionPane
*/
public int getType(ButtonPanel panel)
{
String s = panel.getSelection();
try
{
return JOptionPane.class.getField(s).getInt(null);
}
catch (Exception e)
{
return -1;
}
}
/**
* Suchacz akcji przycisku Poka wywietla okno dialogowe potwierdzenia, danych wejciowych,
* komunikatu lub opcji w zalenoci od wyboru typu panelu.
*/
private class ShowAction implements ActionListener
{
480 Java. Podstawy
/**
* Komponent z pomalowan powierzchni
*/
import javax.swing.*;
/**
* Panel z przecznikami w ramce z tytuem
*/
public class ButtonPanel extends JPanel
{
private ButtonGroup group;
/**
* Tworzy panel przyciskw
* @param title Tytu wywietlany w obramowaniu
* @param options Tablica etykiet przecznikw
*/
public ButtonPanel(String title, String... options)
{
setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
title));
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
group = new ButtonGroup();
/**
* Pobiera aktualnie wybran opcj
* @return Zwraca etykiet aktualnie wybranego przecznika
*/
public String getSelection()
{
return group.getSelection().getActionCommand();
}
}
javax.swing.JOptionPane 1.2
Implementacja takiego okna polega na rozszerzeniu klasy JDialog. Proces ten przebiega
w zasadzie tak samo jak w przypadku rozszerzania klasy JFrame przy tworzeniu okna gw-
nego aplikacji. Mianowicie:
Rozdzia 9. Komponenty Swing interfejsu uytkownika 485
Rysunek 9.38.
Okno dialogowe
typu O programie
W konstruktorze nadklasy trzeba poda ramk nadrzdn, tytu okna dialogowego oraz
okreli modalno.
ok.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});
panel.add(ok);
add(panel, BorderLayout.SOUTH);
setSize(250, 150);
}
}
Jak wida, konstruktor tworzy elementy interfejsu uytkownika (w tym przypadku etykiety
i przycisk) oraz zawiera procedur obsugi przycisku i ustawia rozmiar okna dialogowego.
Aby wywietli okno dialogowe, naley utworzy nowy obiekt okna dialogowego, a nastpnie
go uwidoczni:
JDialog dialog = new AboutDialog(this);
dialog.setVisible(true);
We fragmencie kodu zaprezentowanym poniej okno dialogowe tworzone jest tylko jeden raz,
po czym mona go uywa za kadym razem, gdy uytkownik kliknie pozycj O programie.
if (dialog == null) // pierwszy raz
dialog = new AboutDialog(this);
dialog.setVisible(true);
Okno zostanie ukryte take w wyniku kliknicia przycisku Zamknij. Podobnie jak w przypadku
ramki JFrame, dziaanie to mona zmieni za pomoc metody setDefaultCloseOperation.
Listing 9.17 przedstawia kod programu testujcego omawiane okno dialogowe, a listing 9.18
klas AboutDialog.
import java.awt.event.*;
import javax.swing.*;
Rozdzia 9. Komponenty Swing interfejsu uytkownika 487
/**
* Ramka z menu, ktrego akcja Plik/O programie wywietla okno dialogowe
*/
public class DialogFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private AboutDialog dialog;
public DialogFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
488 Java. Podstawy
/**
* Przykadowe modalne okno dialogowe wywietlajce komunikat i oczekujce na kliknicie przycisku Ok
*/
public class AboutDialog extends JDialog
{
public AboutDialog(JFrame owner)
{
super(owner, "Test okna O programie", true);
add(
new JLabel(
"<html><h1><i>Core Java</i></h1><hr> By Cay Horstmann and Gary
Cornell </html>"),
BorderLayout.CENTER);
pack();
}
}
javax.swing.JDialog 1.2
Przyjrzyjmy si oknu widocznemu na rysunku 9.39, ktre moe suy do pobierania nazwy
i hasa uytkownika pragncego poczy si z jak usug internetow.
Rysunek 9.39.
Okno dialogowe
z polem hasa
Okno dialogowe powinno posiada metody ustawiajce dane pocztkowe. Na przykad klasa
PasswordChooser omawianego programu zawiera metod o nazwie setUser, ktra wstawia
domylne wartoci do pl tekstowych:
public void setUser(User u)
{
username.setText(u.getName());
}
Po ustawieniu wartoci domylnych (jeli jest to konieczne) naley wywoa metod setVi
sible(true) w celu uwidocznienia okna.
Uytkownik moe poda wymagane informacje i klikn przycisk Ok lub Anuluj. Procedury
obsugujce zdarzenia kadego z tych przyciskw wywouj metod setVisible(false),
ktra koczy wywoanie metody setVisible(true). Uytkownik moe te zamkn okno.
Jeli nie zdefiniowano adnego suchacza zdarze okna, zastosowana zostanie standardowa
procedura zamykajca polegajca na ukryciu okna, w wyniku ktrego nastpuje przerwanie
dziaania metody setVisible(true).
Wane jest to, e blokada tworzona przez metod setVisible(true) dziaa do chwili zam-
knicia okna. To uatwia tworzenie modalnych okien dialogowych.
Musimy sprawdzi, czy uytkownik klikn przycisk Ok czy Anuluj. W kodzie znacznik ok
zosta ustawiony na warto false przed pokazaniem okna. Warto t na true moe zmieni
tylko procedura obsugi przycisku Ok. W przypadku jego nacinicia mona pobra dane
wpisane w oknie przez uytkownika.
moe by wywietlane w kilku rnych ramkach. Lepiej jest wybra ramk nadrzdn,
kiedy okno dialogowe jest gotowe do wywietlenia ni po utworzeniu obiektu typu Pas
swordChooser.
Sztuka polega na tym, aby sprawi, e klasa PasswordChooser bdzie rozszerzaa klas JPanel,
a nie JDialog. W tym celu obiekt JDialog naley utworzy w locie, w metodzie showDialog:
public boolean showDialog(Frame owner, String title)
{
ok = false;
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}
Warto zauway, e bezpiecznym ustawieniem dla parametru owner jest warto null.
Mona to zrobi jeszcze lepiej. Czasami ramka nadrzdna nie jest od razu gotowa. Mona j
z atwoci uzyska z dowolnego komponentu parent, np.:
Frame owner;
if (parent instanceof Frame)
owner = (Frame) parent;
else
owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);
Wiele okien dialogowych posiada przycisk domylny (ang. default button), ktry jest auto-
matycznie naciskany, kiedy uytkownik nacinie klawisz wyzwolenia (ang. trigger key)
w wikszoci stylw jest to klawisz Enter. Przycisk domylny jest w jaki sposb wyr-
niony, zazwyczaj grubym obramowaniem.
Przycisk domylny ustawia si w panelu gwnym okna dialogowego (ang. root pane):
dialog.getRootPane().setDefaultButton(okButton);
Rozdzia 9. Komponenty Swing interfejsu uytkownika 491
Listing 9.19 przedstawia kod klasy ramowej programu ilustrujcego przepyw danych
w oknie dialogowym. Klasa tego okna jest pokazana na listingu 9.20.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z menu, ktrego akcja Plik/Pocz wywietla okno dialogowe z polem hasa
*/
public class DataExchangeFrame extends JFrame
{
public static final int TEXT_ROWS = 20;
public static final int TEXT_COLUMNS = 40;
private PasswordChooser dialog = null;
private JTextArea textArea;
public DataExchangeFrame()
{
// Tworzenie menu Plik
/**
* Akcja Connect wywietla okno dialogowe z polem hasa
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Elementy suce do podania hasa, ktre wida w oknie dialogowym
*/
public class PasswordChooser extends JPanel
{
private JTextField username;
private JPasswordField password;
private JButton okButton;
private boolean ok;
private JDialog dialog;
public PasswordChooser()
{
setLayout(new BorderLayout());
/**
* Ustawia wartoci domylne okna dialogowego
* @param u domylne informacje uytkownika
*/
public void setUser(User u)
{
username.setText(u.getName());
}
/**
* Pobiera dane podane w oknie dialogowym
* @return a obiekt typu User, ktrego stan reprezentuje dane wprowadzone w oknie dialogowym
*/
public User getUser()
{
return new User(username.getText(), password.getPassword());
}
/**
* Wywietla panel z elementami przyjmujcymi dane od uytkownika w oknie dialogowym
* @param parent komponent w ramce nadrzdnej lub warto null
* @param title tytu okna dialogowego
*/
public boolean showDialog(Component parent, String title)
{
ok = false;
494 Java. Podstawy
// Jeli jest to pierwszy raz lub zmieni si uytkownik, utworzenie nowego okna dialogowego
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}
}
javax.swing.SwingUtilities 1.2
javax.swing.JComponent 1.2
JRootPane getRootPane()
Pobiera panel gwny (ang. root pane) zawierajcy dany komponent lub warto
null, jeli komponent ten nie posiada przodka z panelem gwnym.
javax.swing.JRootPane 1.2
javax.swing.JButton 1.2
boolean isDefaultButton()
Zwraca warto true, jeli dany przycisk jest domylny w swoim panelu gwnym.
Rozdzia 9. Komponenty Swing interfejsu uytkownika 495
Rysunek 9.40.
Okno dialogowe
wyboru pliku
Ponisze punkty opisuj procedur tworzenia okna dialogowego wyboru pliku i odzyskiwania
tego, co uytkownik wybra w polu wyboru.
1. Utwrz obiekt typu JFileChooser. W przeciwiestwie do konstruktora klasy JDialog
w tym przypadku nie trzeba podawa komponentu nadrzdnego. Dziki temu okno
wyboru pliku mona wykorzysta w kilku ramkach.
Na przykad:
JFileChooser chooser = new JFileChooser();
W takim przypadku konieczne jest dostarczenie obiektu typu File. Obiekty tego
typu zostay szczegowo omwione w rozdziale 12. Na razie wystarczy nam wiedza,
e konstruktor File(String filename) zamienia plik lub katalog o okrelonej
nazwie na obiekt typu File.
3. Jeli istnieje due prawdopodobiestwo, e uytkownik wybierze plik o okrelonej
nazwie, nazw t naley poda za pomoc metody setSelectedFile:
chooser.setSelectedFile(new File(filename));
lub
int result = chooser.showSaveDialog(parent);
Wywoania tego typu zwracaj warto tylko wwczas, gdy uytkownik zatwierdzi,
anuluje lub zamknie okno dialogowe. Moliwe wartoci zwrotne to:
JFileChooser.APPROVE_OPTION, JFileChooser.CANCEL_OPTION
i JFileChooser.ERROR_OPTION.
8. Wybrany plik lub pliki pobiera si za pomoc metod getSelectedFile()
lub getSelectedFiles. Zwracaj one jeden obiekt typu File lub tablic takich
obiektw. Do sprawdzenia nazwy obiektu plikowego mona uy jego metody
getPath. Na przykad:
String filename = chooser.getSelectedFile().getPath();
W chwili pisania tej ksiki dostpne byy dwie takie podklasy: domylny filtr akceptujcy
wszystkie pliki i filtr akceptujcy tylko pliki z okrelonym rozszerzeniem. Ponadto z atwo-
ci mona napisa wasny filtr. Wystarczy zaimplementowa w nim dwie abstrakcyjne
metody nadklasy FileFilter:
public boolean accept(File f);
public String getDescription();
Pierwsza z tych metod sprawdza, czy plik powinien zosta zaakceptowany. Druga zwraca
opis typu pliku, ktry moe by wywietlony w oknie wyboru plikw.
Po utworzeniu obiektu filtru plikw naley go zainstalowa w obiekcie okna wyboru pliku
(ang. file chooser):
chooser.setFileFilter(new FileNameExtensionFilter("Pliki obrazw", "gif", "jpg");
W oknie wyboru plikw mona zainstalowa kilka filtrw. Su do tego ponisze instrukcje:
chooser.addChoosableFileFilter(filter1);
chooser.addChoosableFileFilter(filter2);
. . .
Uytkownik wybiera filtr z listy rozwijalnej znajdujcej si na dole okna. Domylnie filtr
All files jest zawsze dostpny. Jest to bardzo dobre rozwizanie, na wypadek gdyby uyt-
kownik chcia wybra plik o niestandardowym rozszerzeniu. Aby usun filtr All files, naley
uy poniszej instrukcji:
chooser.setAcceptAllFileFilterUsed(false)
Okno wyboru plikw mona ozdobi specjalnymi ikonami i opisami plikw. W tym celu naley
dostarczy obiekt klasy rozszerzajcej klas FileView z pakietu javax.swing.filechooser.
498 Java. Podstawy
Nastpnie do instalacji widoku plikw w oknie wyboru plikw uywamy metody setFileView.
Okno wyboru plikw wywouje zaimplementowane metody dla kadego pliku lub katalogu,
ktry chce wywietli. Jeli metoda zwrci warto null dla ikony, nazwy lub opisu, okno
wyboru plikw odwouje si do widoku domylnego w zastosowanym stylu. Zalet takiego
zachowania jest to, e programista musi si zaj tylko tymi typami plikw, ktre go interesuj.
Okno wyboru plikw podejmuje decyzj, czy otworzy katalog kliknity przez uytkownika
za pomoc metody isTraversable. Pamitajmy, e metoda ta zwraca obiekt typu Boolean,
a nie warto typu boolean (logiczn)! Wydaje si to dziwne, ale jest bardzo wygodne jeli
nie chcemy zmieni domylnego widoku, wystarczy zwrci warto null. Wtedy okno wyboru
plikw zastosuje domylny widok. Innymi sowy, ta metoda zwraca obiekt typu Boolean,
ktry umoliwia wybr jednej z trzech opcji: prawda (Boolean.TRUE), fasz (Boolean.FALSE)
i bez rnicy (null).
Poniszy przykadowy program zawiera prost klas widoku plikw. Wywietla ona okre-
lon ikon, kiedy jaki plik pasuje do filtru. W tym przypadku wywietlana jest ikona palety
dla wszystkich plikw obrazw.
class FileIconView extends FileView
{
public FileIconView(FileFilter aFilter, Icon anIcon)
{
filter = aFilter;
icon = anIcon;
}
Ten widok plikw instalujemy w oknie wyboru plikw za pomoc metody setFileView:
chooser.setFileView(new FileIconView(filter,
new ImageIcon("palette.gif")));
Dziki temu obok wszystkich plikw zaakceptowanych przez filtr bdzie widoczna ikona
palety, a pozostae bd wywietlane w standardowy sposb. Oczywicie uywamy tego
samego filtru, ktry utworzylimy w oknie wyboru plikw.
W kocu okno dialogowe mona wyposay w dodatkowe akcesorium (ang. accessory com-
ponent). Na przykad rysunek 9.41 przedstawia akcesorium podgldu umieszczone obok listy
plikw. Wywietla ono miniatur wybranego pliku.
Rysunek 9.41.
Okno dialogowe
wyboru pliku
z akcesorium
podgldu
Jest tylko jedna trudno. Kiedy uytkownik kliknie inny plik, podgld powinien zosta
zaktualizowany. Okno wyboru plikw wykorzystuje mechanizm JavaBeans do powiadamiania
zainteresowanych suchaczy o zmianach swoich wasnoci. Zaznaczony plik jest wasnoci,
ktr mona monitorowa za pomoc obiektu PropertyChangeListener. Bardziej szczeg-
owo mechanizm ten opisujemy w rozdziale 8. drugiego tomu. Poniej znajduje si kod
przechwytujcy omawiane powiadomienia:
chooser.addPropertyChangeListener(new
PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent event)
{
if (event.getPropertyName() == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)
{
File newFile = (File) event.getNewValue()
// Aktualizacja akcesorium
. . .
}
}
});
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.filechooser.*;
/**
* Ramka z menu zawierajcym opcj Otwrz i obszarem do prezentacji otwartych obrazw
*/
public class ImageViewerFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
private JLabel label;
private JFileChooser chooser;
public ImageViewerFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Pasek menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
chooser.setAccessory(new ImagePreviewer(chooser));
import java.awt.*;
import java.beans.*;
import java.io.*;
import javax.swing.*;
/**
* Akcesorium wywietlajce podgld obrazw
*/
public class ImagePreviewer extends JLabel
{
/**
* Tworzy obiekt ImagePreviewer
* @param chooser okno wyboru plikw, ktrego wasno zmienia si, powoduje zmian obrazu
* w tym podgldzie
*/
public ImagePreviewer(JFileChooser chooser)
{
setPreferredSize(new Dimension(100, 100));
setBorder(BorderFactory.createEtchedBorder());
chooser.addPropertyChangeListener(new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent event)
{
if (event.getPropertyName() ==
JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)
{
// Uytkownik wybra inny plik
File f = (File) event.getNewValue();
if (f == null)
{
setIcon(null);
return;
}
setIcon(icon);
}
}
});
}
}
Rozdzia 9. Komponenty Swing interfejsu uytkownika 503
import java.io.*;
import javax.swing.*;
import javax.swing.filechooser.*;
import javax.swing.filechooser.FileFilter;
/**
* Widok plikw wywietlajcy ikon obok wszystkich plikw zaakceptowanych przez filtr
*/
public class FileIconView extends FileView
{
private FileFilter filter;
private Icon icon;
/**
* Tworzy obiekt FileIconView
* @param aFilter filtr plikw wszystkie pliki zaakceptowane przez ten filtr bd miay ikon
* @param anIcon ikona wywietlana obok wszystkich zaakceptowanych plikw
*/
public FileIconView(FileFilter aFilter, Icon anIcon)
{
filter = aFilter;
icon = anIcon;
}
javax.swing.JFileChooser 1.2
JFileChooser()
Tworzy okno dialogowe wyboru plikw, ktrego mona uywa w wielu ramkach.
void setCurrentDirectory(File dir)
Ustawia pocztkowy katalog wywietlany w oknie dialogowym wyboru plikw.
void setSelectedFile(File file)
void setSelectedFiles(File[] file)
Ustawia domylny plik w oknie dialogowym wyboru plikw.
void setMultiSelectionEnabled(boolean b)
Ustawia lub wycza tryb wyboru wielu plikw.
void setFileSelectionMode(int mode)
Pozwala na wybr tylko plikw (domylnie), tylko katalogw lub jednych i drugich.
Parametr mode moe mie jedn z nastpujcych wartoci
504 Java. Podstawy
JFileChooser.FILES_ONLY, JFileChooser.DIRECTORIES_ONLY
lub FileChooser.FILES_AND_DIRECTORIES.
int showOpenDialog(Component parent)
int showSaveDialog(Component parent)
int showDialog(Component parent, String approveButtonText)
Wizualizuje okno dialogowe, w ktrym przycisk zatwierdzajcy ma etykiet
Open bd Save lub w postaci acucha approveButtonText. Zwraca warto
APPROVE_OPTION, CANCEL_OPTION (jeli uytkownik klikn przycisk anulowania
lub zamkn okno) lub ERROR_OPTION (jeli wystpi bd).
File getSelectedFile()
File[] getSelectedFiles()
Pobiera plik lub pliki wybrane przez uytkownika (lub zwraca warto null,
jeli uytkownik nie wybra adnego pliku).
void setFileFilter(FileFilter filter)
Ustawia mask pliku dla okna dialogowego wyboru plikw. Wywietlone zostan
wszystkie pliki, dla ktrych filter.accept zwrci warto true. Ponadto dodaje
filtr do listy dostpnych filtrw.
void addChoosableFileFilter(FileFilter filter)
Dodaje filtr plikw do listy dostpnych filtrw.
void setAcceptAllFileFilterUsed(boolean b)
Dodaje lub wycza filtr All files w licie rozwijalnej.
void resetChoosableFileFilters()
Czyci list dostpnych filtrw plikw. Pozostaje tylko filtr All files, jeli nie zostanie
jawnie wyczony.
void setFileView(FileView view)
Ustawia widok plikw dostarczajcy informacji o plikach wywietlanych przez okno
wyboru.
void setAccessory(JComponent component)
Ustawia komponent akcesorium.
javax.swing.filechooser.FileFilter 1.2
boolean accept(File f)
Zwraca warto true, jeli plik ma by wywietlany.
String getDescription()
Zwraca opis filtru plikw, na przykad Pliki obrazw (*.gif, *.jpeg).
Rozdzia 9. Komponenty Swing interfejsu uytkownika 505
javax.swing.filechooser.FileNameExtensionFilter 6
javax.swing.filechooser.FileView 1.2
String getName(File f)
Zwraca nazw pliku f lub warto null. W normalnych warunkach metoda
ta zazwyczaj zwraca f.getName().
String getDescription(File f)
Zwraca moliwy do odczytu opis pliku f lub warto null. Jeli na przykad
f jest dokumentem HTML, metoda ta moe zwrci jego tytu.
String getTypeDescription(File f)
Zwraca moliwy do odczytu opis typu pliku f lub warto null. Jeli na przykad
f jest dokumentem HTML, metoda ta moe zwrci acuch Hypertext document.
Icon getIcon(File f)
Zwraca ikon pliku f lub warto null. Jeli na przykad f jest plikiem JPEG,
metoda ta moe zwrci miniatur.
Boolean isTraversable(File f)
Zwraca warto Boolean.TRUE, jeli uytkownik moe otworzy katalog f. Metoda
ta moe zwrci warto false, jeli katalog jest z zaoenia dokumentem zoonym.
Podobnie jak wszystkie metody klasy FileView, take ta metoda moe zwrci
warto null, tym samym odsyajc okno wyboru plikw do widoku domylnego.
W Swing dostpny jest jeszcze tylko jeden dodatkowy komponent wyboru o nazwie JColor
Chooser (rysunki 9.42 do 9.44). Umoliwia on uytkownikowi wybr koloru. Podobnie jak
klasa JFileChooser, klasa JColorChooser jest komponentem, a nie oknem dialogowym. Zawiera
natomiast metody suce do tworzenia okien dialogowych z komponentem wyboru koloru.
506 Java. Podstawy
Rysunek 9.42.
Karta Swatches
(prbki)
Rysunek 9.43.
Karta HSB
Rysunek 9.44.
Karta RGB
komponent nadrzdny,
tytu okna dialogowego,
znacznik ustawiajcy modalno okna,
komponent wyboru koloru,
suchacze przyciskw OK i Cancel (lub null, jeli ma nie by suchaczy).
Mona nawet zrobi to lepiej i doda natychmiastowy podgld wybranego koloru. Aby
monitorowa wybierane kolory, naley utworzy model wyboru komponentu wyboru i doda
suchacza zmian:
chooser.getSelectionModel().addChangeListener(new
ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
Procedury zwizane z chooser.getColor();
}
});
Program przedstawiony na listingu 9.24 demonstruje wszystkie trzy wymienione typy okien
dialogowych. Kliknicie przycisku Modalne pociga za sob konieczno wyboru koloru,
zanim mona zrobi cokolwiek innego. Kliknicie przycisku Niemodalne daje niemodalne
okno dialogowe, ale zmiana koloru nastpuje dopiero po naciniciu przycisku OK. Przycisk
Bezporednie wywietla niemodalne okno dialogowe bez przyciskw. Kolor ta panelu jest
aktualizowany bezporednio po wybraniu koloru w oknie dialogowym.
508 Java. Podstawy
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
* Panel z przyciskami uruchamiajcymi trzy typy okien
*/
public class ColorChooserPanel extends JPanel
{
public ColorChooserPanel()
{
JButton modalButton = new JButton("Modalne");
modalButton.addActionListener(new ModalListener());
add(modalButton);
/**
* Ten suchacz wywietla okno modalne.
*/
private class ModalListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Color defaultColor = getBackground();
Color selected = JColorChooser.showDialog(ColorChooserPanel.this, "Ustaw
kolor ta",
defaultColor);
if (selected != null) setBackground(selected);
}
}
/**
* Ten suchacz wywietla okno niemodalne. Kolor ta panelu zmienia si po
* klikniciu przycisku OK.
*/
private class ModelessListener implements ActionListener
{
private JDialog dialog;
private JColorChooser chooser;
public ModelessListener()
{
chooser = new JColorChooser();
dialog = JColorChooser.createDialog(ColorChooserPanel.this, "Kolor ta",
Rozdzia 9. Komponenty Swing interfejsu uytkownika 509
/**
* Ten suchacz wywietla okno niemodalne. Kolor ta panelu zmienia si bezporednio
* po wybraniu przez uytkownika koloru.
*/
private class ImmediateListener implements ActionListener
{
private JDialog dialog;
private JColorChooser chooser;
public ImmediateListener()
{
chooser = new JColorChooser();
chooser.getSelectionModel().addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
setBackground(chooser.getColor());
}
});
javax.swing.JColorChooser 1.2
JColorChooser()
Tworzy komponent wyboru koloru z pocztkowym kolorem biaym.
510 Java. Podstawy
Color getColor()
void setColor(Color c)
Pobiera i ustawia aktualny kolor.
static Color showDialog(Component parent, String title, Color initialColor)
Wywietla modalne okno dialogowe zawierajce komponent wyboru koloru.
Parametry: parent Komponent, nad ktrym ma si pojawi okno.
title Tytu okna dialogowego.
initialColor Pocztkowy kolor w oknie wyboru koloru.
W tym rozdziale:
Pliki JAR
Java Web Start
Aplety
Zapisywanie ustawie aplikacji
W tej chwili powinnimy swobodnie posugiwa si wikszoci funkcji jzyka Java. Mamy
te solidne podstawy programowania interfejsw graficznych. Skoro potrafimy tworzy
aplikacje uytkowe, musimy pozna techniki przygotowywania ich do uytku na komputerze
uytkownika. W kwestii tej wybr czsto pada na aplety (ang. applet), ktre byy powodem
ogromnego zainteresowania Jav na pocztku jej istnienia. Aplet to specjalny rodzaj pro-
gramu w Javie, ktry moe zosta pobrany przez przegldark z internetu i uruchomiony.
Mia on uwolni uytkownikw od problemw zwizanych z instalacj oprogramowania,
ktre byoby pobierane na dowolne urzdzenie lub komputer obsugujcy Jav i podczony
do internetu.
Aplety nie speniy pokadanych w nich oczekiwa z wielu powodw. Dlatego rozdzia ten
zaczynamy od technik pakowania aplikacji. Nastpnie przechodzimy do mechanizmu Java
Web Start, bdcego alternatyw dla dostarczania aplikacji za pomoc internetu, ktry napra-
wia niektre z wad apletw. Na kocu opisujemy aplety, pokazujc sytuacje, w ktrych mog
znale zastosowanie.
Do tworzenia plikw JAR suy narzdzie o nazwie jar (w standardowej instalacji JDK
znajduje si w katalogu jdk/bin). Najczciej stosowane polecenie tworzce plik JAR ma
nastpujc skadni:
jar cvf JARNazwaPliku Plik1 Plik2 . . .
Na przykad:
jar cvf CalculatorClasses.jar *.class icon.gif
Tabela 10.1 przedstawia wszystkie opcje narzdzia jar. S one podobne do opcji polecenia
tar w systemie Unix.
W plikach JAR mona pakowa aplikacje, komponenty programw (tak zwane beany, o kt-
rych mowa w rozdziale 8. drugiego tomu) i biblioteki kodu. Na przykad biblioteka wyko-
nawcza JDK jest zawarta w bardzo duym pliku o nazwie rt.jar.
10.1.1. Manifest
Poza klasami, obrazami i innymi plikami rdowymi kady plik JAR zawiera plik manifestu,
ktry okrela specjalne wasnoci archiwum.
Zoone pliki tego typu mog zawiera znacznie wicej wpisw pogrupowanych w sek-
cjach. Pierwsza sekcja nosi nazw sekcji gwnej (ang. main section) i ma zastosowanie do
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 513
Opcja Opis
c Tworzy puste archiwum i dodaje do niego pliki. Katalogi s przetwarzane rekursywnie.
C Zmienia tymczasowo lokalizacj. Na przykad polecenie cvf JARFileName.jar -C classes
*.class przechodzi do katalogu classes w celu dodania klas.
e Tworzy punkt startowy w manifecie (zobacz podrozdzia 10.1.2, Wykonywalne pliki JAR).
f Okrela plik JAR o danej nazwie jako drugi argument wiersza polece. Jeli tego parametru
brakuje, jar wyle wynik do standardowego wyjcia (przy tworzeniu pliku JAR) lub wczyta
go ze standardowego wejcia (przy rozpakowywaniu lub tabulacji pliku JAR).
i Tworzy plik indeksowy (przyspieszajcy wyszukiwanie w duych archiwach).
m Dodaje manifest do pliku JAR. Manifest jest opisem zawartoci i pochodzenia pliku archiwum.
Kade archiwum ma domylny manifest, ale mona utworzy wasny, ktry uwierzytelnia
zawarto archiwum.
M Blokuje tworzenie domylnego pliku manifestu.
t Wywietla spis treci.
u Aktualizuje istniejcy plik JAR.
v Generuje obszerne dane wyjciowe.
x Wypakowuje pliki. Jeli podanych zostanie kilka nazw plikw, zostan wypakowane tylko one.
W przeciwnym przypadku program wypakuje wszystkie pliki.
0 Wycza kompresj ZIP.
caego pliku JAR. Kolejne sekcje okrelaj wasnoci rnych elementw majcych nazwy,
jak konkretne pliki, pakiety czy adresy URL. Kada z nich musi si zaczyna od sowa Name.
Sekcje s rozdzielane pust lini. Na przykad:
Manifest-Version: 1.0
opis caego archiwum
Name: Woozle.class
opis jednego pliku
Name: com/mycompany/mypkg/
opis pakietu
Aby zmieni zawarto pliku manifestu, naley dokona niezbdnych zmian i wyda poni-
sze polecenie:
jar cfm NazwaPlikuJAR NazwaPlikuManifest . . .
Aby zaktualizowa plik manifestu istniejcego pliku JAR, naley umieci w pliku teksto-
wym wpisy, ktre maj by dodane, i wyda nastpujce polecenie:
jar ufm MyArchive.jar manifest-additions.mf
514 Java. Podstawy
Klas gwn programu mona te okreli w manifecie. W tym celu naley do niego doda
instrukcj o nastpujcej postaci:
Main-Class: com.mycompany.mypkg.MainAppClass
Jednak programy Javy w plikach JAR to nie to samo co aplikacje rodzime. W systemie
Windows mona skorzysta z narzdzi innych producentw sucych do zamieniania plikw
JAR na pliki wykonywalne tego systemu. Plik JAR jest opakowywany w plik o rozszerzeniu
.exe, ktry lokalizuje i uruchamia maszyn wirtualn Javy (JVM) lub informuje uytkownika,
co powinien zrobi, jeli JVM nie ma. Istnieje kilka komercyjnych i darmowych narzdzi tego
typu, np.: JSmooth (http://jsmooth.sourceforge.net) i Launch4J (http://launch4j.sourceforge.net).
Generator instalatorw IzPack (http://izpack.org) zawiera take rodzimy program urucha-
miajcy. Wicej informacji na ten temat mona znale pod adresem http://www.javalobby.
org/articles/java2exe.
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 515
10.1.3. Zasoby
Klasy uywane zarwno w apletach, jak i aplikacjach czsto wykorzystuj pliki danych tego
samego typu:
pliki obrazw i zawierajce dwik;
pliki tekstowe zawierajce acuchy komunikatw i etykiety przyciskw;
pliki z danymi binarnymi, na przykad opisujcymi rozkad mapy.
Rysunek 10.1.
Wywietlanie
zasobu
z pliku JAR
Wiadomo, e tytu i rok wydania zostan zmienione w kolejnym wydaniu ksiki. Aby
uatwi zmian, ten tekst naley umieci w pliku tekstowym, a nie bezporednio w kodzie
programu.
Powstaje jednak pytanie, gdzie umieci taki plik jak about.txt. Oczywicie najlepiej byoby,
aby znajdowa si on razem z pozostaymi plikami programu w pliku JAR.
Program adujcy klasy potrafi znale pliki klas, jeli znajduj si gdzie na ciece klas,
w archiwum lub na serwerze sieciowym. Mechanizm zasobw oferuje podobn funkcjonalno
dla plikw, ktre nie s klasami. Poniej znajduje si spis wymaganych czynnoci:
516 Java. Podstawy
Chodzi o to, aby program adujcy klasy potrafi znale klas i odszuka zwizane z ni
zasoby w tej samej lokalizacji.
Powyszy kod mona odczyta nastpujco: znajd plik about.gif w tej samej lokalizacji,
w ktrej znajduje si klasa ResourceTest.
Plik zasobu nie musi si znajdowa w tym samym katalogu co klasa moe by w jakim
podkatalogu. Mona zastosowa hierarchiczn nazw zasobu, jak ponisza:
data/text/about.txt
Jest to wzgldna nazwa zasobu. Jest ona interpretowana wzgldem pakietu klasy, ktra aduje
dany zasb. Naley pamita, e zawsze trzeba uywa separatora /, bez wzgldu na sepa-
rator katalogw stosowany w systemie, w ktrym s przechowywane pliki zasobw. Na przy-
kad w systemie plikw systemu Windows separatory / s automatycznie zamieniane na \.
Nazwa zasobu zaczynajca si od znaku / jest bezwzgldn nazw zasobu. Jest ona lokalizo-
wana w taki sam sposb jak klasa wewntrz pakietu. Na przykad zasb:
/corejava/title.txt
Jedynym przeznaczeniem funkcji adowania zasobw jest adowanie plikw. Nie istniej
adne standardowe metody interpretujce zawarto pliku zasobw. Kady program musi
interpretowa zawarto swoich plikw zasobw na swj wasny sposb.
Listing 10.1 przedstawia kod programu demonstrujcego adowanie zasobw. Ponisze pole-
cenia kompiluj go, tworz plik JAR i uruchamiaj go:
javac ResourceTest.java
jar cvfm ResourceTest.jar ResourceTest.mf *.class *.gif *.txt
java -jar ResourceTest.jar
Aby przekona si, e program pobiera pliki zasobw z archiwum JAR, a nie biecego
katalogu, mona przenie ten program do innego folderu.
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
/**
* @version 1.4 2007-04-30
* @author Cay Horstmann
*/
public class ResourceTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ResourceTestFrame();
frame.setTitle("ResourceTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka adujca zasoby graficzne i tekstowe
*/
class ResourceTestFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 300;
public ResourceTestFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
URL aboutURL = getClass().getResource("about.gif");
Image img = new ImageIcon(aboutURL).getImage();
setIconImage(img);
518 Java. Podstawy
java.lang.Class 1.0
Jeli na przykad pakiet com.mycompany.util zostanie zapiecztowany, adna klasa spoza tego
zapiecztowanego archiwum nie moe by zdefiniowana za pomoc poniszej instrukcji:
package com.mycompany.util;
W tym celu wszystkie klasy pakietu naley umieci w pliku JAR. Domylnie pakiety
w pliku JAR nie s zapiecztowane. Mona zmieni to domylne globalne ustawienie, wsta-
wiajc wiersz
Sealed: true
w gwnej sekcji pliku manifestu. Aby zapiecztowa tylko wybrane pakiety, naley do pliku
manifestu w pliku JAR wstawi dodatkowe sekcje:
Name: com/mycompany/util/
Sealed: true
Name: com/mycompany/misc/
Sealed: false
Aby zapiecztowa pakiet, naley utworzy plik tekstowy z instrukcjami manifestu. Nastp-
nie naley uruchomi narzdzie jar w zwyky sposb:
jar cvfm MyArchive.jar manifest.mf pliki do dodania
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 519
Aby wyprbowa mechanizm Java Web Start, zainstaluj Tomcat dostpny na stronie
http://tomcat.apache.org/. Jest to kontener na serwlety i strony JSP, ale serwuje
te strony internetowe. Jest wstpnie skonfigurowany do serwowania poprawnego typu
MIME dla plikw JNLP.
<description>Kalkulator</description>
<offline-allowed/>
</information>
<resources>
<java version="1.6.0+"/>
<jar href="Calculator.jar"/>
</resources>
<application-desc/>
</jnlp>
5. Umie pliki JAR i JNLP na serwerze sieciowym, aby adres URL zgadza
si z wpisem codebase w pliku JNLP. W przypadku serwera Tomcat pliki naley
umieci w katalogu tomcat/webapps/calculator.
6. Upewnij si, e masz przegldark skonfigurowan pod ktem Java Web Start,
sprawdzajc, czy typ MIME application/x-java-jnlp-file jest skojarzony
z aplikacj javaws. Jeli zainstalowano pakiet JDK, konfiguracja ta powinna
zosta wykonana automatycznie.
7. Uruchom serwer Tomcat.
8. Wpisz w przegldarce adres pliku JNLP. Na przykad w przypadku uycia serwera
Tomcat naley wpisa adres http://localhost:8080/calculator/Calculator.jnlp.
9. Powinno si pojawi okno uruchamiania Java Web Start (rysunek 10.2).
Rysunek 10.2.
Uruchamianie
aplikacji Java
Web Start
Rysunek 10.3.
Kalkulator otwarty
za porednictwem
Java Web Start
11. Kiedy nastpnym razem sprbujemy uzyska dostp do pliku JNLP, program bdzie
pobierany z pamici podrcznej. Zawarto tej pamici mona obejrze za pomoc
panelu kontrolnego w postaci wtyczki Javy (rysunek 10.4). W systemie Windows
wtyczki tej naley szuka w Panelu sterowania. W systemie Linux naley wykona
polecenie jdk/jre/bin/ControlPanel.
Aby nie uruchamia serwera podczas testowania konfiguracji JNLP, mona tymcza-
sowo nadpisa adres URL codebase w pliku JNLP za pomoc poniszego polecenia:
javaws -codebase file://katalogProgramu plikJNL
Na przykad w systemie Unix polecenie to mona wyda w katalogu zawierajcym
plik JNLP:
javaws -codebase file://`pwd` Calculator.jnlp
Rysunek 10.4.
Aplikacje
w pamici
podrcznej
Rysunek 10.5.
Ostrzeenie
o integracji
Dodatkowo powinno si doda ikon dla skrtu i ekranu uruchomieniowego. Firma Sun zaleca
stosowanie ikon o rozmiarach 3232 i 6464 piksele. Pliki ikon naley umieci na serwerze
razem z plikami JAR i JNLP. Poniszy kod naley doda do sekcji information pliku JNLP:
<icon href="calc_icon32.png" width="32" height="32" />
<icon href="calc_icon64.png" width="64" height="64" />
Naley pamita, e ikony te nie s zwizane z ikon aplikacji. Aby wstawi ikon dla aplika-
cji, naley doda odrbny plik ikony do pliku JAR i wywoa metod IconImage na rzecz
klasy ramowej (zobacz listing 10.1).
10.2.1. Piaskownica
Kiedy kod uruchamiany na komputerze jest pobierany ze zdalnego miejsca, spraw pierw-
szorzdn staje si zawsze bezpieczestwo. Aplikacj Java Web Start moe uruchomi jedno
kliknicie. Wejcie na stron powoduje automatyczne uruchomienie wszystkich znajdujcych
si na niej apletw. Gdyby kliknicie odnonika lub wejcie na stron internetow pozwalao
na uruchomienie dowolnego kodu na komputerze uytkownika, przestpcy przeywaliby zoty
wiek wykradania poufnych informacji, danych finansowych i przejmowania komputerw uyt-
kownikw w celu rozsyania spamu.
Co pobierany zdalnie kod moe robi na wszystkich platformach? Zawsze mona wywietla
obrazy, odtwarza dwiki, odpowiada na nacinicia przez uytkownika klawiszy i przy-
ciskw myszki oraz wysya dane wprowadzone przez uytkownika do hosta, z ktrego zosta
zaadowany kod. Taka funkcjonalno wystarcza do zaprezentowania faktw i liczb oraz
pobrania danych od uytkownika skadajcego zamwienie. Ograniczone rodowisko wyko-
nawcze jest czsto nazywane piaskownic (ang. sandbox). Kod dziaajcy w piaskownicy
nie moe nic zmienia w systemie uytkownika ani go szpiegowa.
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 523
Aby mc dziaa poza piaskownic, pliki JAR aplikacji Java Web Start musz by podpi-
sane cyfrowo. Podpisany plik JAR ma certyfikat okrelajcy tosamo tego, kto go podpisa.
Techniki kryptograficzne daj pewno, e certyfikat taki nie jest sfaszowany, a wszelkie
prby zmiany jego zawartoci s natychmiast wykrywane.
Wyobramy sobie, e pobieramy aplikacj utworzon i podpisan cyfrowo przez firm yWorks
GmbH z certyfikatem wydanym przez orodek certyfikacji Thawte (rysunek 10.6). Odbiera-
jc aplikacj, mamy pewno, e:
1. Kod aplikacji nie zosta zmieniony w aden sposb od chwili jej podpisania.
2. Podpis rzeczywicie pochodzi od firmy yWorks.
524 Java. Podstawy
Rysunek 10.6.
Certyfikat
bezpieczestwa
3. Certyfikat naprawd zosta wydany przez orodek Thawte (mechanizm Java Web
Start potrafi sprawdza certyfikaty wydane przez Thawte i kilka innych instytucji).
Niestety nic wicej nie wiemy. Nie wiemy, czy kod jest na pewno bezpieczny. W rzeczywi-
stoci, jeli klikniemy odnonik More Information, dowiemy si, e aplikacja zostanie urucho-
miona bez zwyczajowych ogranicze. To, czy zainstalowa t aplikacj, czy nie, w duej mierze
zaley od tego, czy ufamy firmie yWorks.
Oczywicie kadego dnia mnstwo ludzi pobiera z internetu i uruchamia aplikacje. Jeli
stwierdzisz, e uytkownicy ufaj Twojej aplikacji i infrastrukturze sieciowej, uywaj osobicie
podpisywanego certyfikatu (zobacz http://docs.oracle.com/javase/6/docs/technotes/guides/
javaws/developersguide/development.html). W przeciwnym przypadku naley zapewni uyt-
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 525
Rysunek 10.7.
Niebezpieczny
certyfikat
Rysunek 10.8.
Ostrzeenie
Java Web Start
W szczeglnoci aplikacja nie moe sprawdzi lokalizacji pliku. W ten sposb programista
zyskuje narzdzia do implementacji akcji otwierania i zapisywania plikw, ale tak duo infor-
macji systemowych, jak to tylko moliwe, jest ukrytych przed niezaufanymi aplikacjami.
526 Java. Podstawy
Omwimy najbardziej przydatne usugi JNLP. Aby zapisa plik, trzeba poda ciek startow
i rozszerzenia plikw wywietlanych w oknie dialogowym, dane do zapisania oraz sugerowan
nazw pliku. Na przykad:
service.saveFileDialog(".", new String[] { "txt" }, data, "calc.txt");
Dane musz by dostarczone w strumieniu InputStream, co nie zawsze jest atwe. W progra-
mie z listingu 10.2 przyjto nastpujc strategi dziaania:
1. Utworzenie strumienia ByteArrayOutputStream przechowujcego bajty, ktre maj
by zapisane.
2. Utworzenie strumienia PrintStream wysyajcego swoje dane do strumienia
ByteArrayOutputStream.
3. Wydrukowanie informacji, ktre maj by zapisane, do strumienia PrintStream.
4. Utworzenie strumienia ByteArrayInputStream odczytujcego zapisane bajty.
5. Przekazanie strumienia do metody saveFileDialog.
Wicej na temat strumieni piszemy w rozdziale 1. drugiego tomu. Teraz wystarczy tylko
pobienie przejrze przykadowy program.
Do odczytu danych z pliku suy klasa FileOpenService. Jej metoda openFileDialog odbiera
sugerowan ciek pocztkow i rozszerzenia plikw wywietlanych w oknie dialogowym
i zwraca obiekt typu FileContents. Nastpnie mona za pomoc metod getInputStream
i getOutputStream odczyta i zapisa dane do pliku. Jeli uytkownik nie wybra pliku, metoda
openFileDialog zwraca warto null.
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 527
Pamitajmy, e aplikacja nie zna nazwy ani lokalizacji pliku. Aby otworzy okrelony plik,
mona uy klasy ExtendedService:
ExtendedService service = (ExtendedService) ServiceManager.lookup("javax.jnlp.
ExtendedService");
FileContents contents = service.openFile(new File("c:\\autoexec.bat"));
if (contents != null)
{
OutputStream out = contents.getOutputStream();
. . .
}
Rysunek 10.9.
Ostrzeenie
o dostpie
do pliku
Odseparowanie od siebie poszczeglnych aplikacji jest moliwe dziki temu, e kady pro-
gram moe uywa tylko takich kluczy, ktre zaczynaj si od okrelonego podstawowego
acucha (zgodnie z informacjami w pliku JNLP). Jeli na przykad aplikacja zostanie pobrana
ze strony http://myserver.com/apps, moe uywa tylko kluczy w formacie http://myserver.
com/apps/subkey1/subkey2/. Prby dostpu do innych kluczy zakocz si niepowodzeniem.
528 Java. Podstawy
Aplikacja moe sprawdzi podstaw swojego klucza URL za pomoc metody getCodeBase
z klasy BasicService.
Aby uzyska informacje zwizane z okrelonym kluczem, naley wywoa metod get. Zwraca
ona obiekt typu FileContents, za porednictwem ktrego mona odczytywa i zapisywa dane
kluczy. Na przykad:
FileContents contents = service.get(url);
InputStream in = contents.getInputStream();
OutputStream out = contents.getOutputStream(true); // true = nadpisz
Niestety nie ma dobrego sposobu na sprawdzenie, czy okrelony klucz ju istnieje, czy trzeba
go utworzy. Mona wywoa metod get. Jeli klucz nie istnieje, zostanie spowodowany
wyjtek FileNotFoundException.
Aplikacje Java Web Start i aplety mog drukowa, wykorzystujc normalne API dru-
kowania. Pojawia si tylko okienko, w ktrym uytkownik jest proszony o zezwole-
nie na dostp do drukarki. Wicej informacji na temat API drukowania znajduje si w roz-
dziale 7. drugiego tomu.
Rysunek 10.10.
Aplikacja
WebStartCalculator
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.net.*;
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 529
import javax.jnlp.*;
import javax.swing.*;
/**
* Ramka z kalkulatorem i menu do adowania oraz zapisywania historii oblicze
*/
public class CalculatorFrame extends JFrame
{
private CalculatorPanel panel;
public CalculatorFrame()
{
setTitle();
panel = new CalculatorPanel();
add(panel);
pack();
}
/**
* Pobiera tytu z magazynu trwaego lub prosi uytkownika o podanie tytuu, jeli
* nie ma wczeniejszego wpisu.
*/
public void setTitle()
{
try
{
String title = null;
try
{
FileContents contents = service.get(key);
InputStream in = contents.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
title = reader.readLine();
}
catch (FileNotFoundException e)
{
530 Java. Podstawy
service.create(key, 100);
FileContents contents = service.get(key);
OutputStream out = contents.getOutputStream(true);
PrintStream printOut = new PrintStream(out);
printOut.print(title);
}
setTitle(title);
}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (MalformedURLException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}
}
/**
* Otwiera plik historii i aktualizuje zawarto wywietlacza.
*/
public void open()
{
try
{
FileOpenService service = (FileOpenService) ServiceManager
.lookup("javax.jnlp.FileOpenService");
FileContents contents = service.openFileDialog(".", new String[] { "txt" });
JOptionPane.showMessageDialog(this, contents.getName());
if (contents != null)
{
InputStream in = contents.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null)
{
panel.append(line);
panel.append("\n");
}
}
}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 531
/**
* Zapisuje histori kalkulatora w pliku.
*/
public void save()
{
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream printOut = new PrintStream(out);
printOut.print(panel.getText());
InputStream data = new ByteArrayInputStream(out.toByteArray());
FileSaveService service = (FileSaveService) ServiceManager
.lookup("javax.jnlp.FileSaveService");
service.saveFileDialog(".", new String[] { "txt" }, data, "calc.txt");
}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}
}
}
javax.jnlp.ServiceManager
javax.jnlp.BasicService
URL getCodeBase()
Zwraca katalog zawierajcy kod aplikacji.
boolean isWebBrowserSupported()
Zwraca warto true, jeli rodowisko Web Start moe uruchomi przegldark.
boolean showDocument(URL url)
Podejmuje prb pokazania danego adresu URL w przegldarce. Zwraca warto
true, jeli danie koczy si powodzeniem.
javax.jnlp.FileContents
InputStream getInputStream()
Zwraca strumie wejciowy do odczytu zawartoci pliku.
532 Java. Podstawy
javax.jnlp.FileOpenService
javax.jnlp.FileSaveService
javax.jnlp.PersistenceService
10.3. Aplety
Aplety to programy w jzyku Java doczane do stron HTML. Strona HTML musi poinfor-
mowa przegldark, ktre aplety ma zaadowa oraz gdzie maj one by rozmieszczone. Jak
nietrudno si domyli, znacznik sucy do wstawiania apletw musi dostarcza informacje
dotyczce lokalizacji plikw klas oraz samego apletu (jego rozmiaru, lokalizacji itd.). Przegl-
darka pobiera pliki klas z internetu (lub katalogu na urzdzeniu uytkownika) i automatycznie
uruchamia aplet.
Na pocztku istnienia apletw jedyn przegldark, ktra je obsugiwaa, bya HotJava firmy
Sun. Oczywicie znalazo si niewiele osb, ktre byy skonne uywa oddzielnej przegl-
darki dla jednej dodatkowej funkcji. Aplety zyskay prawdziw popularno z chwil do-
czenia przez firm Netscape maszyny wirtualnej Javy do przegldarki Navigator. Niedugo
pniej to samo zrobia firma Microsoft w przegldarce Internet Explorer. Niestety Microsoft
podci skrzyda Netscape, opornie dodajc obsug starych wersji Javy w Internet Explorerze,
a w kocu cakiem tego zaniecha.
Problem ten rozwizuje narzdzi Java Plug-in. Integruje si ono z przegldarkami jako rozsze-
rzenie, co umoliwia uruchamianie apletw przy wykorzystaniu zewntrznego rodowiska
uruchomieniowego Javy.
Jeli aplet zawiera skadniki Swing, naley rozszerzy klas JApplet. Komponenty
Swing w zwykym kontenerze Applet nie s poprawnie rysowane.
Zwr uwag na due podobiestwo do wersji z rozdziau 7. Poniewa aplet dziaa w oknie
przegldarki, nie trzeba byo definiowa metody zamykajcej.
534 Java. Podstawy
Rysunek 10.11.
Diagram
dziedziczenia
klasy Applet
import java.awt.*;
import javax.swing.*;
/**
* @version 1.23 2012-05-14
* @author Cay Horstmann
*/
public class NotHelloWorld extends JApplet
{
public void init()
{
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 535
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JLabel label = new JLabel("To nie jest aplet Witaj, wiecie",
SwingConstants.CENTER);
add(label);
}
});
}
}
Dobrym pomysem jest przetestowanie apletu we wchodzcej w skad pakietu JDK przegl-
darce apletw (ang. applet viewer) przed otwarciem go w przegldarce internetowej. Ponisze
polecenie wiersza polece otwiera nasz aplet we wspomnianej przegldarce:
appletviewer NotHelloWorldApplet.html
Argumentem narzdzia appletviewer jest nazwa pliku HTML, nie pliku klasy. Rysunek 10.12
przedstawia nasz aplet w przegldarce apletw.
Rysunek 10.12.
Aplet
w przegldarce
apletw
Przegldarka apletw dobrze sprawdza si jako pierwszy etap testowania. Trzeba jednak
w kocu uruchomi aplet w przegldarce, aby sprawdzi, jak bdzie si prezentowa uyt-
kownikowi. Przegldarka apletw pokazuje sam aplet bez otaczajcego go kodu HTML.
Jeli strona HTML zawiera kilka znacznikw applet, przegldarka otworzy kilka okien.
Aby obejrze swj aplet w przegldarce, wystarczy zaadowa w niej odpowiedni stron
HTML (rysunek 10.13). Jeli aplet nie pojawia si, naley zainstalowa narzdzie Java
Plug-in.
536 Java. Podstawy
Rysunek 10.13.
Aplet
w przegldarce
Jeli w aplecie zostan wprowadzone jakie zmiany i zostanie on raz jeszcze pod-
dany kompilacji, konieczne jest ponowne uruchomienie przegldarki, aby zaadowaa
nowe pliki klas. Samo odwieenie strony nie spowoduje zaadowania nowej wersji apletu.
Bywa to kopotliwe przy szukaniu bdw. Mona unikn ponownego uruchamiania prze-
gldarki dziki uyciu konsoli Javy. Naley uruchomi t konsol i wyda polecenie x,
ktre czyci pami programu adujcego klasy. Wtedy po odwieeniu strony zostanie
zaadowana nowa wersja apletu. W systemie Windows naley otworzy panel kontrolny
Java Plug-in znajdujcy si w Panelu sterowania. W systemie Linux naley uy polecenia
jcontrol i zada wywietlenia panelu kontrolnego Javy. Konsola bdzie si pojawia
za kadym razem, kiedy adowany jest aplet.
3. Usu z aplikacji metod main. Nie twrz ramki dla aplikacji, poniewa bdzie
ona wywietlana w oknie przegldarki.
4. Przenie kod inicjujcy z konstruktora ramki do metody init apletu. Nie trzeba
jawnie konstruowa obiektu apletu przegldarka robi to automatycznie
i wywouje metod init.
5. Usu wywoanie metody setSize. W apletach za rozmiary odpowiadaj parametry
HTML width i height.
6. Usu wywoanie metody setDefaultCloseOperation. Apletu nie mona zamkn
jego dziaanie koczy si w chwili zamknicia przegldarki.
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 537
java.applet.Applet 1.0
void init()
Jest wywoywana przy pierwszym adowaniu apletu. Metod t naley przesoni
i umieci w niej cay kod inicjujcy.
void start()
Naley przesoni t metod i umieci w niej kod, ktry ma by wykonywany
za kadym razem, gdy uytkownik odwiedza stron zawierajc ten aplet.
Do typowych dziaa naley tu reaktywacja wtku.
void stop()
Naley przesoni t metod i umieci w niej kod, ktry ma by wykonywany
za kadym razem, gdy uytkownik opuszcza stron zawierajc ten aplet.
Do typowych dziaa naley tu dezaktywacja wtku.
void destroy()
Metod t naley przedefiniowa, wstawiajc do niej kod wykonywany
w momencie zamknicia przegldarki.
void resize(int width, int height)
Wymusza zmian rozmiaru apletu. Byaby to doskonaa metoda, gdyby dziaaa
na stronach internetowych. Niestety obecnie nie dziaa w przegldarkach, poniewa
zakca ich mechanizm rozkadu elementw na stronie.
Wartoci atrybutu code jest nazwa pliku klasy, koniecznie z rozszerzeniem .class. Atrybuty
width i height okrelaj rozmiar okna apletu w pikselach. Koniec znacznika wyznacza znacz-
nik zamykajcy </applet>. Tekst znajdujcy si pomidzy znacznikami <applet> i </applet>
jest wywietlany tylko wtedy, gdy przegldarka nie moe wywietli apletu. Atrybuty code,
width i height s wymagane. Przy braku ktregokolwiek z nich przegldarka nie moe wywie-
tli apletu.
</head>
<body>
<p>Poniszy wiersz tekstu jest wywietlany pod patronatem Javy:</p>
<applet code="NotHelloWorld.class" width="100" height="100">
Gdyby Twoja przegldarka obsugiwaa Jav, w tym miejscu znajdowaby si aplet.
</applet>
</body>
</html>
aDirectory/
MyPage.html
myApplets/
MyApplet.class
archive
Okrela list plikw JAR zawierajcych klasy i inne zasoby apletu, ktre s pobierane
z serwera przed jego zaadowaniem. To znacznie przyspiesza proces adowania,
poniewa pobranie jednego pliku JAR zawierajcego kilka mniejszych innych
plikw wymaga tylko jednego dania HTTP. Pliki JAR na licie s rozdzielane
przecinkami:
<applet code="MyApplet.class"
archive="MyClasses.jar,corejava/CoreJavaClasses.jar"
width="100" height="150">
object
Okrela nazw pliku zawierajcego serializowalny obiekt apletu (serializacja
obiektu polega na zapisie jego wszystkich pl w pliku zagadnienie to opisujemy
w rozdziale 1. drugiego tomu). Przed wywietleniem apletu jego obiekt jest
poddawany deserializacji z powrotem do pierwotnego stanu. Jeli jest uywany
ten atrybut, nie jest wywoywana metoda init apletu, a metoda start.
Przed serializacj obiektu apletu naley wywoa jego metod stop. W ten sposb
mona utworzy trwa przegldark, ktra automatycznie przeadowuje aplety
i przywraca je do takiego samego stanu, w ktrym byy w chwili jej zamykania.
Jest to zaawansowana technika, rzadko uywana przez projektantw stron
internetowych.
W kadym znaczniku applet musi si znajdowa atrybut code lub object.
Na przykad:
<applet object="MyApplet.ser" width="100" height="150">
name
Twrcy skryptw wykorzystuj ten atrybut do odwoywania si do apletu w swoich
skryptach. Przegldarki Netscape i Internet Explorer zezwalaj na wywoywanie
metod apletw na stronie za porednictwem JavaScriptu.
Aby uzyska dostp do apletu z poziomu JavaScriptu, najpierw naley nada mu nazw:
<applet code="MyApplet.class" width="100" height="150" name="mine">
</applet>
Atrybut name ma take kluczowe znaczenie w sytuacjach, kiedy dwa aplety znajdujce si
na tej samej stronie maj si ze sob bezporednio komunikowa. Naley nada nazw ka-
demu apletowi. acuch ten naley przekaza do metody getApplet z klasy AppletContext.
Mechanizm ten, o nazwie komunikacja midzy apletami (ang. inter-applet communication),
zosta opisany nieco dalej w tym rozdziale.
alt
Obsug Javy w przegldarce mona wyczy. Jeli jaki przewraliwiony
administrator to zrobi, nieszczni uytkownicy w miejscu apletu zobacz tekst
zawarty w atrybucie alt.
<applet code="MyApplet.class" width="100" height="150"
alt="Wcz Jav, a zobaczysz tutaj mj aplet.">
Metod getParameter mona wywoywa tylko w metodzie init apletu, nie w kon-
struktorze, poniewa w chwili jego wykonywania parametry nie s jeszcze gotowe.
Poniewa ukad wikszoci bardziej rozbudowanych apletw jest zdeterminowany przez
parametry, zalecamy zaniechanie uywania konstruktorw w apletach. Cay kod inicjujcy
mona umieci w metodzie init.
Parametry s zawsze zwracane jako acuchy. Jeli wymagana jest liczba, acuch trzeba
przekonwertowa na typ liczbowy. Su do tego standardowe metody, takie jak parseInt
z klasy Integer.
Na przykad kod HTML zawierajcy parametr size, ktry okrela rozmiar czcionki, mgby
wyglda nastpujco:
<applet code="FontParamApplet.class" width="200" height="200">
<param name="font" value="Helvetica"/>
<param name="size" value="24"/>
</applet>
. . .
}
}
Poza upewnieniem si, e parametry w kodzie pasuj, naley sprawdzi, czy parametr size
zosta ustawiony, czy nie. Suy do tego prosty test na obecno wartoci null. Na przykad:
int fontsize;
String sizeString = getParameter("size");
if (sizeString == null) fontSize = 12;
else fontSize = Integer.parseInt(sizeString);
Rysunek 10.14 przedstawia aplet rysujcy wykres supkowy, ktry w duym stopniu wyko-
rzystuje parametry.
Rysunek 10.14.
Aplet
wywietlajcy
wykres
Aplet ten pobiera etykiety i wysokoci supkw z wartoci atrybutw znacznika param. Poni-
ej znajduje si kod HTML strony widocznej na rysunku 10.14.
<applet code="Chart.class" width="400" height="300">
<param name="title" value="rednice planet"/>
<param name="values" value="9"/>
<param name="name.1" value="Merkury"/>
<param name="name.2" value="Wenus"/>
<param name="name.3" value="Ziemia"/>
<param name="name.4" value="Mars"/>
<param name="name.5" value="Jowisz"/>
<param name="name.6" value="Saturn"/>
<param name="name.7" value="Uran"/>
<param name="name.8" value="Neptun"/>
<param name="name.9" value="Pluton"/>
<param name="value.1" value="3100"/>
<param name="value.2" value="7500"/>
<param name="value.3" value="8000"/>
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 543
Mona byo utworzy w aplecie tablic acuchw i tablic liczb, ale wykorzystanie mecha-
nizmu parametrw ma dwie zalety. Po pierwsze, na jednej stronie mona wywietli kilka kopii
tego samego apletu, pokazujcych rne wykresy trzeba zastosowa kilka znacznikw
applet z rnymi zestawami parametrw. Po drugie, mona zmienia dane przedstawione na
wykresie. Wprawdzie rednice planet jeszcze przez jaki czas si nie zmieni, ale wyobramy
sobie, e na stronie przedstawiamy tygodniowy wykres sprzeday. Stron internetow atwo
si aktualizuje, poniewa jest to czysty tekst. Edytowanie i kompilowanie plikw Javy wymaga
znacznie wicej wysiku.
Istniej nawet komercyjne komponenty JavaBean (tak zwane beany), ktre tworz o wiele
atrakcyjniejsze wykresy. Podajc parametry takiemu zakupionemu komponentowi, nie trzeba
nawet wiedzie nic na temat tworzenia wykresw.
Listing 10.4 przedstawia kod rdowy omawianego apletu rysujcego wykres. Naley zauwa-
y, e metoda init pobiera parametry, a metoda paintComponent rysuje wykres.
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* @version 1.33 2007-06-12
* @author Cay Horstmann
*/
public class Chart extends JApplet
{
public void init()
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
String v = getParameter("values");
if (v == null) return;
int n = Integer.parseInt(v);
double[] values = new double[n];
String[] names = new String[n];
for (int i = 0; i < n; i++)
{
values[i] = Double.parseDouble(getParameter("value." + (i + 1)));
names[i] = getParameter("name." + (i + 1));
}
544 Java. Podstawy
/**
* Komponent rysujcy wykres supkowy.
*/
class ChartComponent extends JComponent
{
private double[] values;
private String[] names;
private String title;
/**
* Tworzy obiekt typu ChartComponent.
* @param v tablica wartoci wykresu
* @param n tablica nazw wartoci
* @param t tytu wykresu
*/
public ChartComponent(double[] v, String[] n, String t)
{
values = v;
names = n;
title = t;
}
// Rysowanie tytuu.
double y = -titleBounds.getY(); // wysoko
double x = (panelWidth - titleWidth) / 2;
g2.setFont(titleFont);
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 545
y = panelHeight - labelMetrics.getDescent();
g2.setFont(labelFont);
// Rysowanie supkw.
for (int i = 0; i < values.length; i++)
{
// Uzyskanie wsprzdnych prostokta tworzcego supek.
double x1 = i * barWidth + 1;
double y1 = top;
double height = values[i] * scale;
if (values[i] >= 0) y1 += (maxValue - values[i]) * scale;
else
{
y1 += maxValue * scale;
height = -height;
}
java.applet.Applet 1.0
Lokalizacj obrazw i plikw audio okrela si za pomoc wzgldnych adresw URL. Bazowy
adres URL zazwyczaj sprawdza si za pomoc metody getDocumentBase lub getCodeBase.
Pierwsza z wymienionych metod pobiera adres URL strony HTML zawierajcej aplet, a druga
adres URL katalogu bazowego kodu.
Bazowy adres URL i lokalizacj pliku naley przekaza do metody getImage lub getAudioClip.
Na przykad:
Image cat = getImage(getCodeBase(), "images/cat.gif");
AudioClip meow = getAudioClip(getCodeBase(), "audio/meow.au");
java.applet.Applet 1.0
URL getDocumentBase()
Pobiera adres URL strony zawierajcej aplet.
URL getCodeBase()
Pobiera adres URL katalogu bazowego z kodem, z ktrego adowany jest aplet.
Jest to albo bezwzgldny adres URL katalogu wskazywanego przez atrybut codebase,
albo adres pliku HTML, jeli atrybut codebase nie istnieje.
void play(URL url)
void play(URL url, String name)
Pierwsza wersja odtwarza plik dwikowy znajdujcy si pod podanym adresem
URL. Druga tworzy ciek wzgldn wobec podanego adresu URL z podanego
acucha. Jeli pliku audio nie ma, nic si nie dzieje.
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 547
Jeli kady aplet w pliku HTML ma okrelon nazw w atrybucie name, za pomoc metody
getApplet z interfejsu AppletContext mona utworzy odwoanie do tego apletu. Jeli na
przykad plik HTML zawiera poniszy znacznik:
<applet code="Chart.class" width="100" height="100" name="Chart1">
Do czego mona wykorzysta takie odwoanie? Jeli klasa Chart zawiera metod przyjmu-
jc nowe dane i ponownie rysujc wykres, metod t mona wywoa, wykonujc odpowied-
nie rzutowanie.
((Chart) chart1).setData(3, "Ziemia", 9000);
548 Java. Podstawy
List wszystkich apletw znajdujcych si na stronie mona wywietli bez wzgldu na to,
czy maj one atrybut name, czy nie. Metoda getApplets zwraca obiekt typu wyliczeniowego
(wicej informacji na temat obiektw wyliczeniowych znajduje si w rozdziale 13.). Ponisza
ptla drukuje nazwy klas wszystkich apletw znajdujcych si na stronie:
Enumeration<Applet> e = getAppletContext().getApplets();
while (e.hasMoreElements())
{
Applet a = e.nextElement();
System.out.println(a.getClass().getName());
}
Aby zmusi przegldark do otwarcia nowej strony, naley uy metody showDocument, ktr
mona wywoa na kilka sposobw. Najprostsze wywoanie polega na podaniu jako argumentu
adresu URL strony, ktra ma zosta otwarta:
URL u = new URL("http://java.sun.com/index.html");
getAppletContext().showDocument(u);
Jednak takie wywoanie moe by problematyczne, poniewa nowa strona zastpuje aktual-
n, co powoduje zniknicie apletu. Aby wrci do apletu, konieczne jest nacinicie przycisku
Wstecz w przegldarce.
Aby nowa strona zostaa otwarta w nowym oknie, naley metodzie showDocument poda dodat-
kowy parametr (zobacz tabela 10.2). acuch _blank wymusza otwarcie dokumentu w nowym
oknie. Co ciekawsze, korzystajc z ramek HTML, mona podzieli okno na kilka czci o uni-
katowych nazwach, w ktrych bd wywietlane dokumenty dane przez aplet rwnie znaj-
dujcy si w jednej z ramek. Przykadowy kod prezentujemy w kolejnym podrozdziale.
java.applet.Applet 1.2
java.applet.AppletContext 1.0
Enumeration<Applet> getApplets()
Zwraca wyliczenie (zobacz rozdzia 13.) wszystkich apletw znajdujcych
si w tym samym rodowisku, czyli na jednej stronie internetowej.
Applet getApplet(String name)
Zwraca aplet o podanej nazwie znajdujcy si w biecym kontekcie. Zwraca
warto null, jeli aplet nie istnieje. Przeszukiwana jest tylko bieca strona
internetowa.
void showDocument(URL url)
void showDocument(URL url, String target)
Otwiera now stron internetow w przegldarce. Pierwsza wersja zastpuje star
stron now. Druga otwiera now stron w miejscu okrelonym przez parametr
target (tabela 10.2).
Do zapisania takiej listy wasnoci w pliku naley uy metody store. My zapiszemy nasz
map w pliku o nazwie program.properties. Drugi argument metody store to komentarz, ktry
jest dodawany do pliku.
FileOutputStream out = new FileOutputStream("program.properties");
settings.store(out, "Ustawienia programu");
Dobrze jest na wszelki wypadek dostarczy zestaw ustawie domylnych dla programu.
Klasa Properties dysponuje dwoma mechanizmami pozwalajcymi okreli ustawienia
domylne. Po pierwsze, mona utworzy acuch, ktry bdzie stosowany domylnie za
kadym razem, kiedy dany klucz nie zostanie znaleziony.
String title = settings.getProperty("title", "Domylny tytu");
Jeli w mapie wasnoci znajduje si wasno title, parametr title zostanie ustawiony na
jej acuch. W przeciwnym przypadku parametr ten przyjmie warto Domylny tytu.
Mona nawet dostarczy domylne ustawienia dla ustawie domylnych. Wystarczy tylko
utworzy kolejn map wasnoci i przekaza j do konstruktora defaultSettings. Nie jest
to jednak czsto spotykane rozwizanie.
import java.awt.EventQueue;
import java.awt.event.*;
import java.io.*;
import java.util.Properties;
import javax.swing.*;
/**
* Program testujcy mechanizm wasnoci. Ten program zapamituje pooenie, rozmiar i tytu ramki.
* @version 1.00 2007-04-29
* @author Cay Horstmann
*/
public class PropertiesTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
552 Java. Podstawy
/**
* Ramka pobierajca dane dotyczce pooenia i rozmiaru z pliku wasnoci oraz aktualizujca ten plik
* w momencie zamykania programu
*/
class PropertiesFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public PropertiesFrame()
{
// Pobranie informacji o pooeniu, rozmiarze i tytule z pliku wasnoci
if (propertiesFile.exists()) try
{
FileInputStream in = new FileInputStream(propertiesFile);
settings.load(in);
}
catch (IOException ex)
{
ex.printStackTrace();
}
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent event)
{
settings.put("left", "" + getX());
settings.put("top", "" + getY());
settings.put("width", "" + getWidth());
settings.put("height", "" + getHeight());
settings.put("title", getTitle());
try
{
FileOutputStream out = new FileOutputStream(propertiesFile);
settings.store(out, "Ustawienia programu");
}
catch (IOException ex)
{
ex.printStackTrace();
}
System.exit(0);
}
});
}
}
java.util.Properties 1.0
Properties()
Tworzy pust map wasnoci.
Properties(Properties defaults)
Tworzy map wasnoci z zestawem ustawie domylnych.
Parametr: defaults Wartoci domylne
Pobiera wasno z domyln wartoci, jeli klucz key nie zostanie znaleziony.
Zwraca acuch skojarzony z kluczem key lub acuch domylny, jeli nie ma go
w tablicy.
Parametry: key Klucz, ktrego warto ma zosta pobrana.
defaultValue Warto zwracana, jeli dany klucz nie istnieje.
java.lang.System 1.0
Properties getProperties()
Pobiera wszystkie waciwoci systemowe. Jeli aplikacja nie ma uprawnie
do pobierania wszystkich waciwoci systemowych, generowany jest wyjtek
zabezpiecze.
String getProperty(String key)
Pobiera waciwo systemow opatrzon podanym kluczem. Jeli aplikacja
nie ma uprawnie do pobierania tej waciwoci systemowej, generowany
jest wyjtek zabezpiecze. Ponisze waciwoci mona zawsze pobiera:
java.version
java.vendor
java.vendor.url
java.class.version
os.name
os.version
os.arch
file.separator
path.separator
line.separator
java.specification.version
java.vm.specification.version
java.vm.specification.vendor
java.vm.specification.name
java.vm.version
java.vm.vendor
java.vm.name
Niektre systemy operacyjne maj centralne repozytorium, w ktrym przechowuj dane kon-
figuracyjne. Najlepszym przykadem takiego repozytorium jest rejestr w systemie Microsoft
Windows. Klasa Preferences pozwala utworzy takie repozytorium niezalenie od platformy.
W systemie Windows klasa ta wykorzystuje rejestr. W systemie Linux informacje te s zapi-
sywane w lokalnym systemie plikw. Oczywicie implementacja repozytorium nie ma tajem-
nic dla programisty uywajcego klasy Preferences.
Kady wze w repozytorium ma oddzieln tablic par klucz warto, w ktrej mona
przechowywa acuchy, liczby i tablice bajtw. Nie ma moliwoci zapisywania obiektw
serializowanych, poniewa projektanci uznali, e format ten jest zbyt ulotny do dugotermi-
nowego przechowywania. Oczywicie ci, ktrzy si z tym nie zgadzaj, mog zapisywa seria-
lizowane obiekty w tablicach bajtw.
Aby uzyska dostp do wza drzewa, naley zacz od uytkownika root lub katalogu
systemowego root:
Preferences root = Preferences.userRoot();
lub
Preferences root = Preferences.systemRoot();
Za pomoc wygodnego skrtu mona pobra wze, ktrego cieka jest taka sama jak nazwa
pakietu klasy. Wystarczy pobra obiekt tej klasy i zastosowa ponisze wywoanie:
556 Java. Podstawy
lub
Preferences node = Preferences.systemNodeForPackage(obj.getClass());
Majc wze, mona uzyska dostp do jego tablicy par klucz warto za pomoc nastpu-
jcych metod:
String get(String key, String defval)
int getInt(String key, int defval)
long getLong(String key, long defval)
float getFloat(String key, float defval)
double getDouble(String key, double defval)
boolean getBoolean(String key, boolean defval)
byte[] getByteArray(String key, byte[] defval)
i tak dalej.
List wszystkich kluczy zapisanych w wle mona uzyska za pomoc metody String[] keys.
Nie ma jednak obecnie sposobu na sprawdzenie typu wartoci okrelonego klucza.
Centralne repozytoria, takie jak rejestr w systemie Windows, zazwyczaj cierpi z dwch
powodw:
Z czasem zamieniaj si w mietnisko pene przestarzaych informacji.
Dane konfiguracyjne plcz si w repozytorium, przez co trudno jest je przenie
na inn platform.
Jeli program wykorzystuje klas Preferences, naley umoliwi uytkownikowi import i eks-
port ustawie, co uatwia przenoszenie ustawie na inny komputer. Program na listingu 10.6
demonstruje omwion technik. Zapisuje on pooenie, rozmiar i tytu gwnego okna. Po
zamkniciu i ponownym uruchomieniu programu okno bdzie wygldao dokadnie tak samo
jak przed zamkniciem programu.
import java.awt.EventQueue;
import java.awt.event.*;
import java.io.*;
import java.util.prefs.*;
import javax.swing.*;
/**
* Program testujcy ustawianie preferencji. Zapamituje pooenie, rozmiar i tytu ramki.
* @version 1.02 2007-06-12
* @author Cay Horstmann
*/
public class PreferencesTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
PreferencesFrame frame = new PreferencesFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
558 Java. Podstawy
}
}
/**
* Ramka pobierajca dane dotyczce pooenia i rozmiaru z preferencji uytkownika oraz aktualizujca
* preferencje w momencie zamykania programu
*/
class PreferencesFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public PreferencesFrame()
{
// Sprawdzanie pooenia, rozmiaru i tytuu w preferencjach
// menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
menu.add(exportItem);
exportItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (chooser.showSaveDialog(PreferencesFrame.this) ==
JFileChooser.APPROVE_OPTION)
{
try
{
OutputStream out = new
FileOutputStream(chooser.getSelectedFile());
node.exportSubtree(out);
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
});
node.put("title", getTitle());
System.exit(0);
}
});
}
}
java.util.prefs.Preferences 1.4
Preferences userRoot()
Zwraca wze preferencji root uytkownika programu.
Preferences systemRoot()
Zwraca wze preferencji root systemu.
Preferences node(String path)
Zwraca wze, do ktrego mona uzyska dostp z biecego wza
za porednictwem podanej cieki path. Jeli cieka jest bezwzgldna (czyli zaczyna
si od znaku /), szukanie wza zaczyna si od korzenia drzewa zawierajcego ten
wze preferencji. Jeli wze w podanej ciece nie istnieje, zostanie utworzony.
Preferences userNodeForPackage(Class cl)
Preferences systemNodeForPackage(Class cl)
Zwraca wze w biecym drzewie uytkownika lub drzewo systemowe, ktrego
cieka bezwzgldna wza odpowiada nazwie pakietu klasy cl.
String[] keys()
Zwraca wszystkie klucze nalece do wza.
String get(String key, String defval)
int getInt(String key, int defval)
long getLong(String key, long defval)
float getFloat(String key, float defval)
double getDouble(String key, double defval)
boolean getBoolean(String key, boolean defval)
byte[] getByteArray(String key, byte[] defval)
Zwraca warto skojarzon z danym kluczem lub podan warto domyln,
jeli z kluczem nie jest skojarzona adna warto lub skojarzona warto
jest nieprawidowego typu, lub magazyn preferencji nie jest dostpny.
void put(String key, String value)
void putInt(String key, int value)
void putLong(String key, long value)
void putFloat(String key, float value)
Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku 561
W tym rozdziale:
Obsuga bdw
Obsuga wyjtkw
Wskazwki dotyczce stosowania wyjtkw
Asercje
Dzienniki
Wskazwki dotyczce debugowania
Wskazwki dotyczce debugowania programw z GUI
Praca z debugerem
Jeli podczas pracy z programem wystpi bd, ktry zniweczy jak cz pracy uytkow-
nika, moe on ju nigdy nie wrci do tego programu. Te nieprzyjemne sytuacje mog by
spowodowane niedopatrzeniem programisty lub rozmaitymi czynnikami zewntrznymi.
W takiej sytuacji program musi przynajmniej:
poinformowa uytkownika o bdzie,
zapisa dotychczasow prac,
pozwoli uytkownikowi na eleganckie zakoczenie pracy.
564 Java. Podstawy
Sytuacje wyjtkowe, ktre mog wywoa awari programu (jak na przykad podanie nie-
prawidowych danych na wejciu), s w Javie rozwizywane za pomoc techniki obsugi
wyjtkw. Obsuga wyjtkw w Javie wyglda podobnie jak w jzykach C++ i Delphi.
Pierwsza cz tego rozdziau opisuje wyjtki w Javie.
Typow reakcj na bd w metodzie jest zwrot specjalnego kodu bdu, ktry nastpnie jest
przekazywany do analizy metodzie wywoujcej. Na przykad metody odczytujce dane
z plikw czsto zwracaj warto -1 zamiast standardowego znaku koca pliku. Jest to dosko-
nay sposb na poradzenie sobie z wieloma sytuacjami wyjtkowymi. Inna czsto spotykana
warto zwrotna oznaczajca bd to referencja null.
Niestety nie zawsze da si zwrci kod bdu. Czasami odrnienie poprawnych danych od
niepoprawnych moe by trudne. Metoda zwracajca liczby cakowite nie moe zwrci
wartoci -1 jako bd, poniewa warto ta moe by poprawna.
W zwizku z tym, jeli metoda nie moe normalnie ukoczy swojego dziaania, moe wybra
alternatywny sposb wybrnicia z sytuacji (pisalimy o tym w rozdziale 5.). Wtedy nie zwraca
adnej wartoci, tylko wyrzuca obiekt zawierajcy informacje o bdzie. Zauwamy, e metoda
jest koczona natychmiast nie zwraca adnej wartoci. Ponadto dziaanie programu nie jest
wznawiane od miejsca, w ktrym metoda zostaa wywoana. Zamiast tego mechanizm obsugi
wyjtkw zaczyna szuka procedury obsugi bdw, ktra potrafi rozwiza dany problem.
Wyjtki maj wasn skadni oraz wchodz w skad specjalnej hierarchii dziedziczenia.
Najpierw zajmiemy si t skadni, a nastpnie udzielimy kilku wskazwek dotyczcych
efektywnego wykorzystania tej waciwoci jzyka.
Programici Javy o wiele wicej czasu powicaj klasie Exception. Ona take dzieli si na
dwie gazie: wyjtki zwizane z wykonywaniem, tak zwane wyjtki wykonawcze (Runtime
Exception), i pozostae. Oglna zasada gosi: wyjtki typu RuntimeException s powodo-
wane przez bdy programisty. Wszystkie pozostae maj zwizek z innymi niepodanymi
zdarzeniami, takimi jak bdy wejcia-wyjcia, ktre zaszy w poza tym dobrym programie.
566 Java. Podstawy
Jak wyglda sprawa z adresami URL? Czy nie mona sprawdzi ich skadni przed uyciem?
Problem w tym, e rne przegldarki obsuguj adresy rnego rodzaju. Na przykad prze-
gldarka Firefox poradzi sobie z adresem typu mailto:, podczas gdy przegldarka apletw
nie. W zwizku z tym pojcie niepoprawnej skadni moe mie rne znaczenie w rnych
rodowiskach.
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 567
W specyfikacji Javy wyjtki typu Error lub RuntimeException nazywane s wyjtkami nie-
kontrolowanymi (ang. unchecked exception). Wszystkie pozostae to wyjtki kontrolowane
(ang. checked exception). My rwnie stosujemy t terminologi. Kompilator sprawdza, czy
dostarczono procedury obsugi dla wszystkich wyjtkw kontrolowanych.
Informacj, e metoda moe spowodowa wyjtek, naley umieci w jej nagwku. Tre
nagwka moe si zmienia w zalenoci od tego, jakie wyjtki kontrolowane metoda moe
spowodowa. Poniej znajduje si na przykad deklaracja jednego z konstruktorw klasy
FileInputStream z biblioteki standardowej (wicej na temat strumieni piszemy w rozdziale 12.).
public FileInputStream(String name) throws FileNotFoundException
Piszc metod, nie trzeba informowa o kadym moliwym wyjtku, ktry moe przez ni
zosta zgoszony. Aby zrozumie, co i kiedy naley uwzgldni w klauzuli throws, trzeba
pamita, e wyjtki s zgaszane w nastpujcych sytuacjach:
Wywoanie metody, ktra zgasza wyjtek kontrolowany, na przykad konstruktor
FileInputStream.
Wykrycie bdu i zgoszenie wyjtku kontrolowanego za pomoc instrukcji throw
(instrukcj throw opisujemy w kolejnym podrozdziale).
568 Java. Podstawy
Deklarujc metod, ktra moe spowodowa wyjtek, naley podobnie jak w metodach
nalecych do standardowych klas Javy w nagwku umieci specyfikacj wyjtku infor-
mujc, e metoda ta moe zgosi wyjtek.
class MyAnimation
{
. . .
public Image loadImage(String s) throws IOException
{
. . .
}
}
Jeli metoda moe zgosi wyjtki kontrolowane rnych typw, w jej nagwku musi si
znale ich lista rozdzielona przecinkami:
class MyAnimation
{
. . .
public Image loadImage(String s) throws FileNotFoundException, EOFException
{
. . .
}
}
Nie trzeba natomiast informowa o wyjtkach wewntrznych Javy, czyli tych, ktre dzie-
dzicz po klasie Error. Ich rdem moe by kady fragment kodu, przez co nie ma moli-
woci sprawowania nad nimi kontroli.
Wyjtki tego typu mog by w peni kontrolowane przez programist. Jeli istnieje ryzyko
wystpienia bdw zwizanych z indeksami w tablicy, naley powici chwil czasu na ich
napraw, zamiast informowa, e mog wystpi.
(Error), albo powstaj w takich sytuacjach, do ktrych programista nie powinien dopuci
(RuntimeException). Jeli metoda nie deklaruje wszystkich wyjtkw kontrolowanych, kom-
pilator wywietli komunikat o bdzie.
Jeli metoda deklaruje zgaszanie wyjtkw nalecych do okrelonej klasy, moe zgasza
wyjtki tej klasy lub dowolnej z jej podklas. Na przykad konstruktor FileInputStream moe
deklarowa zgaszanie wyjtkw typu IOException. W takim przypadku nie wiadomo, o jaki
typ wyjtku IOException konkretnie moe chodzi. Moe to by czysty typ IOException lub
jeden z jego podtypw, na przykad FileNotFoundException.
Znak koca pliku pojawia si jednak po 733 znakach. Decydujemy, e sytuacja ta jest na tyle
nienormalna, e trzeba zgosi wyjtek.
Naley podj decyzj, jakiego typu wyjtek to ma by. Dobrym wyborem wydaje si jaki
rodzaj wyjtku IOException. W dokumentacji API mona znale typ EOFException, ktrego
opis brzmi Sygnalizuje niespodziewane napotkanie koca pliku w czasie wczytywania
danych. Doskonale. Zgaszamy go nastpujco:
throw new EOFException();
570 Java. Podstawy
lub
EOFException e = new EOFException();
throw e;
Klasa EOFException posiada jeszcze jeden konstruktor, ktry przyjmuje argument w postaci
acucha. Mona z niego zrobi dobry uytek, dostarczajc bardziej szczegowy opis sytuacji
wyjtkowej.
String gripe = "Content-length: " + len + ", Received: " + n;
throw new EOFException(gripe);
Jak wida, zgaszanie wyjtkw jest proste, jeli istnieje moliwo wykorzystania jednej
z istniejcych klas. W takim przypadku naley:
1. Znale odpowiedni klas wyjtkw.
2. Utworzy obiekt tej klasy.
3. Zgosi go.
Kiedy metoda zgosi wyjtek, nie zwraca wartoci do wywoujcego. Oznacza to, e nie ma
koniecznoci zajmowania si domyln wartoci zwrotn lub kodem bdu.
Zgaszanie wyjtkw w C++ i Javie wyglda prawie tak samo. Jedyna rnica po-
lega na tym, e w Javie mona generowa wycznie obiekty nalece do podklas
klasy Throwable. W C++ mona zgasza wartoci dowolnego typu.
java.lang.Throwable 1.0
Throwable()
Tworzy nowy obiekt typu Throwable, niezawierajcy adnych szczegowych
informacji.
Throwable(String message)
Tworzy obiekt typu Throwable z opisem okrelonym przez parametr message.
Zgodnie z konwencj wszystkie pochodne klasy wyjtkw posiadaj zarwno
konstruktor domylny, jak i konstruktor ze szczegowym komunikatem.
String getMessage()
Zwraca szczegowy opis wyjtku.
Jeli wygenerowany wyjtek nie zostanie przechwycony, program zostanie zamknity, a w kon-
soli pojawi si komunikat informujcy o typie wyjtku i dane ze ledzenia stosu. Programy
z graficznym interfejsem (aplety i aplikacje) przechwytuj wyjtki, drukuj komunikaty ze
572 Java. Podstawy
Do przechwytywania wyjtkw suy blok try-catch. Najprostsza forma tego bloku wyglda
nastpujco:
try
{
kod
wicej kodu
i jeszcze troch kodu
}
catch (TypWyjtku e)
{
procedura obsugi okrelonego typu wyjtkw
}
Jeli ktrykolwiek z fragmentw kodu w bloku try zgosi wyjtek typu okrelonego w klau-
zuli catch, to:
1. Program pominie reszt kodu w bloku try.
2. Program wykona procedur obsugi znajdujc si w klauzuli catch.
Jeli aden z fragmentw kodu w bloku try nie zgosi wyjtku, klauzula catch zostaje
pominita.
Jak wida, wikszo kodu w klauzuli try jest prosta odczytuje i przetwarza bajty a do
napotkania koca pliku. Rzut oka do API Javy pozwala si zorientowa, e metoda read moe
zgosi wyjtek IOException. W takim przypadku wychodzimy z ptli while, wchodzimy do
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 573
Czsto najlepiej jest nic nie robi, tylko przekaza wyjtek do wywoujcego. Jeli bd
powstanie w metodzie read, najlepiej, by zaja si nim procedura, ktra wywoaa t metod!
Takie podejcie do problemu wymaga dodania informacji, e metoda moe zgosi wyjtek
IOException.
public void read(String filename) throws IOException
{
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read()) != -1)
{
przetwarzanie danych wejciowych
}
}
Ktre z tych dwch rozwiza jest lepsze? Oglna zasada nakazuje przechwytywa te wyjtki,
ktre mona obsuy, i odsya te, ktrych nie potrafimy obsuy.
Odsyajc wyjtek, trzeba doda specyfikator throws, aby ostrzec wywoujcego, e moe
zosta wygenerowany wyjtek.
Informacje na temat typw wyjtkw zgaszanych przez konkretne metody mona znale
w dokumentacji API. Dysponujc tymi informacjami, mona zdecydowa, czy je obsuy,
czy doda do listy throws. Wybr tej drugiej moliwoci nie stanowi dla programisty adnej
ujmy. Lepiej przekaza wyjtek do fachowej obsugi, ni obsuy go le.
Jest jeden wyjtek od tej reguy, o ktrym ju wspominalimy. Piszc metod przesaniajc
metod z nadklasy, ktra nie zgasza adnych wyjtkw (np. paintComponent z klasy JCompo
nent), kady kontrolowany wyjtek musimy przechwyci w kodzie tej metody. Nie mona
doda wicej specyfikatorw throws do metody w podklasie, ni jest w metodzie w nadklasie.
Przechwytywanie wyjtkw w Javie wyglda prawie tak samo jak w C++. Kod:
catch (Exception e) // Java
jest analogiczny do kodu:
catch (Exception& e) // C++
Nie istnieje instrukcja analogiczna do catch(...). Nie jest ona potrzebna w Javie,
poniewa wszystkie wyjtki pochodz od wsplnej nadklasy.
574 Java. Podstawy
Obiekt wyjtku moe zawiera informacje o naturze wyjtku. Aby dowiedzie si wicej
o danym obiekcie, naley uy nastpujcego wywoania:
e.getMessage()
Ponisza instrukcja zwraca szczegowe dane na temat bdu (jeli istniej) lub rzeczywisty
typ obiektu wyjtku:
e.getClass().getName()
Od Java SE 7 mona przechwytywa rne typy wyjtkw w jednej klauzuli catch. Przy-
pumy na przykad, e dziaanie w przypadku braku pliku i nieznanego hosta jest takie
samo. Wwczas mona poczy klauzule catch w nastpujcy sposb:
try
{
kod, ktry moe spowodowa wyjtki
}
catch (FileNotFoundException | UnknownHostException e)
{
dziaania awaryjne w przypadku braku plikw lub nieznanego hosta
}
catch (IOException e)
{
dziaania awaryjne dotyczce pozostaych problemw wejcia i wyjcia
}
Jest to potrzebne tylko wtedy, gdy typ jednego z przechwytywanych wyjtkw nie jest podklas
innego.
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 575
Gdy przechwytywanych jest kilka wyjtkw, zmienn wyjtku jest final. Przyka-
dowo zmiennej e w klauzuli nie mona przypisa innej wartoci:
catch (FileNotFoundException | UnknownHostException e) { ... }
Przechwytywanie po kilka wyjtkw naraz nie tylko sprawia, e kod jest bardziej
przejrzysty, ale rwnie powoduje jego szybsze dziaanie. W wygenerowanym kodzie
bajtowym znajduje si tylko jeden blok dla wsplnej klauzuli catch.
Mona jednak zrobi co lepszego, czyli ustawi pierwotny wyjtek jako powd (ang. cause)
nowego wyjtku:
try
{
dostp do bazy danych
}
catch (SQLException e)
{
Throwable se = new ServletException("database error");
se.initCause(e);
throw se;
}
Czasami trzeba tylko zarejestrowa informacj o wyjtku i zgosi go ponownie bez zmie-
niania:
try
{
dostp do bazy danych
}
catch (Exception e)
{
logger.log(level, message, e);
throw e;
}
Kompilator najpierw znajdowa instrukcj throw w bloku catch, nastpnie sprawdza typ e
i narzeka, e metoda ta moe zgasza dowolny typ wyjtku, nie tylko SQLException. Zostao
to ju naprawione. Kompilator bierze pod uwag, e e pochodzi z bloku try. Biorc pod uwag,
e w bloku tym jedynymi wyjtkami kontrolowanymi s egzemplarze klasy SQLException, oraz
uwzgldniajc fakt, e e nie zmienia si w bloku catch, mona powiedzie, e otaczajca
metoda zgasza wyjtki typu SQLException.
W Javie dostpne jest lepsze rozwizanie polegajce na uyciu klauzuli finally. Poniej
przedstawiamy, jak prawidowo pozby si obiektu Graphics. Tych samych technik naley
uywa do zamykania pocze z baz danych. W rozdziale 4. drugiego tomu wyjaniamy,
dlaczego zamknicie wszystkich pocze z baz danych jest bardzo wane, nawet kiedy
wystpi wyjtki.
Kod w klauzuli finally jest wykonywany bez wzgldu na to, czy wyjtek zostanie prze-
chwycony, czy nie. W poniszym przykadzie kontekst graficzny zostanie usunity bez
wzgldu na okolicznoci.
InputStream in = new FileInputStream(...);
try
{
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 577
// 1
potencjalne rdo wyjtkw
// 2
}
catch (IOException e)
{
// 3
wywietlanie okna dialogowego bdu
// 4
}
finally
{
// 5
in.close();
}
// 6
Przeanalizujmy trzy moliwe sytuacje, w ktrych program wykona zawarto klauzuli finally:
1. Kod nie generuje adnego wyjtku. W tym przypadku najpierw wykonywany jest
kod w klauzuli try. Nastpnie wykonywana jest klauzula finally. Dalej sterowanie
przekazywane jest do pierwszej instrukcji za klauzul finally. Innymi sowy,
przepyw sterowania jest nastpujcy: 1, 2, 5 i 6.
2. Kod generuje wyjtek, w tym przypadku IOException, przechwytywany w klauzuli
catch. W tym przypadku kod w bloku try jest wykonywany do punktu, w ktrym
zosta wygenerowany wyjtek. Reszta kodu w tym bloku zostaje pominita. Nastpnie
wykonywany jest kod z pasujcej klauzuli catch i kod z klauzuli finally.
Jeli kod nie wygeneruje wyjtku, wykonany zostanie pierwszy wiersz kodu
za klauzul finally. Tym razem przepyw sterowania jest taki: 1, 3, 4, 5, 6.
Jeli klauzula catch wygeneruje wyjtek, zostaje on zwrcony do metody,
ktra wywoaa t metod, i przepyw sterowania przechodzi przez punkty 1, 3 i 5.
3. Kod zgasza wyjtek, ktrego nie przechwytuje adna klauzula catch. Wykonywany
jest kod w bloku try do momentu wygenerowania wyjtku. Nastpnie wykonywany
jest kod w klauzuli finally i wyjtek jest zwracany do metody, ktra wywoaa
t metod. Sterowanie przechodzi tylko przez punkty 1 i 5.
Klauzuli finally mona uy bez klauzuli catch. Przyjrzyjmy si poniszej instrukcji try:
InputStream in = ...;
try
{
potencjalne rdo wyjtkw
}
finally
{
in.close();
}
Instrukcja in.close() w klauzuli finally zostanie wykonana bez wzgldu na to, czy w bloku
try zostanie zgoszony wyjtek. Oczywicie, jeli wyjtek zostanie wygenerowany, jest on
zgaszany ponownie i musi zosta przechwycony w innej klauzuli catch.
578 Java. Podstawy
W poniszej wskazwce wyjaniamy, dlaczego naszym zdaniem dobrym pomysem jest uycie
w ten sposb klauzuli finally do zamykania zasobw.
Wewntrzny blok try ma tylko jedno zadanie: pilnuje, czy strumie wejciowy zosta
zamknity. Zewntrzny blok try ma rwnie tylko jedno zadanie: pilnuje, aby bdy byy
wyciszane. Rozwizanie to jest nie tylko bardziej przejrzyste, ale te bardziej funkcjo-
nalne bdy w klauzuli finally s zgaszane.
Jeli wywoamy metod f(2), blok try wyliczy warto r = 4 i wykona instrukcj return.
Jednak przed zwrceniem tej wartoci zostanie wykonana klauzula finally, ktra
spowoduje zwrot wartoci 0 zamiast spodziewanej 4.
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 579
Czasami klauzula finally powoduje powane problemy, zwaszcza kiedy metoda sprztajca
take moe zgosi wyjtek. Wyobramy sobie, e chcemy, aby w chwili wystpienia wyjtku
w kodzie przetwarzajcym strumie zosta on zamknity.
InputStream in = ...;
try
{
potencjalne rdo wyjtkw
}
finally
{
in.close();
}
Nastpnie wyobramy sobie, e kod w bloku try zgasza wyjtek innego typu ni IOException,
ktry ley w sferze zainteresowa wywoujcego ten kod. Zostaje wykonana klauzula finally
i nastpuje wywoanie metody close. Ta metoda sama moe wygenerowa wyjtek IOExcep
tion! Jeli tak si stanie, oryginalny wyjtek zostaje utracony i zamiast niego zgaszany jest
wyjtek IOException.
Stanowi to problem, poniewa pierwszy wyjtek moe by bardziej interesujcy. Jeli chcesz
dziaa zgodnie ze sztuk i ponownie zgosi pierwotny wyjtek, to musisz bardzo skompli-
kowa kod. Oto przykad:
InputStream in = ...;
Exception ex = null;
try
{
try
{
potencjalne rdo wyjtkw
}
catch (Exception e)
{
ex = e;
throw e;
}
}
finally
{
try
{
in.close();
}
catch (Exception e)
{
if (ex == null) throw e;
}
}
Zasb musi jedynie nalee do klasy implementujcej interfejs AutoCloseable. Interfejs ten
ma tylko jedn metod:
void close() throws Exception
Jeli blok try istnieje, metoda res.close() jest wywoywana automatycznie. Oto typowy
przykad odczyt wszystkich sw z pliku:
try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")))
{
while (in.hasNext())
System.out.println(in.next());
}
Gdy dziaanie bloku zakoczy si normalnie lub wystpi wyjtek, nastpi wywoanie metody
in.close(), jak gdyby zdefiniowana bya klauzula finally.
Jak widziae w poprzednim podrozdziale, problemy powstaj w momencie, gdy zarwno blok
try, jak i metoda close zgaszaj wyjtek. Dziki instrukcji try z zasobami mona to elegancko
rozwiza. Oryginalny wyjtek jest zgaszany ponownie, a wszystkie wyjtki zgoszone przez
metody close s tumione. Zostaj automatycznie przechwycone i dodane do oryginalnego
wyjtku przy uyciu metody addSuppressed. Jeli Ci interesuj, moesz uzyska tablic wyjt-
kw metody close, wywoujc metod getSuppressed.
Nie musisz si mczy. Jeli musisz zamkn jaki zasb, zawsze uywaj instrukcji try
z zasobami.
Wicej informacji na temat interfejsu Map i wtkw znajduje si w rozdziaach 13. i 14.
Listing 11.1 drukuje stos wywoa rekursywnej funkcji obliczajcej silni. Na przykad wywo-
anie factorial(3) zwrci nastpujcy wynik:
factorial(3):
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.main(StackTraceTest.java:34)
factorial(2):
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.factorial(StackTraceTest.java:24)
StackTraceTest.main(StackTraceTest.java:34)
factorial(1):
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.factorial(StackTraceTest.java:24)
StackTraceTest.factorial(StackTraceTest.java:24)
StackTraceTest.main(StackTraceTest.java:34)
return 1
return 2
return 6
import java.util.*;
/**
* Program wywietlajcy stos wywoa wywoania rekursywnej metody.
* @version 1.01 2004-05-10
* @author Cay Horstmann
*/
public class StackTraceTest
{
/**
* Oblicza silni liczby.
* @param n nieujemna liczba cakowita
* @return n! = 1 * 2 * . . . * n
*/
public static int factorial(int n)
{
System.out.println("factorial(" + n + "):");
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement f : frames)
System.out.println(f);
int r;
if (n <= 1) r = 1;
else r = n * factorial(n - 1);
System.out.println("return " + r);
return r;
}
factorial(n);
}
}
java.lang.Throwable 1.0
java.lang.Exception 1.0
java.lang.RuntimeException 1.0
java.lang.StackTraceElement 1.4
String getFileName()
Zwraca nazw pliku rdowego zawierajcego punkt wykonania elementu
lub warto null, jeli informacja ta jest niedostpna.
584 Java. Podstawy
int getLineNumber()
Zwraca numer wiersza w pliku rdowym zawierajcym punkt wykonania
elementu lub warto -1, jeli informacja ta jest niedostpna.
String getClassName()
Zwraca nazw klasy z penym kwalifikatorem, zawierajcej punkt wykonania
elementu.
String getMethodName()
Zwraca nazw metody zawierajcej punkt wykonania elementu. Nazwa konstruktora
to <init>. Nazwa statycznego inicjatora to <clinit>. Nie mona odrni
przeadowanych metod o tej samej nazwie.
boolean isNativeMethod()
Zwraca warto true, jeli punkt wykonania elementu znajduje si w metodzie
rodzimej.
String toString()
Zwraca sformatowany acuch zawierajcy nazw metody i klasy oraz nazw
pliku i numer wiersza, jeli dane te s dostpne.
Taki sposb programowania drastycznie zwiksza ilo kodu. Miej na uwadze cel,
ktry chcesz osign. W tym przypadku chcemy pobra 100 liczb ze stosu i zapisa
je w pliku (nie zastanawiaj si po co to tylko dla zabawy). Jeli pojawi si jaki
problem, nic nie moemy zrobi. Jeli stos jest pusty, to si nagle nie zapeni. Jeli
plik zawiera bd, to bd ten si magicznie nie naprawi. W takim razie rozsdnie
by byo umieci cay kod tego zadania w jednym bloku try. Jeli ktre z dziaa
si nie powiedzie, mona zatrzyma cae zadanie.
try
{
for (i = 0; i < 100; i++)
{
n = s.pop();
out.writeInt(n);
}
}
catch (IOException e)
{
// problem z zapisem w pliku
}
catch (EmptyStackException s)
{
// stos by pusty
}
Ten kod jest znacznie bardziej przejrzysty i spenia jedn z zasad obsugi bdw
oddziela normalny tok dziaania od procedur obsugi bdw.
586 Java. Podstawy
Dziki temu kod przejdzie kompilacj bez problemu. Bdzie dziaa jak naley,
dopki nie wystpi wyjtek, ktry zostanie zignorowany. Jeli uwaasz, e wyjtki
s wane, powi nieco czasu, aby je prawidowo obsuy.
5. Nie bd pobaliwy.
Niektrzy programici czuj obawy przed generowaniem wyjtkw w odpowiedzi
na bdy. Myl, e moe lepiej byoby w zamian zwrci jak warto, kiedy
metoda zostanie wywoana z nieprawidowymi parametrami. Czy na przykad metoda
Stack.pop nie mogaby zwrci wartoci null zamiast wyjtku, kiedy stos jest pusty?
Naszym zdaniem lepiej jest zgosi wyjtek EmptyStackException w punkcie
wystpienia awarii, ni pniej odebra wyjtek NullPointerException.
6. Przekazywanie wyjtkw nie stanowi ujmy.
Wielu programistw czuje si zobowizanych do przechwycenia wszystkich
zgaszanych wyjtkw. Kiedy wywouj metod, ktra moe spowodowa wyjtek,
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 587
11.4. Asercje
Asercje s powszechnie stosowan technik programowania zachowawczego. Zamy, e
mamy pewno, i okrelony warunek, na ktrym polegamy w programie, jest speniony.
Moemy na przykad wykonywa nastpujce dziaanie:
double y = Math.sqrt(x);
Jestemy pewni, e zmienna x nie ma wartoci ujemnej. Moe ona by wynikiem innego
dziaania, ktre nie moe zwraca ujemnych wynikw, lub jest parametrem metody, ktra
przyjmuje tylko wartoci dodatnie. Mimo to wolimy jednak sprawdzi dwa razy, ni pozwoli
wkra si do swoich oblicze nieprawidowym wartociom. Oczywicie jednym z wyj jest
wygenerowanie wyjtku:
if (x < 0) throw new IllegalArgumentException("x < 0");
Niestety ten kod pozostanie w programie nawet po skoczeniu testw. Jeli takich miejsc
jest duo, program bdzie dziaa zauwaalnie wolniej, ni powinien.
W Javie dostpne jest sowo kluczowe assert. Istniej dwa podstawowe sposoby jego sto-
sowania:
assert warunek;
i
assert warunek : wyraenie
Kada z tych wersji sprawdza warunek i zgasza bd AssertionError, a jeli warunek nie
zostanie speniony warto false. W drugiej wersji wyraenie jest przekazywane do kon-
struktora obiektu AssertionError i zamieniane na acuch komunikatu.
588 Java. Podstawy
Makro assert w jzyku C zamienia warunek asercji w acuch, ktry jest dru-
kowany w przypadku niespenienia warunku asercji. Jeli na przykad warunek
assert(x>=0) nie zostanie speniony, zostanie wydrukowane, e warunek, ktry nie zosta
speniony, to x >= 0. W Javie warunek nie staje si automatycznie czci komunikatu
o bdzie. Aby go zobaczy, trzeba go przekaza jako acuch do obiektu Assertion
Error: x>=0 : "x>=0".
Pamitajmy, e, aby wczy lub wyczy asercje, nie trzeba ponownie kompilowa programu.
Funkcja ta naley do moduu adujcego klasy (ang. class loader). Kiedy asercje s wy-
czone, modu ten usuwa ich kod, aby nie spowalniay programu.
Asercje mona nawet wczy tylko w wybranej klasie lub pakiecie. Na przykad:
java -ea:MyClass -ea:com.mycompany.mylib... MyApp
Mona te wyczy asercje w wybranych klasach i pakietach. Suy do tego opcja -disa
bleassertions, w skrcie -da:
java -ea:... -da:MyClass MyApp
Niektre klasy nie s adowane przez modu adujcy, a bezporednio przez maszyn wirtualn.
Za pomoc opisywanych opcji mona wcza i wycza wybrane asercje take w tych
klasach.
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 589
Opcje -ea i -da, ktre wczaj i wyczaj wszystkie asercje, nie dziaaj w klasach biblioteki
systemowej. Do wczania asercji w tych klasach suy opcja -enablesystemassertions,
w skrcie -esa.
Stan asercji moduw adujcych mona take kontrolowa z poziomu programu. Wicej
informacji na ten temat znajduje si w wycigach z API na kocu tego podrozdziau.
W zwizku z tym asercji nie naley stosowa do sygnalizowania innym czciom programu
bdw, ktre mona naprawi, lub informowania uytkownika o problemach. Asercji naley
uywa wycznie do lokalizacji bdw wewntrznych powstajcych w fazie testowej.
Czy powinnimy utworzy asercj zapewniajc, e a nie ma wartoci null? Nie. Dokumen-
tacja milczy, jeli chodzi o zachowanie metody, kiedy a ma warto null. Wywoujcy mog
si spodziewa, e w takim przypadku zwrci ona warto, a nie zgosi bd niespenienia
warunku asercji.
Zobaczmy jednak, jak by wygldaa sytuacja, gdyby umowa bya nieco inna:
@param a tablica do posortowania (nie moe by null)
Teraz wywoujcy metod wie, e tej metody nie mona wywoywa na rzecz pustej tablicy.
W zwizku z tym moe si ona zaczyna od asercji:
assert a != null;
Oczywicie jeszcze lepiej byoby przemyle to dokadniej. Jakie s moliwe wartoci dziaa-
nia i % 3? Jeli i jest liczb dodatni, reszta moe wynosi 0, 1 lub 2. Jeli i jest liczb ujemn,
reszta moe mie warto -1 lub -2. W zwizku z tym prawdziwe zaoenie mwi, e i nie
moe by liczb ujemn. Lepsza byaby nastpujca asercja przed instrukcj if:
assert i >= 0;
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 591
W kadym razie ten przykad pokazuje dobry sposb uycia asercji do sprawdzenia samego
siebie przez programist. Jak wida, asercje s taktycznym narzdziem uywanym w testo-
waniu i debugowaniu. Natomiast rejestracja danych w dzienniku jest narzdziem strategicz-
nym uywanym w caym cyklu ycia programu. Dziennikami zajmiemy si w kolejnym pod-
rozdziale.
java.lang.ClassLoader 1.0
11.5. Dzienniki
adnemu programicie nie jest obce wstawianie instrukcji System.out.println do kodu spra-
wiajcego problemy w celu uzyskania wgldu w dziaanie programu. Po odkryciu rda
problemw instrukcj tak usuwamy, aby uy jej ponownie, gdy wystpi kolejny problem. API
Logging ma za zadanie zlikwidowa t niedogodno. Poniej znajduje si lista jego najwa-
niejszych zalet:
Mona atwo wyczy rejestracj wszystkich lub wybranych poziomw rekordw
w dzienniku. Ich ponowne wczenie jest rwnie atwe.
Wyczanie rejestracji danych jest bardzo mao kosztowne, co znaczy,
e pozostawiony w programie kod rejestrujcy powoduje minimalne opnienia.
Rekordy dziennika mona wysya do rnych procedur obsugi, ktre wywietl
go w konsoli, zapisz w pliku itd.
Zarwno rejestratory (ang. logger), jak i procedury obsugi mog filtrowa rekordy.
Filtr odsiewa niepotrzebne dane przy uyciu kryteriw podanych przez programist.
Rekordy w dzienniku mog by formatowane na rne sposoby, na przykad jako
czysty tekst lub XML.
592 Java. Podstawy
Natomiast wywoanie:
Logger.global.setLevel(Level.OFF);
Podobnie jak nazwy pakietw, nazwy rejestratorw wykazuj struktur hierarchiczn. W rze-
czywistoci s one bardziej hierarchiczne ni pakiety. Pomidzy pakietem a jego przodkiem
nie ma adnych zwizkw semantycznych. Natomiast rejestratory potomne dziel pewne wa-
snoci ze swoimi przodkami. Jeli na przykad ustawimy priorytet informacji zapisywanych
przez rejestrator com.mycompany, rejestratory potomne odziedzicz ten priorytet.
INFO,
CONFIG,
FINE,
FINER,
FINEST.
Domylnie wczone s trzy pierwsze poziomy. Aby ustawi inny poziom, mona uy poni-
szej instrukcji:
logger.setLevel(Level.FINE);
Aby wczy zapis wszystkich poziomw, naley napisa Level.ALL, a aby cakowicie wy-
czy zapis do dziennika, naley napisa Level.OFF.
Na przykad:
int read(String file, String pattern)
{
logger.entering("com.mycompany.mylib.Reader", "read",
new Object[] { file, pattern });
. . .
logger.exiting("com.mycompany.mylib.Reader", "read", count);
return count;
}
Typowe zastosowania:
if (. . .)
{
IOException exception = new IOException(". . .");
logger.throwing("com.mycompany.mylib.Reader", "read", exception);
throw exception;
}
i
try
{
. . .
}
catch (IOException e)
{
Logger.getLogger("com.mycompany.myapp").log(Level.WARNING, "Reading image", e);
}
Aby zmieni domylny poziom rejestracji, naley w pliku konfiguracyjnym zmieni poniszy
wiersz:
.level=INFO
Jak przekonamy si dalej, rejestratory nie wysyaj komunikatw do konsoli, poniewa jest
to zadanie dla obiektw typu Handler. Obiekty te rwnie maj swoje poziomy. Aby w kon-
soli byy wywietlane komunikaty poziomu FINE, naley zastosowa nastpujce ustawienie:
java.util.logging.ConsoleHandler.level=FINE
11.5.4. Lokalizacja
Czasami konieczna jest lokalizacja komunikatw dziennika, aby byy zrozumiae dla uyt-
kownikw z rnych krajw. Lokalizacji powicilimy rozdzia 5. drugiego tomu. Tutaj krtko
opisujemy, o czym trzeba pamita przy lokalizacji komunikatw dziennika.
Program moe zawiera kilka pakietw lokalizacyjnych, np. jeden dla menu i jeden dla
komunikatw dziennika. Kady pakiet ma swoj nazw (np. com.mycompany.logmessages).
Dodajc odwzorowania do pakietu lokalizacyjnego, naley dostarczy plik dla kadej loka-
lizacji. Dane dotyczce jzyka angielskiego znajduj si w pliku o nazwie com/mycompany/
logmessages_en.properties, a jzyka niemieckiego w pliku com/mycompany/logmessages_
de.properties (en i de to kody jzykw). Pliki te powinny si znajdowa w tym samym miejscu
co pliki klas aplikacji, aby klasa ResourceBundle moga je automatycznie odszuka. Format tych
plikw to czysty tekst, a ich struktura wyglda nastpujco:
readingFile=Achtung! Datei wird eingelesen
renamingFile=Datei wird umbenannt
...
Nastpnie naley poda klucz pakietu lokalizacyjnego (a nie rzeczywisty acuch komunikatu)
dla komunikatu dziennika.
logger.info("readingFile");
Podobnie jak rejestratory, obiekty Handler maj poziomy rejestracji. Aby rekord zosta zapi-
sany, jego poziom musi by wyszy ni poziom zarwno rejestratora, jak i obiektu Handler.
Plik konfiguracyjny menedera dziennikw ustawia poziom rejestracji domylnego obiektu
Handler konsoli nastpujco:
java.util.logging.ConsoleHandler.level=INFO
Aby rejestrowa rekordy poziomu FINE, naley zmieni poziom domylnego rejestratora
i obiektu Handler w konfiguracji. Istnieje te moliwo cakowitego pominicia pliku konfi-
guracyjnego i instalacji wasnego obiektu Handler.
Logger logger = Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false);
Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);
Domylnie rejestrator wysya rekordy zarwno do swoich wasnych obiektw Handler, jak
i obiektw Handler swojego przodka. Nasz rejestrator jest potomkiem pierwotnego reje-
stratora (o nazwie ""), ktry wysya wszystkie rekordy poziomu INFO lub wyszego do
konsoli. Nie chcemy jednak oglda tych rekordw dwa razy. Dlatego ustawiamy wasno
useParentHandlers na false.
Aby wysa rekordy dziennika gdzie indziej, trzeba doda jeszcze jeden obiekt Handler. API
dziennikw udostpnia dwa przydatne typy obiektw Handler : FileHandler i SocketHandler.
Ten drugi wysya rekordy do okrelonego hosta i portu. Nas bardziej interesuje FileHandler,
ktry zapisuje rekordy w plikach.
Zazwyczaj programista nie chce uywa domylnej nazwy pliku dziennika. W tym celu naley
zastosowa inny wzorzec, jak %h/myapp.log (zmienne wzorcw zostay przedstawione
w tabeli 11.2).
Jeli kilka aplikacji (lub kilka kopii jednej aplikacji) uywa tego samego dziennika, naley
wczy znacznik append lub uy zmiennej %u we wzorcu nazwy pliku, aby kada aplikacja
tworzya unikaln kopi dziennika.
Istnieje moliwo zdefiniowania wasnego typu Handler poprzez rozszerzenie klasy Handler
lub StreamHandler. Przykad takiego typu prezentujemy w programie demonstracyjnym
zamieszczonym na kocu tego podrozdziau. Wywietla on rekordy w oknie (zobacz rysu-
nek 11.2).
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 599
Zmienna Opis
%h Warto wasnoci user.home
%t Katalog tymczasowy systemu
%u Unikalna liczba pozwalajca unikn konfliktw
%g Numer pokolenia dziennikw (przyrostek .%g jest uywany, jeli rotacja jest wczona,
a wzorzec nie zawiera zmiennej %g)
%% Znak %
Wielu programistw wykorzystuje dzienniki jako pomoc dla obsugi technicznej. Jeli
program le dziaa, uytkownik moe odesa dzienniki do sprawdzenia. W takim
przypadku naley wczy znacznik append bd uywa dziennikw rotacyjnych albo jedno
i drugie.
Rysunek 11.2.
Klasa
typu Handler
wywietlajca
rekordy w oknie
Nasza klasa rozszerza klas StreamHandler i instaluje strumie, ktrego metody write drukuj
dane wyjciowe w obszarze tekstowym.
class WindowHandler extends StreamHandler
{
public WindowHandler()
{
. . .
final JTextArea output = new JTextArea();
setOutputStream(new
OutputStream()
{
public void write(int b) {} // nie jest wywoywana
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
. . .
}
Z metod t zwizany jest tylko jeden problem obiekt Handler buforuje rekordy i wysya je
do strumienia dopiero po zapenieniu bufora. Dlatego przedefiniowalimy metod publish, aby
oprniaa bufor po kadym rekordzie.
600 Java. Podstawy
Aby napisa bardziej niezwyk klas Handler dla strumieni, mona rozszerzy klas Handler
i zdefiniowa metody publish, flush oraz close.
11.5.6. Filtry
Domylnie rekordy s filtrowane zgodnie z ich priorytetami. Jednak kady rejestrator i obiekt
Handler moe posiada dodatkowy filtr rekordw. Definicja filtru polega na zaimplemento-
waniu interfejsu Filter i zdefiniowaniu poniszej metody:
boolean isLoggable(LogRecord record)
11.5.7. Formatery
Klasy ConsoleHandler i FileHandler tworz rekordy dziennikw w formacie tekstowym
lub XML. Mona jednak zdefiniowa wasny format. W tym celu naley rozszerzy klas
Formatter i przedefiniowa ponisz metod:
String format(LogRecord record)
Wiele formatw plikw (np. XML) wymaga, aby formatowane rekordy miay jaki nagwek
i stopk. W takim przypadku naley przesoni ponisze metody:
String getHead(Handler h)
String getTail(Handler h)
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 601
Dla wygody do klas, ktre robi duo zapisw, mona doda pola statyczne:
private static final Logger logger = Logger.getLogger("com.mycompany.myprog");
try
{
. . .
}
catch (SomeException e)
{
logger.log(Level.FINE, "objanienie", e);
}
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.logging.*;
import javax.swing.*;
/**
* Zmodyfikowana przegldarka grafiki, ktra zapisuje w dzienniku informacje o rnych zdarzeniach
* @version 1.02 2007-05-31
* @author Cay Horstmann
*/
public class LoggingImageViewer
{
public static void main(String[] args)
{
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null)
{
try
{
Logger.getLogger("com.horstmann.corejava").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
Handler handler = new FileHandler("%h/LoggingImageViewer.log", 0,
LOG_ROTATION_COUNT);
Logger.getLogger("com.horstmann.corejava").addHandler(handler);
}
catch (IOException e)
{
Logger.getLogger("com.horstmann.corejava").log(Level.SEVERE,
"Nie mona utworzy obiektu obsugi pliku dziennika", e);
}
}
EventQueue.invokeLater(new Runnable()
{
public void run()
{
Handler windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger("com.horstmann.corejava").addHandler(windowHandler);
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 603
Logger.getLogger("com.horstmann.corejava").fine("Wywietlanie ramki");
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca obraz
*/
class ImageViewerFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
public ImageViewerFrame()
{
logger.entering("ImageViewerFrame", "<init>");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Pasek menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
// Etykieta
label = new JLabel();
add(label);
logger.exiting("ImageViewerFrame", "<init>");
}
{
logger.entering("ImageViewerFrame.FileOpenListener", "actionPerformed",
event);
// Jeli plik obrazu zosta zaakceptowany, jest on ustawiany jako ikona etykiety
if (r == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
logger.log(Level.FINE, "Wczytywanie pliku {0}", name);
label.setIcon(new ImageIcon(name));
}
else logger.fine("Anulowano okno otwierania pliku.");
logger.exiting("ImageViewerFrame.FileOpenListener", "actionPerformed");
}
}
}
/**
* Klasa obsugi wywietlania rekordw dziennika w oknie
*/
class WindowHandler extends StreamHandler
{
private JFrame frame;
public WindowHandler()
{
frame = new JFrame();
final JTextArea output = new JTextArea();
output.setEditable(false);
frame.setSize(200, 200);
frame.add(new JScrollPane(output));
frame.setFocusableWindowState(false);
frame.setVisible(true);
setOutputStream(new OutputStream()
{
public void write(int b)
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 605
{
} // nie jest wywoywana
java.util.logging.Logger 1.4
void addHandler(Handler h)
void removeHandler(Handler h)
Dodaje lub usuwa obiekt Handler rejestratora.
boolean getUseParentHandlers()
void setUseParentHandlers(boolean b)
Sprawdza bd ustawia wasno use parent handler. Jeli wasno ta ma warto
true, rejestrator przesya wszystkie zapisane rekordy do obiektw Handler swojego
przodka.
Filter getFilter()
void setFilter(Filter f)
Sprawdza i ustawia filtr dla rejestratora.
java.util.logging.Handler 1.4
java.util.logging.ConsoleHandler 1.4
ConsoleHandler()
Tworzy nowy obiekt Handler konsoli.
java.util.logging.FileHandler 1.4
FileHandler(String pattern)
FileHandler(String pattern, boolean append)
608 Java. Podstawy
java.util.logging.LogRecord 1.4
Level getLevel()
Zwraca poziom rekordu.
String getLoggerName()
Zwraca nazw rejestratora, ktry zarejestrowa dany rekord.
ResourceBundle getResourceBundle()
String getResourceBundleName()
Zwraca pakiet lokalizacyjny, ktry ma zosta uyty do lokalizacji komunikatu,
bd jego nazw albo warto null, jeli nie dostarczono adnego pakietu.
String getMessage()
Pobiera surowy komunikat przed lokalizacj lub formatowaniem.
Object[] getParameters()
Zwraca obiekty parametrw lub warto null, jeli nie ma adnego parametru.
Throwable getThrown()
Zwraca wyrzucony obiekt lub warto null, jeli nie ma takiego obiektu.
String getSourceClassName()
String getSourceMethodName()
Zwraca lokalizacj procedury, ktra zarejestrowaa rekord. Informacja ta moe
zosta dostarczona przez procedur rejestrujc lub pobrana bezporednio ze stosu
wywoa. Moe by nieprawidowa, jeli procedura rejestrujca poda nieprawidow
warto lub uruchomiony fragment kodu zosta zoptymalizowany, przez co dokadnej
lokalizacji nie da si sprawdzi.
long getMillis()
Zwraca czas tworzenia w milisekundach, od 1970 roku.
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 609
long getSequenceNumber()
Zwraca unikalny numer sekwencji rekordu.
int getThreadID()
Zwraca unikalny identyfikator ID wtku, w ktrym zosta utworzony rekord.
Identyfikatory te s przypisywane przez klas LogRecord i nie maj adnego
zwizku z identyfikatorami innych wtkw.
java.util.logging.Filter 1.4
java.util.logging.Formatter 1.4
lub
Logger.global.info("x=" + x);
610 Java. Podstawy
Utwrz kilka obiektw, wywoaj wszystkie metody i sprawd, czy kada z nich
zwraca prawidow warto. Aby uruchomi testy, naley kady z plikw wykona
w maszynie wirtualnej osobno. Przy uruchamianiu apletu adna z tych metod nie
jest w ogle wywoywana. Przy uruchamianiu aplikacji maszyna wirtualna wywouje
tylko metod main klasy uruchomieniowej.
3. Osoby, ktrym spodobaa si poprzednia wskazwka, powinny zainteresowa
si narzdziem JUnit ze strony http://junit.org. Jest to bardzo popularny framework
testowy, ktry uatwia organizacj zestaww przypadkw testowych. Testy powinno
si przeprowadza po wprowadzeniu jakich zmian w klasie, a po znalezieniu bdu
naley doda nowe testy.
4. Rejestrujcy obiekt poredni (ang. logging proxy) to obiekt podklasy, ktry
przechwytuje wszystkie wywoania metod, rejestruje je, a nastpnie wywouje
nadklas. Jeli na przykad mamy problem z metod nextDouble z klasy Random,
moemy utworzy obiekt poredni bdcy egzemplarzem podklasy anonimowej:
Random generator = new
Random()
{
public double nextDouble()
{
double result = super.nextDouble();
Logger.getGlobal().info("nextDouble: " + result);
return result;
}
};
Aby wygenerowa lad stosu, nie trzeba nawet przechwytywa wyjtku. Wystarczy
ponisza instrukcja w dowolnym miejscu programu:
Thread.dumpStack();
Aby zapisa zarwno strumie System.err, jak i System.out w jednym pliku, naley
uy nastpujcego polecenia:
java MyProgram >& errors.txt
11. Maszyna wirtualna Javy moe te monitorowa aplikacje i zarzdza nimi. Polega
to na instalacji agentw w maszynie wirtualnej, ktre ledz zuycie pamici,
wykorzystanie wtkw, adowanie klas itd. Funkcja ta jest szczeglnie przydatna
w duych i dugo dziaajcych aplikacjach, takich jak serwery. Demonstracj tych
moliwoci jest dostpne w JDK narzdzie graficzne o nazwie jconsole, ktre
wywietla statystyki dotyczce dziaania maszyny wirtualnej (rysunek 11.3). Naley
znale identyfikator procesu, ktry w systemie operacyjnym obsuguje maszyn
wirtualn. W systemach Unix/Linux naley w tym celu uy narzdzia ps, w systemie
Windows trzeba skorzysta z menedera zada1. Nastpnie uruchamiamy program
jconsole:
jconsole IDprocesu
1
W systemie Windows po uruchomieniu okna Meneder zada za pomoc klawiszy Ctrl+Alt+Delete naley
przej do karty Procesy i z menu Widok wybra opcj Wybierz kolumny oraz zaznaczy pole PID
(identyfikator procesu) przyp. tum.
614 Java. Podstawy
12. Za pomoc narzdzia jmap mona sprawdzi, jakie obiekty znajduj si na stercie.
Su do tego nastpujce polecenia:
jmap -dump:format=b,file=nazwaPlikuZrzutu IDprocesu
jhat NazwaPlikuZrzutu
Kod ten naley umieci na kocu konstruktora ramki. W czasie dziaania programu
panel gwny bdzie si zapenia w zwolnionym tempie. Bardziej precyzyjne
debugowanie mona wykona, wywoujc metod setDebugGraphicsOptions
na rzecz jednego komponentu. Mona ustawi czas, liczb i kolor byskw
szczegy na ten temat mona znale w dokumentacji internetowej metody
DebugGraphics.
package eventTracer;
import java.awt.*;
import java.beans.*;
import java.lang.reflect.*;
/**
* @version 1.31 2004-05-10
* @author Cay Horstmann
*/
public class EventTracer
{
616 Java. Podstawy
public EventTracer()
{
// Handler wszystkich obiektw porednich zdarze
handler = new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object[] args)
{
System.out.println(method + ":" + args[0]);
return null;
}
};
}
/**
* Dodawanie obiektw ledzcych zdarzenia dla wszystkich zdarze, ktrych ten komponent i jego
* potomkowie mog nasuchiwa
* @param c a komponent
*/
public void add(Component c)
{
try
{
// Pobranie wszystkich zdarze, ktrych ten komponent moe nasuchiwa
BeanInfo info = Introspector.getBeanInfo(c.getClass());
if (c instanceof Container)
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 617
{
// Pobranie wszystkich potomkw i rekursywne wywoanie metody add
for (Component comp : ((Container) c).getComponents())
add(comp);
}
}
/**
* Dodanie suchacza do danego zbioru zdarze
* @param c a komponent
* @param eventSet deskryptor interfejsu nasuchujcego
*/
public void addListener(Component c, EventSetDescriptor eventSet)
{
// Utworzenie obiektu poredniego dla tego typu suchaczy i przekazanie wszystkich wywoa
// do handlera
Aby utworzy robota, najpierw trzeba utworzy obiekt klasy GraphicsDevice. Aby uzyska
domylne urzdzenie ekranowe, mona uy poniszych instrukcji:
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screen = environment.getDefaultScreenDevice();
Aby wysa zdarzenie nacinicia klawisza, naley kaza robotowi, aby zasymulowa naci-
nicie i zwolnienie klawisza:
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
618 Java. Podstawy
Pomidzy kolejnymi instrukcjami robota powinna by krtka przerwa, aby aplikacja moga
nady. Do tego celu mona uy metody delay, ktrej parametrem jest liczba milisekund
opnienia. Na przykad:
robot.delay(1000); // opnienie o 1000 milisekund
Program przedstawiony na listingu 11.4 demonstruje sposb wykorzystania robota. Ten robot
testuje program ButtonTest z rozdziau 8. Najpierw nacinicie spacji aktywuje pierwszy
przycisk. Nastpnie robot odczekuje dwie sekundy, aby mona byo zobaczy, co si stao.
Nastpnie symuluje klawisz Tab i kolejne nacinicie spacji powodujce nacinicie kolej-
nego przycisku. Na zakoczenie symulujemy kliknicie przyciskiem myszy trzeciego przy-
cisku (moliwe, e bdzie trzeba dostosowa wsprzdne x i y w programie, aby przycisk by
rzeczywicie wciskany). Program koczy si zrobieniem zrzutu ekranu i wywietleniem go
w dodatkowej ramce (rysunek 11.5).
Robot powinien dziaa w osobnym wtku, jak w przykadowym kodzie. Wicej infor
macji o wtkach znajduje si w rozdziale 14.
Przykad ten pokazuje jasno, e klasa Robot sama w sobie nie jest wygodnym sposobem testo-
wania interfejsu uytkownika. Moe natomiast suy jako podstawa narzdzia testujcego.
Profesjonalne narzdzie powinno przechwytywa, zapisywa i powtarza scenariusze zacho-
wania uytkownika oraz znajdowa lokalizacje komponentw na ekranie, dziki czemu miej-
sca klikni mysz nie s wybierane na drodze prb i bdw.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
/**
* @version 1.04 2012-05-17
* @author Cay Horstmann
*/
public class RobotTest
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 619
Rysunek 11.5.
Zrzut ekranu
zrobiony przez
robota AWT
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
// Ramka z panelem zawierajcym przycisk
GraphicsEnvironment environment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screen = environment.getDefaultScreenDevice();
try
{
final Robot robot = new Robot(screen);
robot.waitForIdle();
new Thread()
{
public void run()
{
runTest(robot);
};
}.start();
}
catch (AWTException e)
{
e.printStackTrace();
}
}
/**
* Uruchamia procedur testow
* @param robot robot zwizany z ekranem
*/
620 Java. Podstawy
/**
* Ramka zawierajca wywietlany obraz
*/
class ImageFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 450;
private static final int DEFAULT_HEIGHT = 350;
/**
* @param image obraz do wywietlenia
*/
public ImageFrame(Image image)
{
setTitle("Zrzut ekranu");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
java.awt.GraphicsEnvironment 1.2
GraphicsDevice getDefaultScreenDevice()
Zwraca domylne urzdzenie z ekranem. Naley pamita, e w komputerach,
do ktrych podczono kilka monitorw, na kady ekran przypada jedno urzdzenie
graficzne do utworzenie tablicy wszystkich urzdze ekranowych mona uy
metody getScreenDevices.
java.awt.Robot 1.3
Robot(GraphicsDevice device)
Tworzy robota, ktry moe wsppracowa z danym urzdzeniem.
void keyPress(int key)
void keyRelease(int key)
Symuluje nacinicie lub zwolnienie klawisza.
Parametr: key Kod klawisza. Wicej informacji na temat kodw
klawiszy mona znale w opisie klasy KeyStroke.
package debugger;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* @version 1.22 2007-05-14
* @author Cay Horstmann
*/
public class BuggyButtonTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new BuggyButtonFrame();
frame.setTitle("BuggyButtonTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
public BuggyButtonFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
W tak krtkim programie znalezienie bdu moe by moliwe dziki samemu przeczytaniu
kodu rdowego. Zamy jednak, e analiza kodu rdowego nie jest praktycznym rozwi-
zaniem. Nauczysz si teraz lokalizowa bdy za pomoc debugera dostpnego w rodowisku
Eclipse.
Punkt wstrzymania zostanie osignity w chwili, gdy Java dojdzie do metody actionPerformed.
Aby tak si stao, kliknij przycisk ty. Debuger zatrzymuje si na pocztku metody action
Performed rysunek 11.6.
Do uruchamiania programu krok po kroku su dwa polecenia. Opcja Step Into wchodzi
do kadego wywoania metody. Opcja Step Over przechodzi do nastpnego wiersza bez
624 Java. Podstawy
wchodzenia do adnych dalszych metod. W Eclipse opcje te to Run/Step Into i Run/Step Over,
a odpowiadajce im skrty klawiaturowe to F5 i F6. Zastosuj dwukrotnie opcj Step Over
i sprawd, gdzie jestemy.
Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie 625
Wiadomo ju, gdzie tkwi bd. Zmienna arg miaa warto ty, z wielk liter na pocztku,
a program do porwnywania uywa sowa ty pisanego ma liter:
if (arg.equals("ty"))
Eclipse posiada o wiele wicej opcji debugera, ale te podstawowe, ktre zostay omwione,
daj ju cakiem due pole do popisu. Inne debugery, na przykad NetBeans, udostpniaj
bardzo podobne zestawy opcji.
W tym rozdziale:
Dlaczego programowanie oglne
Definicja prostej klasy oglnej
Metody oglne
Ograniczenia zmiennych typowych
Kod generyczny a maszyna wirtualna
Ograniczenia i braki
Zasady dziedziczenia dla typw oglnych
Typy wieloznaczne
Refleksja a typy oglne
Typy oglne (take generyczne lub parametryzowane) stanowi najwiksz zmian w jzyku
Java od chwili jego zaistnienia. Mimo e funkcjonalno t wprowadzono dopiero w Java
SE 5.0, o jej dodanie postulowano w jednym z pierwszych dokumentw JSR (ang. Java
Specification Requests), dokadniej mwic JSR 14 z 1999 roku. Prace nad specyfikacj i tes-
towanie implementacji zajy ekspertom okoo piciu lat.
Metoda ta ma dwie wady. Po pierwsze, kada pobierana warto musi zosta poddana rzu-
towaniu:
ArrayList files = new ArrayList();
. . .
String filename = (String) names.get(0);
Programowanie oglne oferuje lepsze rozwizanie: parametry typowe (ang. type parameters).
Obecnie klasa ArrayList ma parametr typowy, ktry okrela typ jej elementw:
ArrayList<String> files = new ArrayList<String>();
Dziki temu kod jest mniej zawiy. Od razu wida, e dana lista tablicowa przechowuje
obiekty typu String.
Poytki pynce z tych informacji mog zosta wykorzystane take przez kompilator. Wywo-
anie metody get nie pociga za sob koniecznoci wykonywania konwersji, poniewa kom-
pilator wie, e zostanie zwrcony typ String, a nie Object:
String filename = files.get(0);
Ponadto kompilator wie, e metoda add klasy ArrayList<String> ma parametr typu String,
ktry jest znacznie bardziej bezpieczny ni Object. Teraz kompilator moe sprawdzi, czy
nie s wstawiane obiekty nieprawidowego typu. Na przykad ponisza instrukcja spowoduje
bd kompilacji:
files.add(new File(". . .")); // Do ArrayList<String> mona wstawia tylko obiekty typu String.
Natomiast implementacja klasy oglnej jest trudniejsza. Programici uywajcy takiej kon-
strukcji w miejsce parametru typu mog wstawia najrniejsze klasy. Oczekuj, e nie bdzie
adnych uciliwych ogranicze ani niejasnych komunikatw o bdach. W zwizku z tym
twrca klasy oglnej musi przewidzie wszystkie potencjalne sposoby uycia jego produktu.
Jak due trudnoci moe to sprawi? Projektanci biblioteki standardowej musieli poradzi
sobie z nastpujcym problemem. Klasa ArrayList zawiera metod addAll, ktra dodaje do
listy wszystkie elementy znajdujce si w innej kolekcji. Programista moe chcie wstawi
wszystkie elementy z kolekcji ArrayList<Manager> do kolekcji ArrayList<Employee>. Oczy-
wicie operacja w przeciwn stron powinna by zabroniona. Jak sprawi, by jeden rodzaj
operacji by moliwy, a drugi nie? Projektanci Javy wykazali si pomysowoci i rozwizali
ten problem, opracowujc now koncepcj typu wieloznacznego (ang. wildcard type). Jest to
twr abstrakcyjny, ale umoliwia twrcy biblioteki tworzenie maksymalnie oglnych metod.
Twrcy aplikacji raczej nie musz pisa duo kodu generycznego. Wikszo trudnej pracy
zostaa wykonana przez programistw z firmy Sun, ktrzy dostarczyli parametrw typowych
dla wszystkich klas kolekcyjnych. Oglna regua jest taka, e na uyciu parametrw typowych
skorzysta mog tylko programy zawierajce duo konwersji z bardzo oglnych typw
(jak Object czy interfejs Comparable).
Rozdzia ten zawiera wszystkie informacje potrzebne do pisania wasnych procedur oglnych.
Przewidujemy jednak, e wikszo Czytelnikw wykorzysta t wiedz przede wszystkim
do rozwizywania problemw oraz zaspokojenia wasnej ciekawoci na temat zasad dziaania
parametryzowanych klas kolekcyjnych.
Klasa ta ma zmienn typu T umieszczon w nawiasach ostrych < > za nazw klasy. Klasa
oglna (parametryzowana) moe mie wicej zmiennych typowych. Na przykad klasa Pair
mogaby mie rne typy dla pierwszego i drugiego pola:
public class Pair<T, U> { . . . }
Zmienne typowe uywane w definicji klasy okrelaj typy zwrotne metod oraz typy pl i zmien-
nych lokalnych. Na przykad:
private T first; // uywa zmiennej typowej
Aby utworzy egzemplarz typu oglnego, naley zastpi zmienne typowe rzeczywistymi
typami, na przykad:
Pair<String>
i metodami:
String getFirst()
String getSecond()
void setFirst(String)
void setSecond(String)
Program z listingu 12.1 przedstawia klas Pair w dziaaniu. Statyczna metoda minmax prze-
mierza tablic i jednoczenie znajduje najmniejsz i najwiksz warto. Znalezione wyniki
zwraca w postaci obiektu klasy Pair. Przypomnijmy, e metoda compareTo porwnuje dwa
acuchy i zwraca warto 0, jeli s one identyczne, liczb cakowit ujemn, jeli pierwszy
acuch wystpuje przed drugim w kolejnoci sownikowej, oraz liczb dodatni cakowit
w pozostaych przypadkach.
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest1
{
public static void main(String[] args)
{
String[] words = { "Ala", "ma", "kota", "i", "psa" };
Pair<String> mm = ArrayAlg.minmax(words);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
class ArrayAlg
{
/**
* Pobiera najmniejsz i najwiksz warto z tablicy acuchw
* @param a tablica acuchw
* @return zoona z najmniejszej i najwikszej wartoci lub null, jeli tablica a jest null bd pusta
*/
public static Pair<String> minmax(String[] a)
{
if (a == null || a.length == 0) return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++)
632 Java. Podstawy
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
}
Ta metoda jest zdefiniowana w zwykej, nieoglnej klasie. Jednak nawiasy ostre i zmienna
typowa jednoznacznie wskazuj, e jest to metoda oglna. Naley zauway, e zmienne typu
znajduj si za modyfikatorami (w tym przypadku public static), a przed typem zwrotnym.
Metody oglne (parametryzowane) mona definiowa zarwno w zwykych klasach, jak i kla-
sach oglnych.
Rzeczywisty typ w wywoaniu metody oglnej naley poda w nawiasach ostrych przed
nazw metody podczas wywoania:
String middle = ArrayAlg.<String>getMiddle("Jan", "S.", "Kowalski");
W tym przypadku (i w wikszoci innych) parametr typu <String> mona opuci. Kompi-
lator i tak ma wystarczajco danych, aby wywoa odpowiedni metod. Porwnuje typ
zmiennej names (czyli String[]) z typem generycznym T[] i dedukuje, e T musi by typu
String. Oznacza to, e mona uy poniszego prostszego wywoania:
String middle = ArrayAlg.getMiddle("Jan", "S.", "Kowalski");
Aby sprawdzi, jaki typ kompilator zastosuje dla wywoania metody parametryzo-
wanej, mona zastosowa sztuczk opracowan przez Petera von der Ah. Naley
celowo popeni bd i przestudiowa zgoszony komunikat o bdzie. Wemy na przykad
instrukcj ArrayAlg.getMiddle("Witaj, ", 0, null). Przypiszemy jej wynik do kompo-
nentu JButton, co nie moe si uda. Zostanie wywietlony komunikat:
found:
java.lang.Object&java.io.Serializable&java.lang.Comparable<? extends
java.lang.Object&java.io.Serializable&java.lang.Comparable<?>>
Krtko mwic, wynik ten mona przypisa do typu Object, Serializable lub Comparable.
Jest jednak jeden problem. Zajrzyjmy do kodu metody min. Zmienna smallest jest typu T,
co oznacza, e moe by obiektem dowolnej klasy. Skd wiadomo, e klasa, do ktrej
naley T, ma metod compareTo?
634 Java. Podstawy
Od tej pory ogln metod min mona wywoywa wycznie z parametrami tablic klas im-
plementujcych interfejs Comparable, na przykad String, Date itd. Wywoanie metody min
z parametrem tablicy typu Rectangle spowoduje bd kompilacji, poniewa klasa Rectangle
nie implementuje interfejsu Comparable.
Zagadk moe by, czemu w tej sytuacji uywa si sowa kluczowego extends zamiast
implements przecie Comparable to interfejs. Notacja:
<T extends typ graniczny>
oznacza, e T musi by podtypem typu granicznego. Zarwno T, jak i typ graniczny mog by
klas lub interfejsem. Projektanci Javy wybrali sowo extends, poniewa jest ono bliskie kon-
cepcji podtypw, a poza tym nie chcieli wprowadza nowego sowa kluczowego, na przy-
kad sub.
Zmienna typowa lub typ wieloznaczny mog mie wiele ogranicze. Na przykad:
T extends Comparable & Serializable
Znakiem rozdzielajcym poszczeglne typy graniczne jest ampersand (&), poniewa przeci-
nek oddziela zmienne typowe.
Tak samo jak w przypadku dziedziczenia, wrd nadtypw moe istnie dowolna liczba inter-
fejsw, ale tylko jedna klasa. Jeli wrd typw granicznych znajduje si klasa, musi by ona
umieszczona na pocztku listy.
import java.util.*;
Rozdzia 12. Programowanie oglne 635
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest2
{
public static void main(String[] args)
{
GregorianCalendar[] birthdays =
{
new GregorianCalendar(1906, Calendar.DECEMBER, 9), // G. Hopper
new GregorianCalendar(1815, Calendar.DECEMBER, 10), // A. Lovelace
new GregorianCalendar(1903, Calendar.DECEMBER, 3), // J. von Neumann
new GregorianCalendar(1910, Calendar.JUNE, 22), // K. Zuse
};
Pair<GregorianCalendar> mm = ArrayAlg.minmax(birthdays);
System.out.println("min = " + mm.getFirst().getTime());
System.out.println("max = " + mm.getSecond().getTime());
}
}
class ArrayAlg
{
/**
Pobiera najmniejsz i najwiksz warto z tablicy obiektw typu T.
@param a tablica obiektw typu T
@return para zoona z najmniejszej i najwikszej wartoci lub warto null, jeli tablica a jest
null bd pusta
*/
public static <T extends Comparable> Pair<T> minmax(T[] a)
{
if (a == null || a.length == 0) return null;
T min = a[0];
T max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
}
Kademu typowi oglnemu odpowiada typ surowy o takiej samej nazwie, ale z usunitymi
parametrami typu. W miejsce parametrw typowych wstawiane s typy graniczne (lub typ
Object w przypadku zmiennych bez ogranicze).
Poniewa T jest nieograniczon zmienn typow, w jej miejsce zosta wstawiony typ Object.
W wyniku powstaa zwyka klasa, ktra mogaby zosta utworzona przed wprowadzeniem
typw oglnych do Javy.
Program moe zawiera rnego rodzaju typy Pair, na przykad Pair<String> i Pair
<GregorianCalendar>, ale usunicie parametrw zawsze zamienia je w surowy typ Pair.
Pod tym wzgldem typy oglne Javy znacznie rni si od szablonw w C++. Drugi
z jzykw dla kadej instancji szablonu tworzy inny typ. Nazywa si to puchniciem
kodu szablonw (ang. template code bloat). W jzyku Java problem ten nie wystpuje.
Zmienne typw w typie surowym s zastpowane pierwsz klas graniczn lub typem Object,
jeli nie podano adnych ogranicze. Na przykad zmienna typowa w klasie Pair<T> nie ma
ogranicze, dlatego w jej typie surowym parametr T zosta zastpiony przez typ Object. Zade-
klarujmy teraz nieco inny typ:
public class Interval<T extends Comparable & Serializable> implements Serializable
{
private T lower;
private T upper;
. . .
jako cae rodziny metod. Ale po wymazaniu typw zostaje tylko jedna metoda:
public static Comparable min(Comparable[] a)
638 Java. Podstawy
Naley zauway, e parametr T zosta usunity, a pozostawiono tylko jego typ graniczny
Comparable.
Obiekt klasy DateInterval zawiera par obiektw klasy Date. Przesonimy metody, aby
mie pewno, e druga warto nie bdzie nigdy mniejsza od pierwszej. Po wymazaniu
typw powysza klasa przyjmie nastpujc posta:
class DateInterval extends Pair // po wymazaniu typw
{
public void setSecond(Date second) { . . . }
. . .
}
Jest to z pewnoci inna metoda, o czym wiadczy inny typ parametru Object zamiast
Date, a nie powinna by inna. Przyjrzyjmy si poniszym instrukcjom:
DateInterval interval = new DateInterval(. . .);
Pair<Date> pair = interval; // OK przypisanie do nadklasy
pair.setSecond(aDate);
Zmienna pair jest typu Pair<Date>, ktry ma tylko jedn metod setSecond(Object). Maszyna
wirtualna wywouje t metod na rzecz obiektu wskazywanego przez zmienn pair. Obiekt
ten jest typu DateInterval. W zwizku z tym wywoywana jest metoda DateInterval.set
Second(Object), ktra jest zsyntetyzowan metod pomostow. Wywouje ona metod
DateInterval.setSecond(Date), czyli t, ktr chcemy.
Rozdzia 12. Programowanie oglne 639
W Javie nie mona pisa takiego kodu, poniewa dwie metody nie mog mie takich samych
typw parametrw w tym przypadku nie maj ich w ogle. Natomiast w maszynie wirtu-
alnej metod identyfikuj typy parametrw i typ zwrotny. Dlatego kompilator moe utwo-
rzy kod bajtowy dwch metod rnicych si tylko typem zwrotnym, ktry zostanie prawi-
dowo obsuony przez maszyn wirtualn.
W tym przypadku Dictionary jest surowym typem, poniewa komponent JSlider zosta
zaimplementowany przed typami oglnymi. Jednak do wstawiania wartoci do sownika
naley uywa typu oglnego:
Dictionary<Integer, Component> labelTable = new Hashtable<>();
labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
labelTable.put(20, new JLabel(new ImageIcon("ten.gif")));
. . .
Kompilator nie ma pewnoci, co metoda setLabelTable moe zrobi z obiektem typu Dic
tionary. Moe na przykad zamieni wszystkie klucze na acuchy, co zamaoby gwa-
rancj, e klucze s typu Integer. To z kolei mogoby doprowadzi do wyjtkw rzutowania
w kolejnych operacjach.
Z ostrzeeniem tym nie mona wiele zrobi. Co najwyej moemy sprbowa dowiedzie
si, co JSlider najprawdopodobniej moe zrobi z danym obiektem Dictionary. W tym przy-
padku jest oczywiste, e JSlider tylko odczytuje dane, a wic ostrzeenie mona zignorowa.
Teraz wyobramy sobie odwrotn sytuacj, w ktrej otrzymujemy obiekt surowego typu ze
starej klasy. Moemy przypisa go do zmiennej typu oglnego, ale wtedy kompilator zgosi
ostrzeenie. Na przykad:
Dictionary<Integer, Components> labelTable = slider.getLabelTable(); // ostrzeenie
Mimo e zasada ta jest denerwujca, jest ona zgodna z zasad odrbnoci typw prostych
w jzyku Java. Nie jest to wielka strata typw prostych jest tylko osiem i zawsze mona je
obsuy za pomoc osobnych klas i metod, jeli typy opakowujce nie mog by uyte.
sprawdza tylko, czy a jest obiektem jakiejkolwiek klasy Pair. To samo dotyczy poniszego
testu:
if (a instanceof Pair<T>) // BD
i rzutowania:
Pair<String> p = (Pair<String>) a; // OSTRZEENIE mona tylko sprawdzi, czy a jest typu Pair
Kade uycie operatora instanceof lub zastosowanie rzutowania zwizane z typami ogl-
nymi bdzie skutkowao zgoszeniem przez kompilator ostrzeenia, majcego na celu powia-
domienie programisty o ryzykownoci operacji.
Z tego samego powodu metoda getClass zawsze zwraca typ surowy, na przykad:
Pair<String> stringPair = . . .;
Pair<Employee> employeePair = . . .;
if (stringPair.getClass() == employeePair.getClass()) // s rwne
Wynikiem porwnania jest warto true, poniewa oba wywoania metody getClass zwracaj
Pair.class.
642 Java. Podstawy
Dlaczego powyszy kod jest zy? Po wymazaniu parametrw zmienna table jest typu Pair[].
Mona j przekonwertowa na typ Object:
Object[] objarray = table;
Wymazywanie typw powoduje jednak, e mechanizm ten nie dziaa w przypadku typw
oglnych. Ponisza instrukcja przeszaby z powodzeniem kontrol tablicy, ale nadal powodo-
waaby bd typu. Dlatego zabroniono tworzenia tablic typw oglnych.
objarray[0] = new Pair<Employee>();
Naley podkreli, e zabronione jest tylko tworzenie tych tablic. Mona zadeklarowa zmienn
typu Pair<String>, ale nie mona jej zainicjowa przy uyciu instrukcji new Pair<String>[10].
Wynik nie jest bezpieczny. Jeli zapiszesz Pair<Employee> w elemencie table[0], a na-
stpnie wywoasz metod klasy String na table[0].getFirst(), otrzymasz wyjtek
ClassCastException.
Aby utworzy kolekcj obiektw typw oglnych, naley uy klasy ArrayList: zapis
ArrayList<Pair<String>> jest bezpieczny i efektywny.
Aby wywoa t metod, maszyna wirtualna Javy musi utworzy tablic Pair<String>, co
jest wbrew zasadom. Reguy zostay jednak rozlunione dla takich przypadkw i zamiast bdu
zostanie zgoszone tylko ostrzeenie.
Mona sobie z tym poradzi, konstruujc obiekty oglne poprzez refleksj i wywoujc
metod Class.newInstance.
644 Java. Podstawy
Wyraenie T.class jest ze. W zamian trzeba tak zaprojektowa API, aby otrzyma obiekt klasy
Class, na przykad:
public static <T> Pair<T> makePair(Class<T> cl)
{
try { return new Pair<T>(cl.newInstance(), cl.newInstance()) }
catch (Exception ex) { return null; }
}
Naley zauway, e klasa Class sama jest generyczna. Na przykad String.class jest egzem-
plarzem (i to jedynym) klasy Class<String>. Dlatego metoda makePair moe wywnioskowa
typ pary, ktr tworzy.
Jeli tablica jest uywana wycznie jako prywatne pole egzemplarza klasy, mona j zade-
klarowa jako Object[] i przy pobieraniu elementw stosowa rzutowanie. Na przykad klas
ArrayList mona zaimplementowa nastpujco:
public class ArrayList<E>
{
private Object[] elements;
. . .
@SuppressWarnings("unchecked") public E get(int n) { return (E) elements[n]; }
public void set(int n, E e) { elements[n] = e; } // nie jest potrzebne rzutowanie
}
W tym przypadku rzutowanie na typ E[] jest ze, ale przez wymazywanie typw jest to nie
do wykrycia.
Technika ta nie zadziaa w naszej metodzie minmax, poniewa zwracamy tablic T[] i podanie
jej zego typu spowoduje bd wykonawczy. Zamy, e mamy nastpujc implementacj:
public static <T extends Comparable> T[] minmax(T[] a)
{
Object[] mm = new Object[2];
Rozdzia 12. Programowanie oglne 645
. . .;
return (T[]) mm; // kompilator zgosi ostrzeenie
}
Wyjtek ClassCastException jest generowany, kiedy referencja do typu Object[] jest rzuto-
wana na typ Comparable[] podczas zwracania wartoci przez metod.
Metoda toArray z klasy ArrayList nie ma tyle szczcia. Musi utworzy tablic T[], ale nie
zna typu elementw. Dlatego istniej jej dwa warianty:
Object[] toArray()
T[] toArray(T[] result)
Druga wersja pobiera parametr w postaci tablicy. Jeli tablica ta jest wystarczajco dua,
zostanie uyta. W przeciwnym razie tworzona jest nowa tablica o odpowiednim rozmiarze,
a typem jej komponentw jest result.
Gdyby to byo moliwe, mona by byo zadeklarowa klas Singleton<Random> dla liczb
losowych i klas Singleton<JFileChooser> do tworzenia okien wyboru pliku. Jest to jednak
niemoliwe, poniewa dziki wymazywaniu typw istnieje tylko jedna klasa Singleton
i tylko jedno pole singleInstance. Dlatego pola i metody statyczne ze zmiennymi typowymi
s zabronione.
646 Java. Podstawy
Zmiennych typowych nie mona uywa w klauzulach catch. Dlatego ponisza metoda
spowoduje bd kompilacji:
public static <T extends Throwable> void doWork(Class<T> t)
{
try
{
procedury
}
catch (T e) // bd nie mona przechwyci zmiennej typowej
{
Logger.global.info(...)
}
}
Ponisza konstrukcja zamienia wszystkie wyjtki w takie, ktre dla kompilatora s niekon-
trolowane:
try
{
instrukcje
}
catch (Throwable t)
{
Block.<RuntimeException>throwAs(t);
}
@SuppressWarnings("unchecked")
public static <T extends Throwable> void throwAs(Throwable e) throws T
{
throw (T) e;
}
}
}
.toThread().start();
}
}
Tutaj jednak nie stosujemy opakowywania. Po prostu zgaszamy wyjtek, tak by kompilator
uzna, e nie jest to wyjtek kontrolowany.
Tym razem jednak nasza intuicja zawodzi. Metoda boolean equals(T) po wymazaniu typw
ma posta boolean equals(Object) i wchodzi w konflikt z metod Object.equals.
Aby sobie z tym poradzi, naley oczywicie zmieni nazw metody sprawiajcej problem.
W specyfikacji typw oglnych opisano jeszcze inn regu: Aby translacja poprzez wyma-
zywanie typw bya moliwa, klasa lub zmienna typowa nie moe by w jednym czasie pod-
typem dwch interfejsw bdcych rnymi wersjami parametrycznymi tego samego inter-
fejsu. Na przykad poniszy kod jest zy:
class Calendar implements Comparable<Calendar> { . . . }
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>
{ . . . } // bd
Zwizek tego ograniczenia z wymazywaniem typw nie jest oczywisty. Niegeneryczna wersja
jest dozwolona:
class Calendar implements Comparable { . . . }
class GregorianCalendar extends Calendar implements Comparable { . . . }
Nie mona mie dwch takich metod dla rnych typw X.<<F1-k>>
Metoda minmax zwraca typ Pair<Manager>, a nie Pair<Employee>, nie mona jej te przypisa
jednego z tych typw do drugiego.
Oglna zasada jest taka, e pomidzy typami Pair<S> i Pair<T> nie ma adnego zwizku,
bez wzgldu na to, co czy S i T (zobacz rysunek 12.1).
Ostatnia instrukcja jest bez wtpienia poprawna, ale zmienne employeeBuddies i managerBuddies
odwouj si do tego samego obiektu. W ten sposb w jednej parze umiecilimy osob
z kierownictwa i zwykego pracownika. Operacja ta nie powinna by moliwa przy uyciu typu
Pair<Manager>.
Tablice jednak maj specjaln ochron. Prba zapisu zwykego pracownika w employ-
eeBuddies[] zakoczy si wygenerowaniem przez maszyn wirtualn wyjtku Array-
StoreException.
Czy mona dokona konwersji na typ surowy, a nastpnie spowodowa bd typu? Niestety
tak. Spjrzmy na poniszy przykad:
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair rawBuddies = managerBuddies; // OK
rawBuddies.setFirst(new File(". . .")); // tylko ostrzeenie kompilatora
To wydaje si straszne, ale naley pamita, e nie jest gorzej ni w starszych wersjach
Javy. Bezpieczestwo maszyny wirtualnej nie wchodzi w gr. Jeli metoda getFirst pobie-
rze obcy obiekt i przypisze go do zmiennej typu Manager, zostanie wygenerowany wyjtek
ClassCastException.
Ostatecznie klasy oglne mog rozszerza lub implementowa inne klasy oglne. Pod tym
wzgldem nie rni si niczym od zwykych klas. Na przykad klasa ArrayList<T> imple-
mentuje interfejs List<T>. Oznacza to, e typ ArrayList<Manager> mona przekonwertowa na
typ List<Manager>. Jak ju jednak wiemy, ArrayList<Manager> to nie ArrayList<Employee>
ani List<Employee>. Relacje te przedstawia rysunek 12.2.
Jak wiadomo z poprzedniego podrozdziau, do metody tej nie mona przekaza typu Pair
<Manager>, co stanowi spore ograniczenie. Jest jednak proste rozwizanie w postaci typw
wieloznacznych:
public static void printBuddies(Pair<? extends Employee> p)
Typ Pair<Manager> jest podtypem Pair<? extends Employee> (zobacz rysunek 12.3).
652 Java. Podstawy
Rysunek 12.3.
Relacje klasowe
przy zastosowaniu
typw
wieloznacznych
Czy za pomoc typu wieloznacznego mona uszkodzi typ Pair<Manager> poprzez referencj
typu Pair<? extends Employee>?
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK
wildcardBuddies.setFirst(lowlyEmployee); // bd kompilacji
To uniemoliwia wywoanie metody setFirst. Kompilator wie tylko, e potrzebny jest jaki
podtyp Employee, ale nie wie jaki. Nie zgadza si na przekazanie adnego konkretnego typu,
poniewa doker (?) moe do niego nie pasowa.
Ten problem nie istnieje w przypadku metody getFirst. Warto zwrotn tej metody mona
z powodzeniem przypisa do referencji typu Employee.
Ten typ wieloznaczny jest ograniczony do wszystkich nadtypw typu Manager (projektanci
mieli duo szczcia, e istniejce sowo kluczowe super tak precyzyjnie okrela ten rodzaj
relacji).
Rozdzia 12. Programowanie oglne 653
Kompilator nie wie, jaki dokadnie typ ma metoda setFirst, ale moe j wywoa na rzecz
kadego obiektu typu Manager, Employee i Object, jednak nie na rzecz obiektw nalecych
do jej podtypw, jak na przykad Executive. Dlatego wywoujcy metod getFirst nie wie
na pewno, jakiego typu obiekt ona zwrci. W zwizku z tym warto t mona przypisa tylko
do typu Object.
Oto typowy przykad takiej sytuacji. Mamy tablic obiektw Manager. Chcemy, aby kierow-
nicy z najwiksz i najmniejsz premi znaleli si w jednym obiekcie Pair. Jakiego rodzaju
ma to by para? Moe to by Pair<Employee> albo Pair<Object> (zobacz rysunek 12.4).
Ponisza metoda przyjmie kady odpowiedni obiekt Pair:
public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
{
if (a == null || a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}
Oto jeszcze jeden przykad zastosowania ogranicze nadtypw. Interfejs Comparable jest typem
generycznym. Jego deklaracja jest nastpujca:
public interface Comparable<T>
{
public int compareTo(T other);
}
Tutaj zmienna typowa okrela typ parametru other. Na przykad klasa String implementuje
interfejs Comparable<String>, a deklaracja jej metody compareTo jest nastpujca:
public int compareTo(String other)
Jest to zgrabne rozwizanie, poniewa parametr jawny ma odpowiedni typ. Przed Java SE 5.0
parametr other by typu Object i implementacja metody musiaa zawiera operacj konwersji
typw.
654 Java. Podstawy
Rysunek 12.4.
Typ wieloznaczny
z ograniczeniem
nadtypw
Poniewa interfejs Comparable jest typem oglnym, metod min z klasy ArrayAlg mona napi-
sa nieco lepiej. Jej deklaracja moe wyglda nastpujco:
public static <T extends Comparable<T>> T min(T[] a)
Taki zapis wydaje si bardziej staranny ni T extends Comparable, dziki czemu moe
nadawa si dla wielu klas. Jeli na przykad chcemy znale najmniejsz warto w tablicy
acuchw, parametr T bdzie typu String, a String jest podtypem Comparable<String>.
Pojawia si jednak problem przy przetwarzaniu tablicy obiektw typu GregorianCalendar.
Tak si skada, e klasa GregorianCalendar jest podklas klasy Calendar, ktra z kolei imple-
mentuje interfejs Comparable<Calendar>. Dlatego klasa GregorianCalendar implementuje inter-
fejs Comparable<Calendar>, a nie Comparable<GregorianCalendar>.
Moe ona przyjmowa obiekty typu T jeli T jest na przykad GregorianCalendar, albo nad-
typu T. Bez wzgldu na wszystko przekazanie obiektu typu T do tej metody jest bezpieczne.
Deklaracje typu <T extends Comparable<? super T>> dla niedowiadczonego programisty
wygldaj przytaczajco. Jak na ironi, celem tej deklaracji jest pomoc programistom poprzez
usunicie niepotrzebnych ogranicze parametrw wywoania. Ci, ktrzy nie s zaintereso-
wani typami generycznymi, szybko ucz si nie zwraca szczeglnej uwagi na te deklaracje
i przyjmowa, e programici biblioteki si nie pomylili. Programici bibliotek natomiast
Rozdzia 12. Programowanie oglne 655
musz si przyzwyczai do typw wieloznacznych, jeli nie chc, aby uytkownicy przekli-
nali ich, kiedy zostan zmuszeni do wykonywania losowych konwersji, a program w kocu
si skompiluje.
Warto zwrotn metody getFirst mona przypisa tylko do typu Object. Metody setFirst
nie mona w oglne wywoa, nawet z typem Object. Na tym polega gwna rnica pomi-
dzy typami Pair<?> i Pair: metod setObject surowej klasy Pair mona wywoa z dowolnym
obiektem typu Object.
Do czego moe si przyda taki typ? Znajduje on zastosowanie w bardzo prostych dziaa-
niach. Na przykad ponisza metoda sprawdza, czy para zawiera dany obiekt. Nie potrzebuje
zna rzeczywistego typu.
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}
Doker nie jest zmienn typow, dlatego nie mona go uywa jako typu w programie. Innymi
sowy, nie mona napisa poniszego kodu:
? t = p.getFirst(); // bd
p.setFirst(p.getSecond());
p.setSecond(t);
656 Java. Podstawy
Mamy problem, poniewa musimy przechowa tymczasowo pierwszy element, aby wykona
zamian. Na szczcie istnieje pewne ciekawe rozwizanie tego problemu. Moemy napisa
metod pomocnicz, na przykad o nazwie swapHelper:
public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
Naley zauway, e metoda swapHelper jest oglna, podczas gdy majca stay parametr typu
Pair<?> swap nie.
W tym przypadku parametr T metody swapHelper chwyta typ wieloznaczny. Nie wiadomo,
jaki typ okrela doker, ale jest to typ okrelony, dziki czemu definicja <T>swapHelper jest
w peni prawidowa, jeli T okrela tamten typ.
Oczywicie w tym przypadku nie musielimy uywa typu wieloznacznego. Mona byo
bezporednio zaimplementowa metod <T> void swap(Pair<T> p) jako ogln, nie uywajc
dokerw. Spjrzmy jednak na poniszy fragment kodu, w ktrym typ wieloznaczny ma swoje
naturalne miejsce w obliczeniach:
public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
{
minmaxBonus(a, result);
PairAlg.swapHelper(result); // OK metoda swapHelper chwyta typ wieloznaczny
}
W tym przypadku mechanizmu chwytania typu wieloznacznego nie mona byo pomin.
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest3
{
public static void main(String[] args)
Rozdzia 12. Programowanie oglne 657
{
Manager ceo = new Manager("Stanisaw Skpy", 800000, 2003, 12, 15);
Manager cfo = new Manager("Piotr Podstpny", 600000, 2003, 12, 15);
Pair<Manager> buddies = new Pair<>(ceo, cfo);
printBuddies(buddies);
ceo.setBonus(1000000);
cfo.setBonus(500000);
Manager[] managers = { ceo, cfo };
class PairAlg
{
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
}
Metoda newInstance zwraca egzemplarz tej klasy utworzony za pomoc konstruktora domyl-
nego. Jej typ zwrotny mona teraz okreli jako T, czyli taki sam jak klasy Class<T>. W ten
sposb unikamy rzutowania.
Metoda cast zwraca przekazany do niej obiekt rzutowany na typ T, jeli jego typ jest podty-
pem T. W przeciwnym razie generuje wyjtek BadCastException.
Metoda getEnumConstants zwraca null, jeli klasa nie jest klas enum, lub tablic wartoci
wyliczenia, ktre wiadomo, e s typu T.
java.lang.Class<T> 1.0
T newInstance() 5.0
Zwraca egzemplarz utworzony za pomoc konstruktora domylnego.
T cast(Object obj) 5.0
Zwraca obj, jeli jest null, lub moe by przekonwertowany na typ T, w przeciwnym
przypadku generuje wyjtek BadCastException.
T[] getEnumConstants() 5.0
Zwraca tablic zapenion wartociami, jeli T jest typem wyliczeniowym, lub null
w przeciwnym przypadku.
Rozdzia 12. Programowanie oglne 659
java.lang.reflect.Constructor<T> 1.1
Innymi sowy, mona zdoby wszystkie dane dotyczce klas i metod parametryzowanych,
ktre zostay podane w ich deklaracjach. Nie ma natomiast sposobu na dowiedzenie si, jak
parametry typowe zostay zastpione w konkretnych obiektach lub wywoaniach metod.
W pakiecie java.lang.reflect znajduje si interfejs o nazwie Type, ktrego celem jest infor-
mowanie o deklaracjach typw oglnych. Interfejs ten ma nastpujce podtypy:
klasa Class opisujca typy konkretne;
interfejs TypeVariable opisujcy zmienne typowe (jak T extends Comparable<?
super T>);
interfejs WildcardType opisujcy typy wieloznaczne (jak ? super T);
interfejs ParameterizedType opisujcy typy klas oglnych lub interfejsowe
(na przykad Comparable<? super T>);
interfejs GenericArrayType opisujcy tablice oglne (na przykad T[]).
Rysunek 12.5 przedstawia hierarchi dziedziczenia tego interfejsu. Naley zauway, e cztery
ostatnie podtypy s interfejsami maszyna wirtualna tworzy egzemplarze odpowiednich klas
implementujcych te interfejsy.
import java.lang.reflect.*;
import java.util.*;
/**
* @version 1.10 2007-05-15
* @author Cay Horstmann
*/
public class GenericReflectionTest
{
public static void main(String[] args)
{
// Wczytanie nazwy klasy z argumentw wiersza polece lub danych wprowadzonych przez uytkownika
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Wpisz nazw klasy (np. java.util.Collections): ");
name = in.next();
}
try
{
// Wydrukuj dane generyczne o klasie i jej metodach publicznych
Class<?> cl = Class.forName(name);
printClass(cl);
for (Method m : cl.getDeclaredMethods())
printMethod(m);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
printType(m.getGenericReturnType(), false);
System.out.print(" ");
System.out.print(name);
System.out.print("(");
printTypes(m.getGenericParameterTypes(), "", ", ", "", false);
System.out.println(")");
}
public static void printTypes(Type[] types, String pre, String sep, String suf,
boolean isDefinition)
{
if (pre.equals(" extends ") && Arrays.equals(types, new Type[]
{ Object.class })) return;
if (types.length > 0) System.out.print(pre);
for (int i = 0; i < types.length; i++)
{
if (i > 0) System.out.print(sep);
printType(types[i], isDefinition);
}
if (types.length > 0) System.out.print(suf);
}
{
printType(owner, false);
System.out.print(".");
}
printType(t.getRawType(), false);
printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
}
else if (type instanceof GenericArrayType)
{
GenericArrayType t = (GenericArrayType) type;
System.out.print("");
printType(t.getGenericComponentType(), isDefinition);
System.out.print("[]");
}
}
}
Jeli uruchomimy go na rzecz klasy ArrayAlg w katalogu PairTest2, raport bdzie zawiera
nastpujce dane:
public static <T extends java.lang.Comparable> Pair<T> minmax(T[])
Metody uyte w tym programie zostay opisane w wycigu z API na kocu podrozdziau.
java.lang.Class<T> 1.0
java.lang.reflect.Method 1.1
java.lang.reflect.TypeVariable 5.0
String getName()
Zwraca nazw zmiennej typowej.
Type[] getBounds()
Zwraca ograniczenia podklasy zmiennej typowej lub tablic o dugoci 0,
jeli zmienna nie ma ogranicze.
java.lang.reflect.WildcardType 5.0
Type[] getLowerBounds()
Zwraca ograniczenia (extends) podklasy zmiennej typowej lub tablic o dugoci
0, jeli nie ma adnych ogranicze podklasowych.
Type[] getUpperBounds()
Zwraca ograniczenia (super) nadklasy zmiennej typowej lub tablic o dugoci 0,
jeli nie ma adnych ogranicze nadklasowych.
java.lang.reflect.ParameterizedType 5.0
Type getRawType()
Zwraca typ surowy typu parametryzowanego.
Type[] getActualTypeArguments()
Zwraca parametry typu, ktre zostay uyte w deklaracji typu parametryzowanego.
Type getOwnerType()
Zwraca typ klasy zewntrznej, jeli jest wywoana na rzecz klasy wewntrznej,
lub null w przypadku wywoania na rzecz klasy najwyszego poziomu.
java.lang.reflect.GenericArrayType 5.0
Type getGenericComponentType()
Zwraca generyczny typ komponentu, ktry zosta uyty w deklaracji
tego typu tablicy.
Potrafimy ju uywa klas oglnych i tworzy wasne klasy oraz metody tego typu. Nie
mniej wan umiejtnoci zdobyt w tym rozdziale jest zdolno rozumienia deklaracji typw
oglnych, ktre mona spotka w dokumentacji API i komunikatach o bdach. Wyczerpu-
jcym rdem wiedzy na temat typw oglnych w Javie jest, sporzdzona przez Ange-
lik Langer, znakomita lista czsto (i niezbyt czsto) zadawanych pyta na ten temat:
http://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html.
W tym rozdziale:
Interfejsy kolekcyjne
Konkretne klasy kolekcyjne
Architektura kolekcji
Algorytmy
Stare kolekcje
Dobr struktur danych do uycia w programie moe mie niebagatelne znaczenie dla p-
niejszej implementacji metod i wydajnoci caej aplikacji. Decydujc si na konkretne struk-
tury, naley odpowiedzie sobie na pytania typu: czy konieczne bdzie przeszukiwanie tysicy
(moe nawet milionw) posortowanych elementw? Czy konieczne bdzie szybkie wstawia-
nie i usuwanie elementw do i ze rodka uporzdkowanego szeregu elementw? Czy konieczne
bdzie powizanie wartoci z ich kluczami?
Ten rozdzia traktuje o strukturach danych dostpnych w bibliotece Javy. Na studiach infor-
matycznych przedmiot dotyczcy struktur danych trwa z reguy jeden semestr, dziki czemu
ta wana tematyka zostaa wyczerpujco przedstawiona w bardzo licznych publikacjach i opra-
cowaniach. W tej ksice prezentujemy odmienne podejcie w stosunku do innych publikacji
z tego zakresu pomijamy teori, a koncentrujemy si na zastosowaniu kolekcji dostpnych
w bibliotece standardowej w profesjonalnym programowaniu.
Interfejs Queue pozwala na dodawanie elementw na kocu kolejki, usuwanie ich z pocztku
struktury danych oraz sprawdzanie liczby elementw w kolejce. Ta struktura danych znajduje
zastosowanie przy tworzeniu zbiorw obiektw, z ktrych elementy s pobierane zgodnie
z zasad pierwszy przyjdzie, pierwszy wyjdzie (zobacz rysunek 13.1).
Rysunek 13.1.
Kolejka
Sam interfejs nie zawiera adnych danych na temat implementacji kolejki. Z dwch najcz-
ciej spotykanych implementacji kolejki jedna dziaa na zasadzie listy cyklicznej, a druga listy
powizanej (rysunek 13.2).
Rozdzia 13. Kolekcje 667
Dziki takiemu podejciu w razie zmiany zdania mona atwo uy innej implementacji.
Wystarczy tylko jedna zmiana w programie wywoanie konstruktora. Jeli na przykad
dojdziemy do wniosku, e lepszym wyborem byby obiekt typu LinkedListQueue, zmieniamy
kod na nastpujcy:
Queue<Customer> expressLane = new LinkedListQueue<Customer>();
expressLane.add(new Customer("Henryk"));
Co sprawia, e wybieramy jedn implementacj zamiast drugiej? Interfejs nie dostarcza ad-
nych informacji na temat wydajnoci. Lista cykliczna jest nieco szybsza od listy powiza-
nej, a wic oglnie rzecz biorc jest preferowana. Jednak jak zawsze jest co za co.
Lista cykliczna jest kolekcj ograniczon, czyli ma ograniczon pojemno. Jeli nie okre-
limy limitu obiektw przechowywanych w takiej licie, niewykluczone, e lepiej bymy na
tym wyszli, gdybymy zastosowali list powizan.
W dokumentacji API mona znale jeszcze jeden zestaw klas, ktrych nazwy zaczynaj si
od sowa Abstract, na przykad AbstractQueue. Klasy te s przeznaczone dla twrcw biblio-
tek. W razie (mao prawdopodobnej) potrzeby zaimplementowania wasnej klasy kolejki atwiej
rozszerzy klas AbstractQueue, ni zaimplementowa wszystkie metody interfejsu Queue.
Metoda add dodaje elementy do kolekcji i zwraca warto true, jeli wykonana przez ni
operacja powoduje zmian stanu kolekcji, lub warto false, jeli kolekcja nie ulega zmianie.
Jeli na przykad do zbioru (ang. set) sprbujemy doda obiekt, ktry ju si tam znajduje,
metoda add nie przyniesie adnego skutku, poniewa w zbiorach nie moe by duplikatw.
Rozdzia 13. Kolekcje 669
Metoda iterator zwraca obiekt implementujcy interfejs Iterator. Za pomoc tego obiektu
mona odwiedzi kolejno wszystkie znajdujce si w kolekcji elementy.
13.1.2.1. Iteratory
W interfejsie Iterator wystpuj trzy metody:
public interface Iterator<E>
{
E next();
boolean hasNext();
void remove();
}
Wywoujc wielokrotnie metod next, mona kolejno odwiedzi wszystkie elementy kolekcji.
Jeli metoda ta napotka koniec kolekcji, zgosi wyjtek NoSuchElementException. Dlatego
przed ni naley zawsze wywoywa metod hasNext, ktra zwraca warto true, jeli s
jeszcze jakie elementy. Aby przejrze wszystkie elementy kolekcji, naley utworzy obiekt
typu Iterator i wywoywa metod next tak dugo, jak metoda hasNext zwraca warto true.
Na przykad:
Collection<String> c = . . .;
Iterator<String> iter = c.iterator();
while (iter.hasNext())
{
String element = iter.next();
obrbka elementu
}
W Java SE 5.0 wprowadzono zgrabniejsz wersj tej ptli. Jej bardziej zwizy zapis reali-
zuje si za pomoc ptli w stylu for each:
for (String element : c)
{
obrbka elementu
}
Ptl for each mona stosowa do wszystkich obiektw implementujcych interfejs Iterable,
w ktrym znajduje si tylko jedna metoda:
public interface Iterable<E>
{
Iterator<E> iterator();
}
Interfejs Collection rozszerza interfejs Iterable. Dziki temu ptli for each mona uywa
do dziaa na wszystkich kolekcjach ze standardowej biblioteki.
losowo. Pewne jest, e przemierzajc t kolekcj, uzyskamy dostp do kadego jej elementu,
ale nie wiadomo, w jakiej kolejnoci bdzie si to odbywa. Nie sprawia to zazwyczaj pro-
blemu, poniewa w dziaaniach typu obliczanie sumy lub zliczanie elementw pasujcych do
wzorca kolejno jest nieistotna.
Ci, ktrzy znaj wczeniejsze wersje Javy, zauwa, e metody next i hasNext inter-
fejsu Iterator speniaj t sam funkcj co nextElement i hasMoreElements
w interfejsie Enumeration. Projektanci biblioteki kolekcyjnej mogli wykorzysta w swo-
jej pracy interfejs Enumeration, ale nie podobay im si niezgrabne nazwy jego metod.
Dlatego utworzono nowy interfejs z krtszymi nazwami metod.
Iteratory w Javie naley wyobraa sobie jako obiekty znajdujce si pomidzy elemen-
tami. W chwili wywoania metody next iterator przeskakuje kolejny element i zwraca refe-
rencj do elementu, ktry wanie przeskoczy (zobacz rysunek 13.3).
Midzy metodami next i remove istnieje pewna bardzo wana zaleno. Tej drugiej nie mona
wywoa, jeli wczeniej nie wywoano pierwszej. Prba zrobienia tego zakoczy si rzu-
ceniem wyjtku IllegalStateException.
Rozdzia 13. Kolekcje 671
Aby usun dwa kolejne elementy, nie mona zastosowa dwch kolejnych wywoa metody
remove:
it.remove();
it.remove(); // Bd!
Najpierw trzeba wywoa metod next, aby przej za kolejny element, ktry ma zosta
usunity.
it.remove();
it.next();
it.remove(); // OK
Twrcy biblioteki standardowej doszli do wniosku, e niektre z tych metod s tak przy-
datne, i powinny by dostpne w bibliotece. Dziki temu uytkownicy tego zbioru klas nie
musz wielokrotnie wynajdywa koa. Jedna z tych metod nosi nazw contains.
672 Java. Podstawy
Przeznaczenie wielu z tych metod atwo odgadn po nazwie. Peny ich opis znajduje si na
kocu podrozdziau w wycigach z API.
Jest to bardzo dobre podejcie do projektowania architektury klas. Uytkownicy kolekcji maj
do dyspozycji bogaty zestaw metod, a twrcy struktur danych nie s obciani implementacj
wszystkich rutynowych metod.
java.util.Collection<E> 1.2
Iterator<E> iterator()
Zwraca obiekt Iterator, za pomoc ktrego mona odwiedza elementy kolekcji.
Rozdzia 13. Kolekcje 673
int size()
Zwraca liczb elementw przechowywanych w kolekcji.
boolean isEmpty()
Zwraca warto true, jeli kolekcja nie zawiera adnych elementw.
boolean contains(Object obj)
Zwraca warto true, jeli kolekcja zawiera obiekt identyczny z obiektem obj.
boolean containsAll(Collection<?> other)
Zwraca warto true, jeli kolekcja zawiera wszystkie elementy znajdujce
si w innej kolekcji.
boolean add(Object element)
Dodaje element do kolekcji. Zwraca warto true, jeli w wyniku wywoania
w kolekcji nastpiy zmiany.
boolean addAll(Collection<? extends E> other)
Dodaje do kolekcji wszystkie elementy z kolekcji other. Zwraca warto true,
jeli w wyniku wywoania w kolekcji nastpiy zmiany.
boolean remove(Object obj)
Usuwa obiekt obj z kolekcji. Zwraca warto true, jeli obiekt zosta znaleziony
i usunity.
boolean removeAll(Collection<?> other)
Usuwa z kolekcji wszystkie elementy, ktre mona znale w kolekcji other.
Zwraca warto true, jeli w wyniku wywoania w kolekcji nastpiy zmiany.
void clear()
Usuwa wszystkie elementy z kolekcji.
boolean retainAll(Collection<?> other)
Usuwa z kolekcji wszystkie elementy, ktre nie s takie same jak jeden z obiektw
w kolekcji other. Zwraca warto true, jeli w wyniku wywoania w kolekcji
nastpiy zmiany.
Object[] toArray()
Zwraca tablic obiektw zapenion elementami z kolekcji.
<T> T[] toArray(T[] arrayToFill)
Zwraca tablic zapenion obiektami z kolekcji. Jeli tablica arrayToFill
jest odpowiedniej dugoci, zostaje zapeniona elementami kolekcji. Dodatkowe
miejsca s zapeniane wartociami null. W przeciwnym przypadku tworzona
jest nowa tablica takiego samego typu jak arrayToFill i o takiej samej dugoci
jak rozmiar kolekcji.
674 Java. Podstawy
java.util.Iterator<E> 1.2
boolean hasNext()
Zwraca warto true, jeli s jeszcze elementy do odwiedzenia.
E next()
Zwraca nastpny obiekt do odwiedzenia. Wyrzuca wyjtek NoSuchElementException,
jeli osignie koniec kolekcji.
void remove()
Usuwa ostatnio odwiedzony obiekt. Wywoanie tej metody musi nastpowa
bezporednio po odwiedzeniu elementu. Jeli kolekcja zmienia si od ostatniego
odwiedzenia elementu, metoda ta wyrzuci wyjtek IllegalStateException.
Problem ten pomaga rozwiza inna powszechnie znana struktura danych, nazywana list
powizan (ang. linked list). Podczas gdy obiekty w tablicy s zapisywane w kolejnych komr-
kach pamici, w licie powizanej znajduj si one w osobnych ogniwach (ang. link). Kade
ogniwo przechowuje take referencj do nastpnego ogniwa w szeregu. W Javie praktycznie
wszystkie listy powizane s listami dwukierunkowymi (ang. doubly linked list), co ozna-
cza, e kade ogniwo przechowuje take referencj do ogniwa je poprzedzajcego (zobacz
rysunek 13.5).
Rysunek 13.4.
Usuwanie
elementu
z tablicy
Wiele osb ukoczyo kurs struktur danych, na ktrym uczy si implementacji list powiza-
nych. Niektrzy mog mie ze wspomnienia zwizane z pltanin pocze przy usuwaniu
lub dodawaniu elementw do list powizanych. Dla tych osb mamy dobr wiadomo
w bibliotece kolekcji Javy znajduje si gotowa do uycia klasa LinkedList.
Poniszy fragment programu dodaje do listy trzy elementy, a nastpnie usuwa drugi element:
List<String> staff = new LinkedList<String>(); // Klasa LinkedList implementuje interfejs List
staff.add("Ania");
staff.add("Bartek");
676 Java. Podstawy
staff.add("Karol");
Iterator iter = staff.iterator();
String first = iter.next(); // dojcie do pierwszego elementu
String second = iter.next(); // dojcie do drugiego elementu
iter.remove(); // usunicie ostatnio odwiedzonego elementu
Rozdzia 13. Kolekcje 677
Dodatkowo interfejs ListIterator zawiera dwie metody, za pomoc ktrych mona porusza
si po licie wstecz.
E previous()
boolean hasPrevious()
Metoda previous, podobnie jak next, zwraca obiekt, ktry wanie przeskoczya.
Metoda add dodaje element przed iteratorem. Na przykad ponisze instrukcje pomijaj pierw-
szy element na licie powizanej i dodaj element Julia przed drugim elementem (zobacz
rysunek 13.7):
List<String> staff = new LinkedList<String>();
staff.add("Ania");
staff.add("Bartek");
staff.add("Karol");
ListIterator<String> iter = staff.listIterator();
iter.next(); // pominicie pierwszego elementu
iter.add("Julia");
Jeli metoda add zostanie wywoana kilka razy, elementy zostan dodane do listy w kolej-
noci podawania. Kady z nich jest dodawany przed aktualnym miejscem pobytu iteratora.
Jeli metoda add zostanie uyta z nieuywanym jeszcze iteratorem utworzonym przez me-
tod listIterator, wskazujcym pocztek listy powizanej, nowy element zostanie dodany
na samym pocztku listy. Jeli iterator przejdzie za ostatni element listy (czyli znajdzie si
w miejscu, w ktrym metoda hasNext zwraca warto false), dodawany element bdzie
stanowi nowy ogon listy. W licie o dugoci n istnieje n+1 miejsc, w ktrych mona wsta-
wia elementy. Ta liczba odpowiada liczbie pooe, w ktrych moe si znale iterator.
678 Java. Podstawy
Jeli na przykad lista powizana zawiera trzy elementy A, B i C, to istniej w niej cztery
miejsca (oznaczone znakiem |), w ktrych mona wstawi nowy element:
|ABC
A|BC
AB|C
ABC|
W kocu metoda set zastpuje ostatni element zwrcony przez metod next lub previous
nowym elementem. Na przykad ponisza procedura zastpuje pierwszy element listy now
wartoci:
ListIterator<String> iter = list.listIterator();
String oldValue = iter.next(); // zwraca pierwszy element
iter.set(newValue); // ustawia pierwszy element na newValue
Nietrudno sobie wyobrazi, e w sytuacji, w ktrej jeden iterator przemierza kolekcj, a inny
j modyfikuje, mog wystpi nieprzyjemne zdarzenia. Wyobramy sobie, e jaki iterator
wskazuje miejsce przed elementem, ktry zosta usunity przez inny iterator. Ten pierwszy
iterator traci w takiej sytuacji racj bytu i nie powinien by uywany. Iteratory list powiza-
nych zostay zaprojektowane w taki sposb, aby wykrywa tego typu modyfikacje. Jeli itera-
tor odkryje, e jego kolekcja zostaa zmodyfikowana przez inny iterator lub metod samej
kolekcji, wyrzuca wyjtek ConcurrentModificationException. Przeanalizujmy poniszy przy-
kadowy kod:
List<String> list = . . .;
ListIterator<String> iter1 = list.listIterator();
ListIterator<String> iter2 = list.listIterator();
iter1.next();
iter1.remove();
iter2.next(); // wyrzuca wyjtek ConcurrentModificationException
Aby unikn tego typu wyjtkw, naley stosowa si do prostej reguy: do kolekcji mona
wstawi dowoln liczb iteratorw, pod warunkiem e su one tylko do odczytu. Alterna-
tywnie jeden z nich moe by zarwno do odczytu, jak i zapisu.
przydaje si podczas debugowania. Aby sprawdzi, czy na licie znajduje si okrelony ele-
ment, naley uy metody contains. Na przykad instrukcja staff.contains("Henryk") zwrci
warto true, jeli lista powizana zawiera acuch Henryk.
Biblioteka zawiera take kilka wtpliwych z teoretycznego punktu widzenia metod. Listy
powizane nie umoliwiaj szybkiego dostpu do dowolnego elementu. Aby dotrze do n-tego
elementu listy, trzeba zacz wdrwk od pocztku i omin pierwszych n1 elementw.
Nie da si nic skrci. Z tego powodu listy powizane rzadko s uywane w sytuacjach,
w ktrych potrzebny jest dostp do elementw za pomoc indeksu cakowitoliczbowego.
Pomimo tego klasa LinkedList udostpnia metod get, ktra pozwala na dostp do wybra-
nego elementu:
LinkedList<String> list = . . .;
String obj = list.get(n);
Oczywicie efektywno tej metody jest niska. Jeli jej uywasz, to zastanw si, czy nie
naleaoby zmieni zastosowanej struktury danych do rozwizywanego problemu.
Nigdy nie naley przemierza listy powizanej za pomoc tej metody, dajcej zudzenie
swobodnego dostpu. Poniszy kod jest niebywale powolny:
for (int i = 0; i < list.size(); i++)
operacje na elemencie list.get(i);
Metoda get posiada jedn niewielk optymalizacj jeli warto indeksu wynosi
co najmniej size()/2, szukanie elementu zaczyna si od koca listy.
Interfejs Iterator listy zawiera take metod informujc o aktualnym pooeniu iteratora.
W rzeczywistoci, ze wzgldu na to, e iteratory w Javie wskazuj miejsce pomidzy elemen-
tami, istniej dwie takie metody. Metoda nextIndex zwraca indeks cakowitoliczbowy ele-
mentu, ktry zostaby zwrcony przez nastpne wywoanie metody next. Metoda previous
Index zwraca indeks elementu, ktry zostaby zwrcony przez nastpne wywoanie metody
previous. Oczywicie warto ta jest o jeden mniejsza od wartoci nextIndex. Metody te s
efektywne, poniewa iteratory pamitaj swoj aktualn pozycj. W kocu, jeli mamy
indeks n, instrukcja list.listIterator(n) zwrci iterator wskazujcy miejsce bezporednio
przed elementem znajdujcym si w polu o indeksie n. Oznacza to, e metoda next zwraca
ten sam wynik co wywoanie list.get(n).
Dysponujc list powizan zawierajc tylko kilka elementw, nie trzeba przesadnie oba-
wia si braku szybkoci metod get i set. Po co wic w ogle w takiej sytuacji uywa listy
powizanej? Jedynym powodem do uywania list powizanych jest szybko wstawiania
i usuwania elementw do i ze rodka kolekcji. Jeli masz tylko kilka elementw, moesz
uy struktury ArrayList.
import java.util.*;
/**
* Program demonstrujcy dziaania na listach powizanych
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class LinkedListTest
{
public static void main(String[] args)
{
List<String> a = new LinkedList<>();
a.add("Ania");
a.add("Karol");
a.add("Eryk");
// Scalenie list a i b
while (bIter.hasNext())
{
if (aIter.hasNext()) aIter.next();
aIter.add(bIter.next());
}
System.out.println(a);
bIter = b.iterator();
while (bIter.hasNext())
{
bIter.next(); // Opuszczenie jednego elementu
if (bIter.hasNext())
682 Java. Podstawy
{
bIter.next(); // Opuszczenie nastpnego elementu
bIter.remove(); // Usunicie elementu
}
}
System.out.println(b);
a.removeAll(b);
System.out.println(a);
}
}
java.util.List<E> 1.2
ListIterator<E> listIterator()
Zwraca iterator listy.
ListIterator<E> listIterator(int index)
Zwraca iterator listy, ktrego pierwsze wywoanie metody next zwrci element
znajdujcy si pod podanym indeksem.
void add(int i, E element)
Dodaje element w okrelonym miejscu.
void addAll(int i, Collection<? extends E> elements)
Dodaje wszystkie elementy z kolekcji w okrelonym miejscu.
E remove(int i)
Usuwa i zwraca element znajdujcy si w okrelonym miejscu.
E get(int i)
Zwraca element znajdujcy si w okrelonym miejscu.
E set(int i, E element)
Zastpuje element znajdujcy si w okrelonym miejscu nowym elementem
i zwraca stary element.
int indexOf(Object element)
Zwraca pooenie pierwszego wystpienia elementu element lub warto -1,
jeli element nie zostanie znaleziony.
int lastIndexOf(Object element)
Zwraca pooenie ostatniego wystpienia elementu element lub warto -1,
jeli element nie zostanie znaleziony.
Rozdzia 13. Kolekcje 683
java.util.ListIterator<E> 1.2
java.util.LinkedList<E> 1.2
LinkedList()
Tworzy pust list powizan.
LinkedList(Collection<? extends E> elements)
Tworzy list powizan i dodaje do niej wszystkie elementy z okrelonej kolekcji.
void addFirst(E element)
void addLast(E element)
Dodaje element na pocztku lub kocu listy.
E getFirst()
E getLast()
Zwraca element znajdujcy si na pocztku lub kocu listy.
E removeFirst()
E removeLast()
Usuwa element z pocztku lub koca listy.
684 Java. Podstawy
Wielu dowiadczonych programistw uywao do tej pory klasy Vector jako dyna-
micznej tablicy. Czemu uywa klasy ArrayList zamiast Vector? Z jednego po-
wodu wszystkie metody klasy Vector s synchronizowane, dziki czemu mona bez-
piecznie uzyska dostp do jednego obiektu tej klasy z dwch wtkw. Jeli natomiast
pracujemy tylko w jednym wtku co zdarza si nieporwnywalnie czciej tra-
cimy bardzo duo czasu na synchronizacj. Natomiast metody klasy ArrayList nie s
synchronizowane. Zalecamy stosowanie klasy ArrayList zamiast Vector zawsze wtedy,
gdy nie jest potrzebna synchronizacja.
Powszechnie znan struktur danych pozwalajc szybko wyszukiwa obiekty jest tablica
mieszajca (ang. hash table; nazywana te haszow). Tablica ta oblicza liczb cakowit,
zwan kodem mieszajcym (ang. hash code), dla kadego obiektu. Kod mieszajcy powstaje
w jaki sposb z pl obiektu, najlepiej w taki, aby obiekty zawierajce rne dane daway
rne kody. Tabela 13.2 zawiera kilka przykadowych kodw mieszajcych zwrconych przez
metod hashCode z klasy String.
Definiujc wasne klasy, naley zaimplementowa wasn metod hashCode wicej na ten
temat mona znale w rozdziale 5. Implementacja ta musi by zgodna z metod equals
jeli a jest rwne b (a.equals(b)), to a i b musz mie ten sam kod mieszajcy.
To, co jest wane teraz, to fakt, e obliczanie kodu mieszajcego przebiega szybko, a wynik
tego dziaania jest uzaleniony tylko od stanu obiektu, ktrego kod jest tworzony, a nie od
innych obiektw w tablicy.
Tablice mieszajce w Javie s zaimplementowane jako tablice list powizanych. Listy w tej
sytuacji nosz miano komrek lub kubekw (ang. bucket; zobacz rysunek 13.8). Aby okre-
li miejsce do przechowywania obiektu w tablicy, naley obliczy jego kod mieszajcy
i znale reszt z dzielenia tej liczby przez liczb wszystkich komrek. Jeli na przykad obiekt
ma kod mieszajcy 76268, a wszystkich komrek jest 128, zostanie on umieszczony w komrce
108 (poniewa reszta z dzielenia 76 268 przez 128 wynosi 108). Jeli mamy szczcie i nie
ma w tej komrce adnego innego elementu, umieszczamy tam nasz obiekt. Oczywicie nie
da si unikn trafienia komrki ze znajdujcym si wewntrz obiektem. Sytuacj tak nazy-
wamy kolizj. Wtedy porwnujemy nowy obiekt z wszystkimi obiektami w tej komrce,
aby sprawdzi, czy go tam jeszcze nie ma. Jeli kody mieszajce s rozsdnie rozoone losowo,
a liczba komrek wystarczajco dua, powinno by konieczne przeprowadzenie tylko kilku
porwna.
Rysunek 13.8.
Tablica
mieszajca
Aby zyska wiksz kontrol nad dziaaniem tablicy, mona okreli pocztkowy licznik
komrek. Licznik ten okrela liczb komrek, w ktrych przechowywane s obiekty o identycz-
nych wartociach mieszajcych. Jeli do tablicy wstawi si zbyt wiele elementw, zwiksza
si liczba kolizji i pogarsza szybko znajdowania elementw.
Jeli znana jest przybliona ostateczna liczba elementw tablicy, to mona ustawi licznik
komrek. Zazwyczaj ustawia si go na 75 do 150% spodziewanej liczby elementw. Nie-
ktrzy badacze utrzymuj, e dobrze jest ustawi ten licznik na liczb pierwsz, co pozwoli
unikn grupowania si kluczy, jednak nie ma na to przekonujcych dowodw. Biblioteka
standardowa stosuje liczniki komrek bdce potgami liczby 2, a domylna liczba to 16 (kada
warto podawana jako wielko tablicy jest automatycznie zaokrglana do najbliszej potgi
dwjki).
686 Java. Podstawy
Oczywicie nie zawsze wiadomo, ile elementw bdzie trzeba przechowa. Czasami mona
te poda za ma liczb. Jeli tablica zostanie przepeniona, konieczna jest jej reorganizacja.
Polega to na utworzeniu nowej tablicy z wiksz liczb komrek i przeniesieniu wszystkich
danych do tej nowej tablicy. Stara tablica zostaje usunita. O reorganizacji tablicy decyduje
wspczynnik zapenienia (ang. load factor). Jeli na przykad wspczynnik ten wynosi 0,75
(warto domylna), a tablica zostanie zapeniona w ponad 75 procentach, nastpuje jej auto-
matyczna reorganizacja, w wyniku ktrej tworzona jest tablica o dwukrotnie wikszej liczbie
komrek. W wikszoci zastosowa najlepiej pozostawi wspczynnik 0,75 bez zmian.
W bibliotece kolekcji Javy znajduje si klasa HashSet, ktra implementuje zbir bazujcy na
tablicy mieszajcej. Dodawanie elementw do tego zbioru odbywa si za pomoc metody
add. Metoda contains zostaa przedefiniowana w taki sposb, e szybko sprawdza, czy doda-
wany element nie znajduje si ju w zbiorze. Sprawdza tylko elementy w jednej komrce, a nie
caej kolekcji.
Iterator zbioru HashSet odwiedza wszystkie komrki po kolei. Poniewa elementy te s poroz-
rzucane po tablicy, iterator odwiedza je w losowej kolejnoci. Dlatego zbioru HashSet naley
uywa wycznie wwczas, gdy kolejno elementw w kolekcji nie jest wana.
import java.util.*;
/**
* Program drukujcy wszystkie sowa ze strumienia wejciowego przy uyciu zbioru
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class SetTest
{
public static void main(String[] args)
{
Set<String> words = new HashSet<>(); // Klasa HashSet implementuje interfejs Set
long totalTime = 0;
Rozdzia 13. Kolekcje 687
Naley zachowa ostrono przy modyfikowaniu elementw zbioru. Jeli kod mie-
szajcy elementu zmieni si, element ten bdzie si znajdowa w niewaciwym
miejscu w strukturze danych.
java.util.HashSet<E> 1.2
HashSet()
Tworzy pusty obiekt HashSet.
HashSet(Collection<? extends E> elements)
Tworzy obiekt HashSet i wstawia do niego wszystkie elementy okrelonej kolekcji.
HashSet(int initialCapacity)
Tworzy pusty obiekt HashSet o okrelonej pojemnoci (liczbie komrek).
HashSet(int initialCapacity, float loadFactor)
Tworzy obiekt HashSet o okrelonej pojemnoci i z okrelonym wspczynnikiem
zapenienia (liczba od 0,0 do 1,0 okrela poziom zapenienia zbioru, przy ktrym
jest on reorganizowany na zbir o wikszej pojemnoci).
java.lang.Object 1.0
int hashCode()
Zwraca kod mieszajcy obiektu. Kod mieszajcy moe by dowoln liczb
cakowit, take ujemn. Definicje metod equals i hashCode musz by ze sob
zgodne jeli instrukcja x.equals(y) zwraca warto true, to instrukcja
x.hashCode() musi zwraca tak sam warto jak y.hashCode().
688 Java. Podstawy
Wartoci zostan wydrukowane w kolejnoci alfabetycznej: Ania, Bartek, Karol. Jak wska-
zuje sama nazwa klasy, elementy w tym zbiorze s przechowywane w strukturze drzewiastej
(obecnie uywane s drzewa czerwono-czarne; ich opis mona znale na przykad w ksice
pod tytuem Wprowadzenie do algorytmw, ktrej autorami s Thomas Cormen, Charles Leiser-
son, Ronald Rivest i Clifford Stein, WNT, Warszawa 2007). Kady nowy element jest umiesz-
czany w odpowiednim miejscu zgodnie z kolejnoci. Dziki temu elementy odwiedzane przez
iterator s zawsze posortowane.
java.util.TreeSet<E> 1.2
TreeSet()
Tworzy pusty zbir TreeSet.
TreeSet(Collection<? extends E> elements)
Tworzy zbir TreeSet i wstawia do niego wszystkie elementy z okrelonej kolekcji.
Rozdzia 13. Kolekcje 689
Aby mc wstawia wasne obiekty, naley we wasnym zakresie zdefiniowa porzdek sorto-
wania, implementujc interfejs Comparable. W klasie Object nie istnieje domylna implemen-
tacja metody compareTo.
Ponisza procedura na przykad sortuje obiekty typu Item wedug numeru czci:
class Item implements Comparable<Item>
{
public int compareTo(Item other)
{
return partNumber - other.partNumber;
}
. . .
}
Porwnywanie dwch cakowitych liczb dodatnich, takich jak numery seryjne, mona zaim-
plementowa, wykorzystujc ich rnic. Jeli rnica jest ujemna, pierwszy element powi-
nien si znajdowa przed drugim, zero oznacza, e elementy s identyczne, a liczba dodatnia
pozostae moliwoci.
Sztuczka ta dziaa wycznie dla niezbyt duych liczb. Jeli x jest bardzo du
liczb dodatni, a y jest bardzo du liczb ujemn, rnica x-y moe przekroczy
zakres typu.
W tego rodzaju sytuacjach naley zmusi zbir TreeSet do uycia rnych metod porw-
nujcych, przekazujc obiekt Comparator do konstruktora TreeSet. Interfejs Comparator za-
wiera metod compare z dwoma parametrami jawnymi:
690 Java. Podstawy
Metoda compare, podobnie jak compareTo, zwraca ujemn liczb cakowit, jeli a wyst-
puje przed b, zero, jeli a i b s identyczne, lub dodatni liczb cakowit w pozostaych
przypadkach.
Aby posortowa elementy wedug ich opisw, wystarczy zdefiniowa klas implementujc
interfejs Comparable:
class ItemComparator implements Comparator<Item>
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
}
Elementy drzewa utworzonego przy uyciu komparatora (obiektu typu Comparator) s porw-
nywane za pomoc tego komparatora.
Naley zauway, e komparator ten nie zawiera adnych danych, a jedynie metod porwnu-
jc. Tego typu obiekty czasami nazywane s obiektami funkcyjnymi.
Biorc pod uwag liczby przedstawione w tabeli 13.3, mona si zastanawia, czy nie lepiej
byoby zawsze uywa zbiorw TreeSet zamiast HashSet. Nie da si ukry, e wstawianie
elementw do tego pierwszego nie zajmuje wiele wicej czasu, a przy okazji obiekty s auto-
matycznie sortowane. Konkretna decyzja zaley od danych, ktre maj by przechowywane.
Jeli nie musz one by uporzdkowane, nie ma sensu marnowa czasu na ich sortowanie.
Co wicej, w niektrych przypadkach atwiej jest napisa funkcj mieszajc ni sortujc.
Funkcja mieszajca musi tylko dobrze miesza obiekty, natomiast funkcja porwnujca musi
je precyzyjnie rozrnia.
Program przedstawiony na listingu 13.3 tworzy dwa zbiory TreeSet zapenione obiektami Item.
Pierwszy z nich jest sortowany wedug numerw czci, czyli w domylny sposb. Drugi
natomiast posortowano wedug opisw za pomoc niestandardowego komparatora. Na lis-
tingu 13.4 przedstawiona jest klasa Item.
package treeSet;
/**
@version 1.12 2012-01-26
@author Cay Horstmann
*/
import java.util.*;
/**
Program sortujcy zbir elementw poprzez porwnanie ich opisw
*/
public class TreeSetTest
{
public static void main(String[] args)
{
SortedSet<Item> parts = new TreeSet<>();
parts.add(new Item("Toster", 1234));
parts.add(new Item("Widget", 4562));
692 Java. Podstawy
sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}
}
import java.util.*;
/**
* Element z opisem i numerem czci
*/
public class Item implements Comparable<Item>
{
private String description;
private int partNumber;
/**
* Tworzy element
*
* @param aDescription
* opis elementu
* @param aPartNumber
* numer czci elementu
*/
public Item(String aDescription, int aPartNumber)
{
description = aDescription;
partNumber = aPartNumber;
}
/**
* Pobiera opis elementu
*
* @return opis
*/
public String getDescription()
{
return description;
}
Rozdzia 13. Kolekcje 693
java.lang.Comparable<T> 1.2
java.util.Comparator<T> 1.2
int compare(T a, T b)
Porwnuje dwa obiekty i zwraca warto ujemn, jeli a wystpuje przed b, zero,
jeli obiekty s identyczne, lub warto dodatni, jeli a wystpuje za b.
java.util.SortedSet<E> 1.2
java.util.NavigableSet<E> 6
E higher(E value)
E lower(E value)
Zwraca najmniejszy element wikszy od wartoci value lub najwikszy element
mniejszy od wartoci value, lub warto null, jeli nie ma takiego elementu.
E ceiling(E value)
E floor (E value)
Zwraca najmniejszy element wikszy od lub rwny wartoci value lub najwikszy
element mniejszy od lub rwny wartoci value, lub warto null, jeli nie ma
takiego elementu.
E pollFirst()
E pollLast()
Usuwa i zwraca najmniejszy lub najwikszy element zbioru lub warto null,
jeli zbir jest pusty.
Iterator<E> descendingIterator()
Zwraca iterator, ktry przemierza zbir w kolejnoci malejcej.
java.util.TreeSet<E> 1.2
TreeSet()
Tworzy zbir TreeSet do przechowywania obiektw implementujcych interfejs
Comparable.
TreeSet(Comparator<? super E> c)
Tworzy zbir TreeSet i sortuje jego elementy przy uyciu okrelonego komparatora.
TreeSet(SortedSet<? extends E> elements)
Tworzy zbir TreeSet, wstawia do niego wszystkie elementy z posortowanego
zbioru oraz wykorzystuje ten sam komparator co okrelony zbir uporzdkowany.
java.util.Queue<E> 5.0
java.util.Deque<E> 6
E peekLast()
Zwraca element znajdujcy si na czole kolejki (ale go nie usuwa), pod warunkiem
e kolejka nie jest pusta. Jeli kolejka jest pusta, pierwsze dwie z powyszych
metod zgaszaj wyjtek NoSuchElementException, a pozostae zwracaj warto null.
java.util.ArrayDeque<E> 6
ArrayDeque()
ArrayDeque(int initialCapacity)
Tworzy nieograniczon kolejk dwukierunkow o pocztkowej pojemnoci
16 elementw lub odpowiadajcej wartoci initialCapacity.
Kolejka priorytetowa, podobnie jak struktura TreeSet, moe przechowywa elementy klasy
implementujcej interfejs Comparable lub obiekt Comparator przekazywany do konstruktora.
import java.util.*;
/**
* Program demonstrujcy zastosowanie kolejki priorytetowej
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
Rozdzia 13. Kolekcje 697
java.util.PriorityQueue 5.0
PriorityQueue()
PriorityQueue(int initialCapacity)
Tworzy kolejk priorytetow do przechowywania obiektw implementujcych
interfejs Comparable.
PriorityQueue(int initialCapacity, Comparator<? super E> c)
Tworzy kolejk priorytetow i wykorzystuje do jej sortowania okrelony
komparator.
13.2.8. Mapy
Zbir (ang. set) to rodzaj kolekcji pozwalajcy na szybkie wyszukiwanie elementw. Aby
jednak znale jaki element, trzeba posiada jego wiern kopi. Nie jest to zbyt czsto wyko-
nywany rodzaj wyszukiwania zazwyczaj do dyspozycji jest jaki rodzaj informacji klu-
czowej i zadanie polega na znalezieniu zwizanego z ni elementu. Tego typu struktury reali-
zowane s za pomoc map. Mapy przechowuj pary klucz warto. Aby znale element
w mapie, trzeba zna jego klucz. Na przykad w mapie mona przechowywa tabel danych
o pracownikach, gdzie kluczami bd identyfikatory pracownikw, a wartociami obiekty typu
Employee.
Ktr struktur wybra: HashMap czy TreeMap? Podobnie jak ze zbiorami, mieszanie jest
nieco szybsze i naley je wybiera, gdy kolejno kluczy nie ma znaczenia.
Dla kadego obiektu dodawanego do mapy HashMap naley poda jego klucz. W tym przy-
padku kluczem jest acuch, a odpowiadajc mu wartoci obiekt typu Employee.
Aby pobra (i zapisa w pamici) obiekt z mapy, naley posuy si jego kluczem.
String s = "987-98-9996";
e = staff.get(s); // pobiera obiekt harry
Jeli z danym kluczem nie jest skojarzona adna warto w mapie, metoda get zwraca war-
to null.
Klucze musz by unikatowe, to znaczy, e nie mona dwm rnym wartociom przypisa
takiego samego klucza. Jeli metoda put zostanie wywoana dwa razy przy uyciu jednego
klucza, warto z drugiego wywoania zastpi t z pierwszego. W rzeczywistoci metoda ta
zwrci poprzedni warto przechowywan pod tym kluczem.
W architekturze kolekcji mapa nie jest uznawana za kolekcj (inne architektury struktur
danych traktuj map jako kolekcj par lub kolekcj wartoci indeksowan za pomoc kluczy).
Istnieje jednak moliwo utworzenia widoku mapy w postaci obiektw implementujcych
interfejs Collection lub jeden z jego podinterfejsw.
Istniej trzy rodzaje widokw: zbir kluczy, kolekcja wartoci (ktra nie jest zbiorem) i zbir
par klucz warto. Klucze i pary klucz warto tworz zbiory, poniewa mapa moe
zawiera tylko jeden egzemplarz kadego klucza. Ponisze metody zwracaj wymienione
widoki (elementy zbioru entrySet s obiektami statycznej klasy wewntrznej Map.Entry):
Set<K> keySet()
Collection<K> values()
Set<Map.Entry<K, V>> entrySet()
Naley zauway, e keySet nie jest zbiorem typu HashSet ani TreeSet, tylko obiektem jeszcze
innej klasy implementujcej interfejs Set. Interfejs Set rozszerza interfejs Collection, dziki
czemu zbioru keySet mona uywa w taki sam sposb jak kadej innej kolekcji.
Metoda remove iteratora usuwa z mapy klucz i skojarzon z nim warto. Nie mona
natomiast doda elementu do widoku keySet, poniewa dodanie klucza bez wartoci
jest bezcelowe. Prba wywoania metody add zakoczy si zgoszeniem wyjtku Unsup
portedOperationException. Widok entrySet jest ograniczony w podobny sposb, mimo
e dodanie nowej pary klucz warto z teoretycznego punktu widzenia nie byoby pozba-
wione sensu.
import java.util.*;
/**
* Program demonstrujcy uycie mapy z kluczami typu String i wartociami typu Employee
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class MapTest
{
public static void main(String[] args)
{
Map<String, Employee> staff = new HashMap<>();
staff.put("144-25-5464", new Employee("Anna Kowalska"));
staff.put("567-24-2546", new Employee("Henryk Kwiatek"));
staff.put("157-62-7935", new Employee("Marcin Nowak"));
staff.put("456-62-5527", new Employee("Franciszek Frankowski"));
System.out.println(staff);
// usunicie wartoci
staff.remove("567-24-2546");
// podmienienie pozycji
// wyszukanie wartoci
System.out.println(staff.get("157-62-7935"));
V get(K key)
Zwraca warto skojarzon z kluczem key. Jeli nie znajdzie klucza w mapie,
zwraca warto null. Klucz moe mie warto null.
V put(K key, V value)
Wstawia par klucz warto do mapy. Jeli taki klucz jest ju w mapie, skojarzony
z nim obiekt jest zastpowany nowym. Metoda ta zwraca poprzedni warto
skojarzon z kluczem lub warto null, jeli wczeniej takiego klucza nie byo.
Klasy implementujce mog zabrania stosowania kluczy lub wartoci null.
void putAll(Map<? extends K, ? extends V> entries)
Wstawia do mapy wszystkie pozycje z okrelonej mapy.
boolean containsKey(Object key)
Zwraca warto true, jeli klucz znajduje si w mapie.
boolean containsValue(Object value)
Zwraca warto true, jeli warto znajduje si w mapie.
Set<Map.Entry<K, V>> entrySet()
Zwraca widok zbiorowy obiektw Map.Entry, czyli par klucz warto znajdujcych
si w mapie. Ze zbioru tego mona usuwa elementy, a skutki tego dziaania bd
uwzgldnione w mapie. Nie mona natomiast nic dodawa.
Set<K> keySet()
Zwraca widok zbiorowy wszystkich kluczy znajdujcych si w mapie. Jeli jaki
klucz z tego zbioru zostanie usunity, z mapy zostanie usunity ten sam klucz
i skojarzona z nim warto. Nie mona nic dodawa.
Collection<V> values()
Zwraca widok kolekcyjny wszystkich wartoci znajdujcych si w mapie. Jeli jaka
warto z tego zbioru zostanie usunita, z mapy zostanie usunita ta sama warto
i skojarzony z ni klucz. Nie mona nic dodawa.
Rozdzia 13. Kolekcje 701
K getKey()
V getValue()
Zwraca klucz lub warto okrelonej pozycji.
V setValue(V newValue)
Zamienia warto na now warto i zwraca star warto.
HashMap()
HashMap(int initialCapacity)
HashMap(int initialCapacity, float loadFactor)
Tworzy pust map HashMap o okrelonej pojemnoci i z okrelonym
wspczynnikiem zapenienia (liczba z zakresu od 0,0 do 1,0 okrelajca stopie
zapenienia tablicy HashTable, po przekroczeniu ktrego nastpuje reorganizacja
na wiksz jednostk). Domylny wspczynnik zapenienia wynosi 0,75.
java.util.TreeMap<K, V> 1.2
Niestety nie jest to takie proste. System ten przechowuje informacje o aktywnych obiektach.
Dopki mapa jest aktywna, wszystkie jej komrki rwnie s aktywne, przez co nie mona
ich usun. Dlatego naley pamita, aby usuwa z programu nieuywane wartoci z dugo
wykorzystywanych map. Mona te wybra inne rozwizanie i uy klasy WeakHashMap. Struk-
tura ta wsppracuje z systemem zbierania nieuytkw w zakresie usuwania par klucz war-
to, jeli jedyne odwoanie do klucza pochodzi z pozycji w tablicy.
Proces ten mona nawet zautomatyzowa. W tym celu naley utworzy podklas klasy
LinkedHashMap i przedefiniowa ponisz metod:
protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
Dziki temu dodanie nowego elementu spowoduje usunicie najstarszego elementu (eldest),
pod warunkiem e wywoana metoda zwrci warto true. Ponisza przykadowa pami pod-
rczna przechowuje maksymalnie sto elementw:
Map<K, V> cache = new
LinkedHashMap<K, V>(128, 0.75F, true)
{
protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
{
return size() > 100;
}
};
Klasa EnumSet nie posiada konstruktora publicznego. Do utworzenia tego typu zbioru trzeba
uy statycznej metody fabrycznej:
enum Weekday { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };
EnumSet<Weekday> always = EnumSet.allOf(Weekday.class);
EnumSet<Weekday> never = EnumSet.noneOf(Weekday.class);
EnumSet<Weekday> workday = EnumSet.range(Weekday.MONDAY, Weekday.FRIDAY);
EnumSet<Weekday> mwf = EnumSet.of(Weekday.MONDAY, Weekday.WEDNESDAY, Weekday.FRIDAY);
EnumMap to mapa, w ktrej klucze s typu wyliczeniowego. Jest ona prost i wydajn tablic
wartoci. Typ klucza naley okreli w konstruktorze:
EnumMap<Weekday, Employee> personInCharge = new EnumMap<Weekday, Employee>(Weekday.class);
Dziki temu dwa obiekty s uznawane za rne, nawet jeli maj tak sam zawarto. Klasa
ta znajduje zastosowanie w implementacji algorytmw przemierzajcych obiekty (na przykad
serializujcych), gdzie konieczne jest pamitanie, ktre obiekty zostay ju przetworzone.
WeakHashMap()
WeakHashMap(int initialCapacity)
WeakHashMap(int initialCapacity, float loadFactor)
Tworzy pust map WeakHashMap o okrelonej pojemnoci i okrelonym
wspczynniku zapenienia.
java.util.LinkedHashSet<E> 1.4
LinkedHashSet()
LinkedHashSet(int initialCapacity)
LinkedHashSet(int initialCapacity, float loadFactor)
Tworzy pusty zbir LinkedHashSet o okrelonej pojemnoci i okrelonym
wspczynniku zapenienia.
LinkedHashMap()
LinkedHashMap(int initialCapacity)
LinkedHashMap(int initialCapacity, float loadFactor)
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
Tworzy pust map LinkedHashMap o okrelonej pojemnoci oraz okrelonym
wspczynniku zapenienia i porzdku. Jeli parametr accessOrder ma warto true,
stosowany jest porzdek wedug kolejnoci dostpu. Warto false oznacza
porzdek w kolejnoci wstawiania.
protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
T metod naley przedefiniowa, aby zwracaa warto true, jeli najstarsza
pozycja ma by usuwana. Parametr eldest okrela pozycj, ktrej usunicie
jest rozwaane. Metoda ta jest wywoywana po dodaniu pozycji do mapy. Domylna
implementacja zwraca warto false stare elementy nie s domylnie usuwane.
706 Java. Podstawy
EnumMap(Class<K> keyType)
Tworzy map, ktrej klucze s okrelonego typu.
IdentityHashMap()
IdentityHashMap(int expectedMaxSize)
Tworzy pust map IdentityHashMap o pojemnoci rwnej najmniejszej potdze
cyfry 2 wikszej ni 1,5*expectedMaxSize (domylna warto parametru
expectedMaxSize to 21).
java.lang.System 1.0
Uytkownik architektury rozszerza jej klasy, dziki czemu nie musi opracowywa niekt-
rych podstawowych mechanizmw od nowa. Na przykad Swing jest architektur interfej-
sw uytkownika.
Biblioteka kolekcji w Javie stanowi architektur klas kolekcyjnych. Zawiera definicje kilku
interfejsw i klas abstrakcyjnych przeznaczonych do uytku przez twrcw kolekcji (zobacz
rysunek 13.10) oraz udostpnia pewne mechanizmy, jak na przykad protok iteracyjny.
Aby uywa klas kolekcyjnych, nie trzeba posiada wiedzy na temat ich architektury udo-
wodnilimy to w poprzednich podrozdziaach. Aby jednak tworzy algorytmy generyczne
dziaajce na wielu typach kolekcji lub cakiem nowe typy kolekcji, trzeba zna budow tej
architektury.
Poniewa mapy przechowuj pary klucz warto, elementy wstawia si do nich za pomoc
metody put:
V put(K key, V value)
Do odczytu elementw z kolekcji suy iterator. Elementy z mapy mona take odczyta za
pomoc metody get:
V get(K key)
708 Java. Podstawy
Jak byo ju wczeniej wspominane, interfejs List udostpnia te metody bez wzgldu na to,
czy s one wydajne w okrelonej implementacji, czy nie. Aby pozwoli na uniknicie po-
wolnych operacji dostpu swobodnego, w Java SE 1.4 wprowadzono interfejs o nazwie
RandomAccess. Nie posiada on adnych metod, ale mona za jego pomoc sprawdzi, czy
okrelona kolekcja umoliwia szybki dostp swobodny do elementw:
if (c instanceof RandomAccess)
{
algorytm dostpu losowego
}
else
{
algorytm dostpu liniowego
}
Teoretycznie mona by byo utworzy osobny interfejs o nazwie Array, ktry roz-
szerzaby interfejs List i deklarowa metody dostpu swobodnego. Gdyby istnia
taki osobny interfejs, algorytmy wymagajce dostpu swobodnego uywayby parame-
trw typu Array i nie mona by byo zastosowa ich do kolekcji o powolnym dostpie
swobodnym. Jednak projektanci architektury kolekcji zdecydowali si nie definiowa osob-
nego interfejsu, poniewa chcieli, aby liczba interfejsw bya jak najmniejsza. Nie chciano
te traktowa protekcjonalnie programistw. Programista moe przekaza list powizan
do algorytmu implementujcego dostp swobodny musi tylko mie wiadomo kosz-
tw wydajnociowych tego dziaania.
Interfejs Set jest identyczny z interfejsem Collection, ale jego metody s nieco bardziej restryk-
cyjne. Metoda add zbioru nie powinna dodawa duplikatw. Metoda equals powinna by
zdefiniowana w taki sposb, aby dwa zbiory byy identyczne, jeli maj takie same elementy,
ale niekoniecznie w takiej samej kolejnoci. Metoda hashCode dla dwch zbiorw zawiera-
jcych takie same elementy powinna zwraca ten sam kod mieszajcy.
Po co tworzy osobny interfejs, skoro sygnatury metod s takie same? Koncepcyjnie nie
wszystkie kolekcje s zbiorami. Dziki istnieniu interfejsu Set programici mog pisa
metody, ktre dziaaj tylko na zbiorach.
Rozdzia 13. Kolekcje 709
Kolej na klasy implementujce wymienione interfejsy. Wiemy ju, e niektre metody interfej-
sw kolekcyjnych mona z atwoci zaimplementowa na bazie bardziej podstawowych
metod. Wiele z tych implementacji znajduje si w klasach abstrakcyjnych:
AbstractCollection
AbstractList
AbstractSequentialList
AbstractSet
AbstractQueue
AbstractMap
Klasy te mona rozszerza przy tworzeniu wasnych klas kolekcyjnych, dziki czemu dzie-
dziczy si po nich wiele rutynowych procedur.
Na koniec naley jeszcze wymieni kilka starszych klas kontenerowych, ktre s dostpne
w Javie od pocztku, zanim jeszcze powstaa architektura kolekcji:
Vector
Stack
Hashtable
Properties
Zostay one wcielone do kolekcji rysunek 13.12. Omawiamy te klasy nieco dalej.
Rysunek 13.12.
Starsze klasy
w architekturze
kolekcji
Za pomoc tak zwanych widokw (ang. view) mona tworzy inne obiekty implementujce
metody keySet mapy. Na pierwszy rzut oka wydaje si, e metoda ta tworzy nowy zbir, zape-
nia go wszystkimi kluczami z mapy i zwraca go. Nie jest to jednak prawda. Metoda keySet
zwraca obiekt klasy implementujcej interfejs Set, ktrego metody operuj na oryginalnej
mapie. Taka kolekcja nazywana jest widokiem.
Zwrcony obiekt nie jest typu ArrayList. Jest to obiekt widokowy udostpniajcy metody
get i set, ktre operuj na lecej u podoa tablicy. Wszystkie metody, ktre mog zmieni
rozmiar tej tablicy (na przykad metody add i remove doczonego iteratora), zgaszaj wyjtek
UnsupportedOperationException.
Od Java SE 5.0 metoda asList moe przyjmowa rne zestawy argumentw. Zamiast tablicy
mona jej przekaza poszczeglne elementy. Na przykad:
List<String> names = Arrays.asList("Ania", "Bartek", "Karol");
Struktura ta zajmuje bardzo mao miejsca w pamici obiekt jest zapisany tylko w jednym
miejscu. Jest to sprytne zastosowanie techniki widokw.
Klasa Collections zawiera kilka metod, ktrych parametry lub wartoci zwrotne
s kolekcjami. Nie naley jej myli z interfejsem Collection.
Metoda wywoana poniej zwraca obiekt widokowy implementujcy interfejs Set (w odr-
nieniu od metody nCopies, ktra tworzy list). Zwrcony obiekt implementuje niemodyfiko-
walny jednoelementowy zbir pozbawiony narzutu struktury danych. Metody singletonList
i singletonMap maj podobne dziaanie.
Collections.singleton(anObject)
Pierwszy indeks jest wliczany, drugi nie podobnie jak z parametrami metody substring
z klasy String.
712 Java. Podstawy
Przedzia group2 jest teraz pusty, a z listy staff zostay usunite te elementy, ktre znajdoway
si w tym przedziale.
Zwracaj one podzbiory wszystkich elementw, ktre s wiksze ni lub rwne from i mniej-
sze od to. Podobne metody dla uporzdkowanych map to:
SortedMap<K, V> subMap(K from, K to)
SortedMap<K, V> headMap(K to)
SortedMap<K, V> tailMap(K from)
Zwracaj one widoki map zawierajce pozycje, ktrych klucze mieszcz si w okrelonych
zakresach.
Kada z tych metod dziaa w kooperacji z jakim interfejsem. Na przykad metoda Collec
tions.unmodifiableList wsppracuje z klasami ArrayList, LinkedList i wszystkimi innymi,
ktre implementuj interfejs List.
Wyobramy sobie, e chcemy zezwoli pewnej procedurze pobra zawarto jakiej kolekcji,
ale nie chcemy, by j modyfikowaa. Oto, co mona w takiej sytuacji zrobi:
Rozdzia 13. Kolekcje 713
Niemodyfikowalny widok nie czyni samej kolekcji niemodyfikowaln. Nadal mona j mody-
fikowa przy uyciu jej pierwotnej referencji (w naszym przypadku staff). Mona te wywo-
ywa metody modyfikujce na rzecz jej elementw.
Poniewa widoki opakowuj interfejs, a nie prawdziwy obiekt kolekcyjny, dostpne s tylko
te metody, ktre zostay zdefiniowane w tym interfejsie. Na przykad klasa LinkedList zawiera
metody addFirst i addLast, ktre nie nale do interfejsu List. Dlatego nie mona ich uywa
w widoku niemodyfikowalnym.
Teraz mona dostawa si do obiektu map z rnych wtkw. Metody takie jak get i put s
synchronizowane, to znaczy kade wywoanie metody musi zosta w peni ukoczone, zanim
inny wtek bdzie mg wywoa kolejn metod. Kwesti synchronizowanego dostpu do
struktur danych szczegowo omawiamy w rozdziale 14.
714 Java. Podstawy
Bd w instrukcji add nie zostanie wykryty w czasie dziaania programu. W zamian wystpi
wyjtek rzutowania, kiedy w innej czci kodu zostanie wywoana metoda get i zostanie
wykonane rzutowanie jej wyniku na typ String.
Metoda add widoku sprawdza, czy wstawiany obiekt naley do okrelonej klasy; jeli nie,
generuje wyjtek ClassCastException. Jest to korzystne, poniewa bd zostaje zgoszony
w odpowiednim miejscu:
ArrayList rawList = safeStrings;
rawList.add(new Date()); // Lista kontrolowana generuje wyjtek ClassCastException.
W dokumentacji API wiele metod klas i interfejsw kolekcyjnych oznaczono jako operacje
opcjonalne (ang. optional operations). Wydaje si to sprzeczne z ide interfejsu, ktry prze-
cie jak wiadomo okrela zestaw metod, ktre klasa musi implementowa. Rzeczy-
wicie ten stan rzeczy nie jest satysfakcjonujcy z teoretycznego punktu widzenia. Lepszym
rozwizaniem byoby utworzenie osobnych interfejsw dla widokw tylko do odczytu i wido-
kw, ktre nie mog zmieni rozmiaru kolekcji. Wtedy jednak liczba interfejsw potroiaby
si, a to dla projektantw biblioteki byoby nie do zaakceptowania.
java.util.Collections 1.2
java.util.Arrays 1.2
java.util.List<E> 1.2
java.util.SortedSet<E> 1.2
API java.util.NavigableSet<E> 6
java.util.NavigableMap<K, V> 6
Wykorzystujemy tutaj fakt, e kada kolekcja posiada konstruktor, ktrego parametr jest
inn kolekcj przechowujc wartoci pocztkowe.
Zwraca ona wszystkie elementy, ktre znajduj si take w zbiorze b. W ten sposb znaleli-
my cz wspln dwch zbiorw bez tworzenia ptli.
Poniewa zbir kluczy jest widokiem mapy, klucze i skojarzeni z nimi pracownicy s auto-
matycznie usuwani z tej mapy.
Przy uyciu widoku podprzedziau mona ograniczy operacje zbiorcze do podlist i podzbio-
rw. Zamy, e chcemy doda 10 elementw z listy do innego zbiornika. Pobieramy dziesi
pierwszych elementw i umieszczamy je w podlicie:
relocated.addAll(staff.subList(0, 10));
Utworzenie tablicy z kolekcji nie jest ju takie atwe. Mona oczywicie uy do tego metody
toArray:
Object[] values = staff.toArray();
Jednak wynik bdzie tablic obiektw typu Object. Nie mona zastosowa rzutowania, nawet
jeli wiadomo, e kolekcja zawieraa obiekty okrelonego typu:
String[] values = (String[]) staff.toArray(); // Bd!
Metoda toArray zwraca tablic typu Object[], ktrego nie mona zmieni. W zamian naley
uy alternatywnej wersji tej metody. Przekazujemy do niej jako parametr tablic o dugoci 0
takiego typu, jakiego potrzebujemy. Zwrcona tablica bdzie wtedy miaa taki wanie typ:
String[] values = staff.toArray(new String[0]);
13.4. Algorytmy
Uoglnione interfejsy kolekcyjne maj pewn du zalet algorytmy trzeba implemen-
towa tylko jeden raz. Wemy na przykad prosty algorytm znajdujcy najwikszy element
w kolekcji. Standardowo jego implementacja polegaaby na uyciu ptli. Ponisza procedura
znajduje najwikszy element tablicy:
if (a.length == 0) throw new NoSuchElementException();
T largest = a[0];
for (int i = 1; i < a.length; i++)
if (largest.compareTo(a[i]) < 0)
largest = a[i];
Rozdzia 13. Kolekcje 719
Jak wyglda sytuacja w listach powizanych? Nie istnieje w nich szybki mechanizm dostpu
swobodnego, ale mona uy iteratora:
if (l.isEmpty()) throw new NoSuchElementException();
Iterator<T> iter = l.iterator();
T largest = iter.next();
while (iter.hasNext())
{
T next = iter.next();
if (largest.compareTo(next) < 0)
largest = next;
}
Pisanie tych ptli jest uciliwe, a ponadto s one podatne na bdy. atwo popeni bd
pomyki o jeden (ang. off-by-one error), nie wiadomo, czy ptla zadziaa prawidowo w przy-
padku pustego kontenera lub zawierajcego tylko jeden element. Perspektywa cigego testo-
wania i debugowania caego tego kodu nie jest zachcajca, ale implementacja caej masy
metod take nie napawa radoci, na przykad:
static <T extends Comparable> T max(T[] a)
static <T extends Comparable> T max(ArrayList<T> v)
static <T extends Comparable> T max(LinkedList<T> l)
W takiej sytuacji z pomoc przychodz interfejsy kolekcyjne. Pomylmy, jak powinien wygl-
da minimalny interfejs potrzebny do realizacji tego algorytmu. Dostp swobodny za pomoc
metod get i set jest operacj bardziej zoon ni zwyky dostp za pomoc iteratora. Jak prze-
konalimy si przy okazji szukania najwikszego elementu listy powizanej, do wykonania tego
zadania nie jest potrzebny dostp swobodny. Najwikszy element mona znale za pomoc
prostej iteracji po elementach. Dlatego metod max mona zaimplementowa w taki sposb,
aby przyjmowaa kady obiekt implementujcy interfejs Collection.
public static <T extends Comparable> T max(Collection<T> c)
{
if (c.isEmpty()) throw new NoSuchElementException();
Iterator<T> iter = c.iterator();
T largest = iter.next();
while (iter.hasNext())
{
T next = iter.next();
if (largest.compareTo(next) < 0)
largest = next;
}
return largest;
}
Teraz dysponujemy jedn metod, ktra znajduje najwikszy element w listach powiza-
nych, listach tablicowych i tablicach.
720 Java. Podstawy
Technika ta daje bardzo due moliwoci. W bibliotece standardowej C++ znajduje si mn-
stwo przydatnych algorytmw, z ktrych kady operuje na kolekcjach uoglnionych. Biblioteka
Javy nie jest tak bogata, ale posiada podstawowe funkcje: sortowanie, wyszukiwanie binarne
i kilka algorytmw uytkowych.
Ta metoda zakada, e elementy listy implementuj interfejs Comparable. Aby posortowa t li-
st w inny sposb, mona jako drugi parametr przekaza obiekt Comparator (komparatory
omawialimy w podrozdziale 13.2.5, Porwnywanie obiektw). Oto sposb sortowania
listy elementw:
Comparator<Item> itemComparator = new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
return a.partNumber - b.partNumber;
}
});
Collections.sort(items, itemComparator);
W tym miejscu moe si rodzi pytanie, jak metoda sort sortuje listy. Typowe algorytmy
sortujce prezentowane w ksikach o algorytmach dziaaj na tablicach i korzystaj z dostpu
swobodnego. W listach ten typ dostpu moe by bardzo wolny. Mona je jednak szybko
sortowa przy uyciu algorytmu sortowania przez scalanie (zobacz Algorithms in C++ autor-
stwa Roberta Sedgewicka, Addison-Wesley, 1998, s. 336 369). W Javie jednak nie wykorzy-
stano tej metody. W zamian wszystkie elementy listy s wrzucane do tablicy, tam sortowane
przy uyciu innej wersji algorytmu sortowania przez scalanie, a nastpnie kopiowane w upo-
rzdkowanej kolejnoci z powrotem do listy.
Rozdzia 13. Kolekcje 721
Algorytm sortowania przez scalanie stosowany w bibliotece kolekcji ustpuje nieco prdko-
ci algorytmowi quick sort, ktry jest standardowo uywany do implementacji algorytmw
sortujcych oglnego przeznaczenia. Ma on jednak pewn wan zalet jest stabilny, to
znaczy nie zamienia miejscami takich samych elementw. Po co przejmowa si kolejno-
ci identycznych elementw? Oto typowa sytuacja. Mamy list pracownikw posortowan
wedug nazwisk. Teraz sortujemy wedug wysokoci zarobkw. Stabilny algorytm sortujcy
zachowa kolejno nazwisk. Mwic inaczej, lista bdzie posortowana najpierw wedug zarob-
kw, a potem wedug nazwisk.
Poniewa kolekcje nie musz implementowa wszystkich swoich metod opcjonalnych, wszyst-
kie metody przyjmujce parametry kolekcyjne musz informowa, kiedy dana kolekcja moe
by bezpiecznie przekazana do algorytmu. Na przykad z pewnoci nie mona przekaza listy
unmodifiableList do algorytmu sort. Jakiego rodzaju list mona przekaza? Zgodnie
z dokumentacj lista musi by modyfikowalna, ale nie musi zezwala na zmian rozmiaru.
W klasie Collections dostpna jest te metoda shuffle, ktra dziaa odwrotnie do sortowa-
nia tasuje elementy listy. Na przykad:
ArrayList<Card> cards = . . .;
Collections.shuffle(cards);
Program przedstawiony na listingu 13.7 wstawia do listy tablicowej 49 obiektw typu Integer
zawierajcych liczby od 1 do 49. Nastpnie tasuje zawarto tej listy i wybiera sze pierw-
szych elementw. Na kocu sortuje pobrane wartoci i drukuje je.
import java.util.*;
/**
* Program demonstrujcy algorytmy tasowania i sortowania
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class ShuffleTest
{
public static void main(String[] args)
{
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 49; i++)
numbers.add(i);
722 Java. Podstawy
Collections.shuffle(numbers);
List<Integer> winningCombination = numbers.subList(0, 6);
Collections.sort(winningCombination);
System.out.println(winningCombination);
}
}
java.util.Collections 1.2
Warto zwrotna metody binarySearch wiksza bd rwna zeru okrela indeks pasujcego
obiektu. To znaczy c.get(i) jest rwnoznaczne z equal w porzdku porwnywania. Warto
ujemna oznacza, e element nie zosta znaleziony. Mona jednak wykorzysta j do okre-
lenia miejsca, w ktrym naleaoby ten element wstawi do kolekcji, aby zachowa porzdek
sortowania. Miejsce to jest nastpujce:
insertionPoint = -i - 1;
Nie mona jednak napisa po prostu -i, poniewa warto zero nie byaby jednoznaczna.
Innymi sowy, element w odpowiednim miejscu umieszcza ponisza procedura:
if (i < 0)
c.add(-i - 1, element);
Aby byo warte uwagi, wyszukiwanie binarne musi obsugiwa dostp swobodny. Gdyby
trzeba byo w poszukiwaniu rodkowego elementu przemierzy poow listy powizanej, to
stracilibymy wszystkie jego zalety. Dlatego algorytm binarySearch w przypadku list powi-
zanych przestawia si na przeszukiwanie liniowe.
W Java SE 1.3 nie istnia osobny interfejs dla uporzdkowanych kolekcji z szybkim
dostpem swobodnym, a metoda binarySearch implementowaa bardzo prymitywny
mechanizm sprawdzajcy, czy lista podana na wejciu rozszerza klas AbstractSequen
tialList. Poprawiono to w Java SE 1.4. Obecnie metoda ta sprawdza, czy podana
jako parametr lista implementuje interfejs RandomAccess. Jeli tak, przeprowadzane jest
wyszukiwanie binarne, w przeciwnym przypadku wyszukiwanie liniowe.
java.util.Collections 1.2
innego, zawsze musimy rozszyfrowa intencje innego programisty. Kiedy widzimy wywo-
anie metody typu Collections.max, od razu wiemy, jakie jest przeznaczenie danego frag-
mentu kodu.
java.util.Collections 1.2
list [a, r, t]. Dugo czasu dziaania tej metody to O(n), gdzie n okrela
dugo listy
static int frequency(Collection<?> c, Object o) 5.0
Zwraca liczb elementw w c, ktre s rwne obiektowi o.
boolean disjoint(Collection<?> c1, Collection<?> c2) 5.0
Zwraca warto true, jeli kolekcje nie maj wicej elementw wsplnych.
Jednak w ten sposb ograniczamy zakres ruchu wywoujcego metod opcje wyboru musz
by podane w postaci obiektu ArrayList. Jeli zdarzy si, e opcje te bd w jakim innym
kontenerze, konieczne bdzie ich przepakowanie. Znacznie lepiej byoby przyj bardziej
ogln kolekcj.
Naley sobie zada nastpujce pytanie: jaka jest najbardziej oglna metoda, ktra przyda si
w tym zastosowaniu? W tym przypadku konieczne jest tylko odwiedzenie wszystkich ele-
mentw, a do tego nadaje si podstawowy interfejs Collection. Poniej znajduje si popra-
wiona wersja metody fillMenu przyjmujca kolekcje kadego rodzaju:
void fillMenu(JMenu menu, Collection<JMenuItem> items)
{
for (JMenuItem item : items)
menu.addItem(item);
}
Teraz metod t mona wywoa przy uyciu struktury ArrayList, LinkedList bd tablicy
opakowanej przez metod opakowujc Arrays.asList.
Skoro uywanie interfejsw kolekcyjnych jako parametrw metod jest takim dobrym
rozwizaniem, czemu nie jest ono stosowane czciej w bibliotece Javy? Na przy-
kad klasa JComboBox ma dwa konstruktory:
JComboBox(Object[] items)
JComboBox(Vector<?> items)
Powodem jest po prostu czas. Biblioteka Swing zostaa utworzona przed powstaniem
biblioteki kolekcji.
726 Java. Podstawy
Pniej moemy doj do wniosku, e nie chcemy kopiowa elementw, a tylko utworzy ich
widok. Moemy tego dokona, zwracajc anonimow podklas klasy AbstractList.
List<MenuItem> getAllItems(final JMenu menu)
{
return new
AbstractList<MenuItem>()
{
public MenuItem get(int i)
{
return item.getItem(i);
}
public int size()
{
return item.getItemCount();
}
};
}
13.5.2. Wyliczenia
Stare kolekcje do przemierzania elementw uoonych liniowo wykorzystuj interfejs Enume
ration. Znajduj si w nim dwie metody: hasMoreElements i nextElement. S one wier-
nymi odpowiednikami metod hasNext i next z interfejsu Iterator.
Czasami spotyka si stare metody wymagajce wyliczenia jako parametru. Statyczna metoda
Collections.enumeration tworzy obiekt wyliczeniowy zapeniony elementami pobranymi
z kolekcji. Na przykad:
List<InputStream> streams = . . .;
SequenceInputStream in = new SequenceInputStream(Collections.enumeration(streams));
// Konstruktor SequenceInputStream wymaga na wejciu typu wyliczeniowego.
java.util.Enumeration<E> 1.0
boolean hasMoreElements()
Zwraca warto true, jeli s jeszcze jakie elementy.
E nextElement()
Zwraca kolejny element do odwiedzenia. Nie naley wywoywa tej metody, jeli
metoda hasMoreElements() zwrci warto false.
Enumeration<K> keys()
Zwraca obiekt wyliczeniowy, ktry przemierza klucze tablicy Hashtable.
728 Java. Podstawy
Enumeration<V> elements()
Zwraca obiekt wyliczeniowy, ktry przemierza elementy tablicy Hashtable.
java.util.Vector<E> 1.0
Enumeration<E> elements()
Zwraca obiekt wyliczeniowy, ktry przeglda elementy wektora.
java.util.Properties 1.0
Properties()
Tworzy pust map wasnoci.
Properties(Properties defaults)
Tworzy pust map wasnoci z zestawem wartoci domylnych.
String getProperty(String key)
Pobiera warto przypisan do wasnoci. Zwraca acuch przypisany
do okrelonego klucza. Jeli nie ma go w tablicy gwnej, szuka w tablicy
wartoci domylnych.
String getProperty(String key, String defaultValue)
Zwraca wasno z domyln wartoci, jeli klucz nie zostanie znaleziony.
Zwraca acuch przypisany do klucza lub acuch domylny, jeli acuch
nie zostanie znaleziony w mapie.
void load(InputStream in)
aduje map wasnoci ze strumienia wejciowego.
void store(OutputStream out, String commentString)
Zapisuje zawarto mapy wasnoci w strumieniu wyjciowym.
Rozdzia 13. Kolekcje 729
13.5.4. Stosy
Biblioteka standardowa od wersji 1.0 zawiera klas Stack udostpniajc powszechnie znane
metody, takie jak push i pop. Klasa ta dziedziczy jednak po klasie Vector, ktra z teoretycz-
nego punktu widzenia nie jest zadowalajca pozwala na stosowanie takich niewaciwych
stosom metod jak insert i remove, wstawiajcych i usuwajcych wartoci w dowolnym
miejscu, nie tylko na wierzchu stosu.
java.util.Stack<E> 1.0
E push(E item)
Wstawia element na stos i go zwraca.
E pop()
Usuwa i zwraca element znajdujcy si na wierzchu stosu. Nie naley uywa
tej metody na pustym stosie.
E peek()
Zwraca element z wierzchu stosu, nie usuwajc go. Nie naley wywoywa
tej metody na pustym stosie.
Klasa BitSet udostpnia wygodny interfejs do odczytu, ustawiania oraz resetowania poszcze-
glnych bitw. Interfejs ten pozwala unikn tworzenia masek bitowych i innych operacji na
bitach, ktre s konieczne przy przechowywaniu bitw w zmiennych typu int lub long.
Na przykad dla zbioru BitSet o nazwie bucketOfBits ponisza instrukcja zwrci warto
true, jeli i-ty bit jest wczony, lub false w przeciwnym przypadku.
bucketOfBits.get(i)
Szablon bitset w C++ ma tak sam funkcjonalno jak klasa BitSet w Javie.
730 Java. Podstawy
java.util.BitSet 1.0
BitSet(int initialCapacity)
Tworzy zbir bitw.
int length()
Zwraca logiczn dugo zbioru bitw 1 plus indeks najwyszego
ustawionego bitu.
boolean get(int bit)
Pobiera bit.
void set(int bit)
Ustawia bit.
void clear(int bit)
Usuwa bit.
void and(BitSet set)
Tworzy sum logiczn dwch zbiorw bitw.
void or(BitSet set)
Tworzy alternatyw logiczn dwch zbiorw.
void xor(BitSet set)
Wykonuje dziaanie logiczne XOR (lub wykluczajce) na dwch zbiorach bitw.
void andNot(BitSet set)
Usuwa wszystkie bity ze zbioru bitw, ktre s ustawione w innym zbiorze bitw.
Jest to jednak ukon w stron tradycji przedstawiamy implementacj tego algorytmu. Pre-
zentowany program zlicza wszystkie liczby pierwsze w zakresie od 2 do 2 000 000 (jest ich
148 933, a wic pewnie lepiej ich wszystkich nie drukowa).
Nie zagbiajc si zbytnio w szczegy implementacyjne tego programu, jego zadaniem jest
przejcie przez zbir bitw zawierajcy dwa miliony elementw. Najpierw wczamy wszyst-
kie bity. Nastpnie wyczamy bity bdce wielokrotnociami liczb, ktre wiadomo, e s
pierwsze. Liczby odpowiadajce bitom pozostaym po tym procesie s pierwsze. Listing 13.8
przedstawia implementacj tego programu w jzyku Java, a listing 13.9 w jzyku C++.
Rozdzia 13. Kolekcje 731
import java.util.*;
/**
* Program wykonujcy test porwnawczy na bazie algorytmu sita Eratostenesa. Oblicza wszystkie
* liczby pierwsze do 2 000 000.
* @version 1.21 2004-08-03
* @author Cay Horstmann
*/
public class Sieve
{
public static void main(String[] s)
{
int n = 2000000;
long start = System.currentTimeMillis();
BitSet b = new BitSet(n + 1);
int count = 0;
int i;
for (i = 2; i <= n; i++)
b.set(i);
i = 2;
while (i * i <= n)
{
if (b.get(i))
{
count++;
int k = 2 * i;
while (k <= n)
{
b.clear(k);
k += i;
}
}
i++;
}
while (i <= n)
{
if (b.get(i)) count++;
i++;
}
long end = System.currentTimeMillis();
System.out.println(count + " liczb pierwszych");
System.out.println((end - start) + " milisekund");
}
}
Mimo e sito nie jest dobrym testem wydajnoci, nie moglimy si oprze poku-
sie zmierzenia czasu obu implementacji algorytmu. Oto wyniki uzyskane na note-
booku ThinkPad z dwurdzeniowym procesorem 2,4 GHz, 4 GB pamici RAM i systemem
operacyjnym Linux Ubuntu 7.04:
C++ (g++ 4.6.3): 160 milisekund,
Java (Java SE 7): 84 milisekundy.
#include <bitset>
#include <iostream>
#include <ctime>
int main()
{
const int N = 2000000;
clock_t cstart = clock();
bitset<N + 1> b;
int count = 0;
int i;
for (i = 2; i <= N; i++)
b.set(i);
i = 2;
while (i * i <= N)
{
if (b.test(i))
{
count++;
int k = 2 * i;
while (k <= N)
{
b.reset(k);
k += i;
}
}
i++;
}
while (i <= N)
{
if (b.test(i))
count++;
i++;
}
cout << count << " liczb pierwszych\n" << millis << " milisekund\n";
return 0;
}
Na tym koczy si nasza podr po architekturze kolekcji w jzyku Java. Jak wida, biblioteka
w tym jzyku udostpnia bogaty zestaw klas kolekcyjnych, majcych na celu zaspokajanie
potrzeb programisty. W ostatnim rozdziale ksiki zajmiemy si bardzo wanym tematem
wspbienoci.
734 Java. Podstawy
14
Wielowtkowo
W tym rozdziale:
Czym s wtki
Przerywanie wtkw
Stany wtkw
Wasnoci wtkw
Synchronizacja
Kolejki blokujce
Kolekcje bezpieczne wtkowo
Interfejsy Callable i Future
Klasa Executors
Synchronizatory
Wtki a biblioteka Swing
Jaka jest zatem rnica pomidzy wieloma procesami a wieloma wtkami? Przede wszystkim
naley zauway, e kady proces posiada peen zestaw wasnych zmiennych, podczas gdy
wtki wspdziel dane z innymi wtkami. Brzmi to dosy ryzykownie i rzeczywicie moe
czasami sprawia problemy, o czym przekonasz si za chwil. Z drugiej strony dziki wsp-
dzieleniu zmiennych komunikacja pomidzy wtkami zachodzi sprawniej i jest atwiejsza do
zaprogramowania ni komunikacja midzyprocesowa. Ponadto wtki w niektrych systemach
operacyjnych s lejsze od procesw, to znaczy utworzenie i zniszczenie pojedynczego wtku
zajmuje mniej czasu ni uruchomienie nowego procesu.
Rysunek 14.1.
Jednowtkowa
animacja
odbijajcej
si piki
Wywoanie metody Thread.sleep nie powoduje utworzenia nowego wtku, poniewa metoda
ta wstrzymuje dziaanie biecego wtku na okrelon liczb milisekund.
Metoda sleep moe zgosi wyjtek InterruptedException. Zajmiemy si nim dalej. Na razie
jeli wystpi ten wyjtek, po prostu koczymy odbijanie piki.
Po uruchomieniu programu pika bardzo adnie odbija si od cian, ale niestety cakowicie
pochania aplikacj. Jeli zechcemy zatrzyma odbijanie przed wykonaniem przez pik tysica
ruchw i naciniemy w tym celu przycisk Zamknij, nic si nie stanie. Pika bdzie dalej si
odbija, poniewa nie mona robi w programie nic innego, dopki nie zakoczy si jedno
zadanie.
Mieci si ona w metodzie addBall klasy BounceFrame. Jest to bardzo dziwna technika
programowania normalnie wywoano by metod repaint, a reszt zajaby si biblio-
teka AWT. Jeli jednak wywoamy metod repaint w tym programie, panel nie zostanie
nigdy odwieony, poniewa metoda addBall cakowicie zaja wszystkie procesy. Dodat-
kowo warto zauway, e komponent piki dziedziczy po klasie JPanel, dziki czemu atwiej
jest wyczyci to. W nastpnym programie, w ktrym pooenie piki jest obliczane
w osobnym wtku, wrcimy do znanej nam metody repaint z klasy JComponent.
Oczywicie funkcjonalno tego programu nie jest zbyt bogata. Z pewnoci nie chcieliby-
my, aby programy wykonujce czasochonne zadania dziaay w ten sposb. Na przykad
podczas pobierania danych z sieci bardzo czsto zdarza si nam utkn w zadaniu, ktre
chcielibymy przerwa. Wyobramy sobie na przykad, e pobieramy duy obraz i po zoba-
czeniu jego fragmentu wiemy ju, e nie chcemy oglda reszty. W takiej sytuacji przydaje
si przycisk Stop lub Wstecz, ktry przerywa proces adowania. W kolejnym podrozdziale
nauczysz si pozostawia kontrol w rkach uytkownika dziki uruchamianiu najwaniej-
szych czci kodu w osobnym wtku.
package bounce;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
738 Java. Podstawy
/**
* Wywietla animowan pik
* @version 1.33 2007-05-17
* @author Cay Horstmann
*/
public class Bounce
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca komponent piki i przyciski
*/
class BounceFrame extends JFrame
{
private BallComponent comp;
public static final int STEPS = 1000;
public static final int DELAY = 3;
/**
* Tworzy ramk z komponentem zawierajcym odbijajc si pik oraz przyciskami
* Start i Zamknij.
*/
public BounceFrame()
{
setTitle("Pika");
pack();
}
/**
* Dodaje przycisk do kontenera.
* @param c kontener
* @param title tytu przycisku
* @param listener suchacz akcji przycisku
*/
public void addButton(Container c, String title, ActionListener listener)
{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}
/**
* Dodaje odbijajc si pik do panelu i odbija j 1000 razy
*/
public void addBall()
{
try
{
Ball ball = new Ball();
comp.add(ball);
import java.awt.geom.*;
/**
* Pika, ktra porusza si i odbija od krawdzi prostokta
* @version 1.33 2007-05-17
* @author Cay Horstmann
*/
public class Ball
{
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private double x = 0;
private double y = 0;
private double dx = 1;
740 Java. Podstawy
private double dy = 1;
/**
* Przesuwa pik do nastpnego pooenia, odwracajc kierunek, jeli pika uderzy w krawd
*/
public void move(Rectangle2D bounds)
{
x += dx;
y += dy;
if (x < bounds.getMinX())
{
x = bounds.getMinX();
dx = -dx;
}
if (x + XSIZE >= bounds.getMaxX())
{
x = bounds.getMaxX() - XSIZE;
dx = -dx;
}
if (y < bounds.getMinY())
{
y = bounds.getMinY();
dy = -dy;
}
if (y + YSIZE >= bounds.getMaxY())
{
y = bounds.getMaxY() - YSIZE;
dy = -dy;
}
}
/**
* Ustawia pik w jej aktualnym pooeniu
*/
public Ellipse2D getShape()
{
return new Ellipse2D.Double(x, y, XSIZE, YSIZE);
}
}
import java.awt.*;
import java.util.*;
import javax.swing.*;
/**
* Komponent rysujcy piki
* @version 1.34 2012-01-26
* @author Cay Horstmann
*/
public class BallComponent extends JPanel
{
private static final int DEFAULT_WIDTH = 450;
private static final int DEFAULT_HEIGHT = 350;
Rozdzia 14. Wielowtkowo 741
/**
* Dodaje pik do komponentu
* @param b pika, ktra ma zosta dodana
*/
public void add(Ball b)
{
balls.add(b);
}
java.lang.Thread 1.0
Poniej przedstawiamy prosty zestaw czynnoci, ktre naley wykona, aby uruchomi zadanie
w osobnym wtku:
1. Kod zadania umie w metodzie run klasy implementujcej interfejs Runnable.
Ten bardzo prosty interfejs posiada tylko jedn metod:
742 Java. Podstawy
4. Uruchom wtek:
t.start();
Kade kliknicie przycisku Start powoduje, e metoda addBall uruchamia nowy wtek (zobacz
rysunek 14.2):
Rozdzia 14. Wielowtkowo 743
Rysunek 14.2.
Uruchomienie
kilku wtkw
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Wywietla animowane piki
* @version 1.33 2007-05-17
* @author Cay Horstmann
*/
public class BounceThread
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new BounceFrame();
frame.setTitle("BounceThread");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Klasa implementujca interfejs Runnable i tworzca animacj piki
*/
744 Java. Podstawy
/**
* Tworzy obiekt Runnable
* @param aBall pika
* @param aComponent komponent, w ktrym odbija si pika
*/
public BallRunnable(Ball aBall, Component aComponent)
{
ball = aBall;
component = aComponent;
}
/**
* Ramka z panelem i przyciskami
*/
class BounceFrame extends JFrame
{
private BallComponent comp;
/**
* Tworzy ramk z komponentem zawierajcym pik i przyciski Start oraz Zamknij
*/
public BounceFrame()
{
comp = new BallComponent();
add(comp, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start", new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
addBall();
}
});
Rozdzia 14. Wielowtkowo 745
/**
* Dodaje przycisk do kontenera
* @param c kontener
* @param title tytu przycisku
* @param listener suchacz akcji przycisku
*/
public void addButton(Container c, String title, ActionListener listener)
{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}
/**
* Dodaje pik do obszaru roboczego i uruchamia wtek wykonujcy kod odpowiedzialny za jej odbijanie
*/
public void addBall()
{
Ball b = new Ball();
comp.add(b);
Runnable r = new BallRunnable(b, comp);
Thread t = new Thread(r);
t.start();
}
}
Wtedy naley utworzy obiekt powstaej podklasy i wywoa na jego rzecz metod start.
Podejcie to jest jednak obecnie odradzane. Zadanie, ktre ma by uruchamiane, powinno
by oddzielone od mechanizmu je uruchamiajcego. W przypadku duej liczby zada two-
rzenie osobnego wtku dla kadego z nich moe by zbyt kosztowne. W takiej sytuacji
naley uy puli wtkw zobacz podrozdzia 14.9, Klasa Executors.
746 Java. Podstawy
Nie wywouj metody run z klasy Thread lub obiektu typu Runnable. Bezporednie
wywoanie tej metody powoduje jedynie wykonanie zadania w tym samym wtku
aden nowy wtek nie jest uruchamiany. W zamian naley wywoywa metod
Thread.start. Tworzy ona nowy wtek, ktry uruchamia metod run.
java.lang.Thread 1.0
Thread(Runnable target)
Tworzy nowy wtek, ktry wywouje metod run() okrelonego obiektu target.
void start()
Uruchamia wtek, wywoujc metod run(). Ta metoda zwraca warto
natychmiast. Nowy wtek dziaa rwnolegle.
void run()
Wywouj metod run obiektu implementujcego interfejs Runnable.
java.lang.Runnable 1.0
void()
Metod t naley przedefiniowa, wstawiajc do niej instrukcje zadania,
ktre ma zosta wykonane.
Istnieje sposb na zmuszenie wtku do zamknicia. Suy do tego metoda interrupt, ktra
wysya danie zamknicia wtku.
Wywoanie metody interrupt na rzecz wtku powoduje ustawienie jego statusu przerwa-
nia (ang. interrupted state). Jest to zmienna logiczna obecna w kadym wtku. Kady wtek
powinien co jaki czas sprawdza, czy nie zosta przerwany.
Aby sprawdzi, czy status przerwania zosta ustawiony, naley najpierw pobra biecy wtek
za pomoc metody Thread.currentThread, a nastpnie wywoa metod isInterrupted:
while (!Thread.currentThread().isInterrupted() && wicej instrukcji)
{
dodatkowe dziaania
}
Rozdzia 14. Wielowtkowo 747
Statusu przerwania nie mona jednak sprawdzi, jeli wtek jest zablokowany. W takiej
sytuacji do gry wchodzi wyjtek InterruptedException. Kiedy metoda interrupt jest wywo-
ywana na rzecz wtku, ktry blokuj metody takie jak sleep czy wait, wywoania blokujce
zostaj zakoczone przez wyjtek InterruptedException (istniej metody blokujce wej-
cia-wyjcia, ktrych nie mona przerwa; w takiej sytuacji naley rozway uycie ich
przerywalnych zamiennikw szczegowe informacje na ten temat znajduj si w roz-
dziaach 1. i 3. drugiego tomu).
Nie istnieje aden wymg formalny, e wtek, ktry zosta przerwany, musi zosta zamknity.
Przerwanie wtku powoduje jedynie zwrcenie jego uwagi, a decyzja, jak na to zareagowa,
naley do niego samego. Niektre bardzo wane wtki powinny obsuy wyjtek i konty-
nuowa prac. Czsto jednak przerwanie jest przez wtek interpretowane jako danie zamkni-
cia. Metoda run takiego wtku wyglda nastpujco:
public void run()
{
try
{
. . .
while (!Thread.currentThread().isInterrupted() && dodatkowe instrukcje)
{
instrukcje
}
}
catch(InterruptedException e)
{
// Wtek zosta przerwany, bdc w stanie upienia bd oczekiwania.
}
finally
{
czyszczenie w razie potrzeby
}
// Wyjcie z metody run powoduje zakoczenie wtku.
}
{
// Wtek zosta przerwany w czasie upienia.
}
finally
{
Czyszczenie w razie potrzeby
}
// Wyjcie z metody run powoduje zamknicie wtku.
}
Nie naley tego robi! Jeli nie masz adnego dobrego pomysu na klauzul catch, masz
jeszcze dwa inne dobre wyjcia:
W klauzuli catch umie instrukcj Thread.currentThread().interrupt(),
ktra ustawi status przerwania. Dziki temu wywoujcy bdzie mg sprawdzi
wyjtek.
void mySubTask()
{
. . .
try { sleep(delay); }
catch (InterruptedException e) { Thread().currentThread().interrupt(); }
. . .
}
java.lang.Thread 1.0
void interrupt()
Wysya danie przerwania do wtku. Status przerwania zostaje ustawiony na true.
Jeli wtek jest aktualnie zablokowany przez metod sleep, zgaszany jest wyjtek
InterruptedException.
static boolean interrupted()
Sprawdza, czy biecy wtek (to znaczy ten, ktry wykonuje t instrukcj)
nie zosta przerwany. Naley zauway, e jest to metoda statyczna. Jej wywoanie
ma jeden efekt uboczny zeruje status przerwania biecego wtku na warto
false.
boolean isInterrupted()
Sprawdza, czy wtek nie zosta przerwany. W przeciwiestwie do statycznej
metody interrupted, ta nie zmienia statusu przerwania wtku.
static Thread currentThread()
Zwraca obiekt Thread reprezentujcy aktualnie wykonywany wtek.
Po uruchomieniu wtek nie musi dziaa cay czas. Zaleca si nawet wstrzymywanie dziaaj-
cych wtkw co jaki czas, aby da szans na dziaanie innym wtkom. Szczegowa kon-
trola harmonogramu wykonywania wtkw zaley od usug udostpnianych przez system
operacyjny. Systemy planowania wywaszczajcego wtkw przydzielaj kademu wyko-
nywalnemu wtkowi okrelon ilo czasu na wykonanie zadania. Kiedy czas mija, system
operacyjny wywaszcza wtek i przydziela czas innemu wtkowi (zobacz rysunek 14.4).
Przy wybieraniu kolejnego wtku system operacyjny kieruje si priorytetami wtkw
zobacz podrozdzia 14.4.1, Priorytety wtkw.
Naley zawsze pamita, e wykonywalny wtek moe w danej chwili by uruchomiony lub
nie (dlatego stan ten nazwano RUNNABLE, co oznacza wykonywalny, zamiast na przykad
RUNNING, co znaczyoby wykonywany).
Rysunek 14.3 przedstawia stany, w ktrych moe si znale wtek, oraz moliwe przejcia
pomidzy poszczeglnymi stanami. Kiedy wtek zostanie zablokowany lub przejdzie w stan
oczekiwania (bd zostanie zakoczony), planowane jest uruchomienie kolejnego wtku. Kiedy
wtek jest reaktywowany (poniewa skoczy si jego czas oczekiwania lub zaj blokad),
algorytm planujcy sprawdza, czy ma on wyszy priorytet ni aktualnie dziaajce wtki.
Jeli tak, wywaszcza jeden z uruchomionych wtkw i uruchamia nowy wtek.
Rysunek 14.3.
Stany wtkw
752 Java. Podstawy
Do zamykania wtkw suy metoda stop. Wyrzuca ona bd ThreadDeath, ktry zabija wtek.
Metoda ta jest jednak odradzana, dlatego nie powinno si jej ju uywa.
java.lang.Thread 1.0
void join()
Oczekuje na zamknicie okrelonego wtku.
void join(long millis)
Czeka na zamknicie okrelonego wtku albo na upyw okrelonej liczby
milisekund.
Thread.State getState() 5.0
Zwraca stan wtku: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING lub TERMINATED.
void stop()
Zatrzymuje wtek. Metoda ta jest odradzana.
void suspend()
Zawiesza wykonywanie wtku. Metoda odradzana.
void resume()
Ponownie uaktywnia wtek. Metoda ta moe dziaa tylko po wywoaniu metody
suspend(). Metoda odradzana.
Kiedy algorytm planujcy musi wybra nowy wtek, decyduje si na ten o najwyszym
priorytecie. Naley jednak pamita, e priorytety wtkw s w duym stopniu uzalenio-
ne od systemu. Jeli maszyna wirtualna korzysta z implementacji wtkw lecej u podo-
a platformy, priorytety Javy s odwzorowywane na poziomy platformy, ktra moe dys-
ponowa wiksz lub mniejsz liczb poziomw priorytetu.
Na przykad system Windows wyrnia siedem poziomw priorytetu, a wic niektre z prio-
rytetw Javy zostan odwzorowane na tym samym poziomie priorytetw systemowych.
W maszynie wirtualnej Javy firmy Sun dla systemu Linux priorytety wtkw s cakiem
ignorowane wszystkie wtki maj taki sam priorytet.
java.lang.Thread 1.0
Nie ma w nim jednak nic demonicznego. Demon to taki wtek, ktrego istnienie polega na
sueniu innym wtkom. Nale do nich wtki zegarowe, ktre wysyaj w rwnych odst-
pach czasu tyknicia zegara do innych wtkw, lub takie, ktre usuwaj przestarzae obiekty
z pamici podrcznej. Jeli w programie pozostan same demony, maszyna wirtualna zostaje
zamknita, poniewa nie ma sensu kontynuowa dziaania programu, w ktrym nie ma nic
poza demonami.
java.lang.Thread 1.0
Nie ma jednak klauzuli catch, do ktrej wyjtek taki mona by byo przesa. Zamiast tego
bezporednio przed zamkniciem wtku wyjtek jest przekazywany do procedury obsugi nie-
przechwyconych wyjtkw.
Jeli domylna procedura obsugi nie zostanie zainstalowana, bdzie ona null. Jeli jednak
zabraknie procedury obsugi dla konkretnego wtku, bdzie ni jego obiekt typu ThreadGroup.
Dane ze ledzenia stosu z pewnoci kady widzia ju wiele razy w swoich programach.
java.lang.Thread 1.0
java.lang.Thread.UncaughtExceptionHandler 5.0
java.lang.ThreadGroup 1.0
14.5. Synchronizacja
W wikszoci aplikacji wielowtkowych znajdujcych praktyczne zastosowanie jeden zestaw
danych jest wspdzielony przez co najmniej dwa wtki. Co si stanie, jeli dwa wtki majce
dostp do tego samego obiektu wywoaj metod zmieniajc jego stan? Jak pewnie si
domylasz, wtki mog sobie wzajemnie przeszkadza. Zalenie od kolejnoci dostpu do
danych, w opisanych wyej sytuacjach mog powstawa uszkodzone obiekty. Sytuacje te
nazywa si wycigami (ang. race condition).
Kolejny przykadowy program jest symulacj banku, w ktrym zaoono kilka kont. Przepro-
wadzamy rne losowe transakcje majce na celu przemieszczenie pienidzy pomidzy tymi
kontami. Kade konto dysponuje wasnym wtkiem. Kada transakcja przelewa losowo okre-
lon ilo pienidzy z jednego konta na inne losowo wybrane konto.
Kod symulacji jest bardzo prosty. Zawiera klas Bank z metod transfer, ktra przelewa
rodki pienine pomidzy kontami (na razie nie przejmujemy si tym, e saldo na koncie
moe sta si ujemne). Oto kod metody transfer z klasy Bank:
public void transfer(int from, int to, double amount)
// Ostrzeenie: metoda niebezpieczna, jeli wywoywana w kilku wtkach.
{
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
}
Poniej znajduje si kod klasy TransferRunnable. Jej metoda run przelewa pienidze z okre-
lonego konta bankowego. W kadej iteracji metoda ta losowo wybiera jedno konto docelowe
i sum pienidzy do przelania, wywouje metod transfer na rzecz obiektu banku i prze-
chodzi w stan upienia.
class TransferRunnable implements Runnable
{
. . .
public void run()
{
try
{
int toAccount = (int) (bank.size() * Math.random());
double amount = maxAmount * Math.random();
bank.transfer(fromAccount, toAccount, amount);
Thread.sleep((int) (DELAY * Math.random()));
Rozdzia 14. Wielowtkowo 757
}
catch(InterruptedException e) {}
}
}
W adnym momencie dziaania symulacji nie wiadomo, ile jest pienidzy na kadym z kont.
Wiadomo natomiast, e oglna suma nie powinna si zmienia, poniewa program tylko
przelewa rodki pomidzy rnymi kontami.
Na kocu kadej transakcji metoda transfer oblicza sum pienidzy dostpnych na wszyst-
kich kontach i drukuje wynik.
Program ten nigdy si nie koczy. Aby go zamkn, naley nacisn kombinacj klawiszy
Ctrl+C.
Jak wida, program zawiera powany bd. Przez kilka transakcji saldo czne wszystkich
rachunkw wynosi 100 000 dolarw, co jest prawidow kwot, zwaywszy, e jest 100 kont
po 1000 dolarw. Jednak po jakim czasie saldo ulega nieznacznej zmianie. Bdy w oblicze-
niach mog si pojawi na krtko po uruchomieniu programu lub dopiero po duszym czasie.
Taka sytuacja nie napawa optymizmem i z pewnoci nikt nie chciaby zoy w tym banku
swoich ciko zarobionych pienidzy.
Listingi od 14.5 do 14.7 przedstawiaj kompletny kod rdowy omawianego programu. Spr-
buj odszuka bd w kodzie, a rozwizanie zagadki znajdziesz w kolejnym podrozdziale.
/**
* Program demonstrujcy zniszczenie danych spowodowane dostpem kilku wtkw do struktury
danych
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class UnsynchBankTest
758 Java. Podstawy
{
public static final int NACCOUNTS = 100;
public static final double INITIAL_BALANCE = 1000;
package unsynch;
/**
* Bank z kilkoma kontami
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class Bank
{
private final double[] accounts;
/**
* Tworzy bank.
* @param n liczba kont
* @param initialBalance saldo pocztkowe na kadym koncie
*/
public Bank(int n, double initialBalance)
{
accounts = new double[n];
for (int i = 0; i < accounts.length; i++)
accounts[i] = initialBalance;
}
/**
* Przelewa pienidze pomidzy kontami.
* @param from konto, z ktrego ma nastpi przelew
* @param to konto, na ktre maj zosta przelane rodki
* @param amount kwota do przelania
*/
public void transfer(int from, int to, double amount)
{
if (accounts[from] < amount) return;
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
}
Rozdzia 14. Wielowtkowo 759
/**
* Zwraca sum sald wszystkich kont.
* @return saldo oglne
*/
public double getTotalBalance()
{
double sum = 0;
return sum;
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
public int size()
{
return accounts.length;
}
}
/**
* Obiekt Runnable przelewajcy pienidze z jednego konta bankowego na inne
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class TransferRunnable implements Runnable
{
private Bank bank;
private int fromAccount;
private double maxAmount;
private int DELAY = 10;
/**
* Tworzy obiekt Runnable do przelewania rodkw
* @param b bank, na ktrego kontach wykonywany jest przelew
* @param from konto, z ktrego maj by przelane pienidze
* @param max maksymalna suma, jaka moe zosta przelana za kadym razem
*/
public TransferRunnable(Bank b, int from, double max)
{
bank = b;
fromAccount = from;
maxAmount = max;
}
while (true)
{
int toAccount = (int) (bank.size() * Math.random());
double amount = maxAmount * Math.random();
bank.transfer(fromAccount, toAccount, amount);
Thread.sleep((int) (DELAY * Math.random()));
}
}
catch (InterruptedException e)
{
}
}
}
14.5.2. Wycigi
W poprzednim podrozdziale napisalimy program, w ktrym kilka wtkw aktualizowao
salda na kontach bankowych. Po jakim czasie wkrada si bd, ktry powodowa poja-
wienie si lub zniknicie pewnej kwoty pienidzy. Problem ten wystpowa w sytuacjach,
w ktrych dwa wtki rwnoczenie prboway zaktualizowa jedno konto. Wyobramy sobie,
e dwa wtki w tej samej chwili wykonuj ponisz instrukcj:
accounts[to] += amount;
Problem polega na tym, e nie s to operacje niepodzielne. Instrukcja ta moe zosta wyko-
nana w nastpujcy sposb:
1. Zaadowanie accounts[to] do rejestru.
2. Dodanie wartoci amount.
Wyobramy sobie teraz, e pierwszy z wtkw wykonuje dwa pierwsze kroki i zostaje
wywaszczony. Nastpnie budzi si drugi wtek, ktry aktualizuje t sam pozycj w tablicy
accounts. Potem budzi si pierwszy wtek i koczy dziaanie, wykonujc krok trzeci.
Ta czynno wymazuje zmiany dokonane przez drugi wtek, w wyniku czego zmienia si
oglna suma (zobacz rysunek 14.4).
Nasz program testowy wykrywa ten bd (oczywicie istnieje niewielkie ryzyko faszywego
alarmu, ktry moe nastpi w sytuacji, gdy zostanie zakcona praca wtku przeprowadza-
jcego test).
Jakie jest ryzyko wystpienia tego bdu? Zwikszylimy je, przeplatajc instrukcje druko-
wania z instrukcjami aktualizujcymi saldo.
Jeli usuniemy instrukcje drukowania, ryzyko znacznie si zmniejszy, poniewa kady wtek
przed zaniciem bdzie wykonywa bardzo mao pracy, a poza tym jest mao prawdopodobne,
aby algorytm planujcy wywaszczy wtek w trakcie wykonywania oblicze. Nie znaczy to
jednak, e ryzyka wystpienia bdu nie ma ju w ogle. Jeli na powanie obcionej maszynie
Rozdzia 14. Wielowtkowo 761
Rysunek 14.4.
Jednoczesny
dostp
przez dwa wtki
uruchomimy bardzo duo wtkw, program nadal bdzie robi bdy i nie pomoe usunicie
instrukcji drukujcych. Na wystpienie bdw moe przyj nam czeka kilka minut, godzin,
a nawet dni. Szczerze mwic, w yciu programisty jest niewiele gorszych rzeczy od bdu,
ktry daje o sobie zna tylko raz na kilka dni.
Istot problemu jest to, e dziaanie metody transfer moe zosta przerwane w rodku operacji.
Gdybymy zapewnili ukoczenie metody przed utrat przez wtek kontroli, stan obiektu konta
bankowego byby niezagroony przez bdy.
762 Java. Podstawy
Szkielet konstrukcji chronicej blok kodu przy uyciu klasy ReentrantLock ma nastpujc
posta:
myLock.lock(); // Obiekt klasy ReentrantLock.
try
{
sekcja krytyczna
}
finally
{
Dostp do sekcji krytycznej powyszej instrukcji w jednym czasie moe mie tylko jeden
wtek. Kiedy jeden wtek zablokuje obiekt blokady, aden inny wtek nie bdzie mg przej
przez instrukcj lock. Jeli jaki inny wtek wywoa metod lock, zostanie dezaktywowany
do czasu, a poprzedni wtek odblokuje obiekt blokady.
Metoda unlock musi si bezwzgldnie znajdowa w bloku finally. Jeli kod w sek-
cji krytycznej spowoduje wyjtek, blokada musi zosta zdjta. W przeciwnym przy-
padku reszta wtkw pozostanie zablokowana na zawsze.
Z blokadami nie mona uywa instrukcji try z zasobami. Przede wszystkim metoda
zdejmujca blokad nie nazywa si close. Jednak nawet gdyby zmieniono jej nazw,
to instrukcja try z zasobami i tak by nie dziaaa. W jej nagwku powinna si znale
deklaracja nowej zmiennej, a w blokadzie uywa si jednej zmiennej, z ktrej korzystaj
rne wtki.
{
bankLock.lock();
try
{
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
}
finally
{
bankLock.unlock();
}
}
}
Zamy, e jaki wtek wywouje metod transfer, ale zostaje wywaszczony przed jej
ukoczeniem. Nastpnie inny wtek rwnie wywouje t metod. Nie moe on jednak zaoy
blokady i zostaje zablokowany wywoaniem metody lock. Jest dezaktywowany i musi pocze-
ka, a pierwszy wtek skoczy wykonywanie metody transfer. Kiedy ten zdejmie blokad,
drugi wtek moe kontynuowa (zobacz rysunek 14.5).
Rysunek 14.5.
Porwnanie wtkw
synchronizowanych
i niesynchronizowanych
Wyprbuj, czy to dziaa. Dodaj kod blokujcy do metody transfer i ponownie uruchom
program, a przekonasz si, e saldo bankowe nie zmieni si bez wzgldu na dugo czasu
dziaania programu.
Naley zauway, e kady obiekt klasy Bank posiada wasny obiekt klasy ReentrantLock.
Jeli dwa wtki prbuj uzyska dostp do tego samego obiektu Bank, blokada ustawia je
w kolejce. Jeli natomiast kady z wtkw dobiera si do innego obiektu Bank, zakadaj one
osobne blokady i aden z nich nie jest blokowany. Jest to jak najbardziej prawidowe dzia-
anie, poniewa wtki dziaajce na rnych obiektach nie mog sobie przeszkadza.
764 Java. Podstawy
Blokada ta jest wielowejciowa (ang. reentrant), poniewa wtek moe wielokrotnie zakada
blokad, ktr ju posiada. Blokada posiada licznik pamitajcy liczb zagniedonych
wywoa metody lock. Dlatego, aby blokada zostaa zwolniona, wtek musi wywoa tyle razy
metod unlock, ile razy wywoa metod lock. Dziki temu kod chroniony przez blokad
moe wywoa inn metod, ktra wykorzystuje te same blokady.
Z reguy ochron obejmuje si bloki kodu, ktre aktualizuj lub badaj wspdzielone obiekty.
Daje to pewno, e operacja zostanie zakoczona, zanim inny wtek bdzie mg uy tego
samego obiektu.
Trzeba uwaa, aby procedury zawarte w sekcji krytycznej nie zostay pominite
z powodu wystpienia wyjtku. Jeli wyjtek wystpi przed kocem sekcji, klauzula
finally zwolni blokad, ale obiekt moe pozosta w naruszonym stanie.
java.util.concurrent.Locks.Lock 5.0
void lock()
Zakada blokad. Zostaje zablokowana, jeli blokada ta jest aktualnie w posiadaniu
innego wtku.
void unlock()
Zwalnia blokad.
java.util.concurrent.locks.ReentrantLock 5.0
ReentrantLock()
Tworzy wielowejciow blokad, za pomoc ktrej mona chroni sekcj krytyczn.
ReentrantLock(boolean fair)
Tworzy obiekt blokady z okrelon zasad uczciwoci. Uczciwa blokada ustawia
na pierwszym miejscu wtek, ktry czeka najduej. Jednak zasada ta moe
powodowa due straty szybkoci. Dlatego domylnie blokady nie musz
by uczciwe.
14.5.4. Warunki
Czsto zdarza si tak, e po wejciu do sekcji krytycznej wtek dowiaduje si, i nie moe
kontynuowa, dopki nie zostanie speniony warunek. Do zarzdzania wtkami, ktre uzy-
skay blokad, ale nie mog robi nic poytecznego, su obiekty warunkw. W tym roz-
dziale opisujemy implementacj warunkw w bibliotece Javy (ze wzgldu na przeszo
obiekty warunkw s czasami nazywane zmiennymi warunkowymi).
Ulepszymy nasz symulacj banku. Nie chcemy, aby pienidze byy przelewane z kont, na kt-
rych nie ma wystarczajcych rodkw. Zauwa, e nie moemy uy instrukcji jak poniej:
if (bank.getBalance(from) >= amount)
bank.transfer(from, to, amount);
Zanim wtek zostanie ponownie uruchomiony, saldo na koncie moe spa poniej minimal-
nej potrzebnej kwoty. Naley przypilnowa, aby aden wtek nie zmodyfikowa salda pomi-
dzy testem a wykonaniem przelewu. Dlatego zarwno test, jak i operacj przelewu chronimy
przy uyciu blokady:
public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] < amount)
{
// czekanie
. . .
}
// przelew rodkw
. . .
}
finally
{
bankLock.unlock();
}
}
Kolej na podjcie decyzji, co zrobi, jeli na koncie bdzie za mao pienidzy. W takiej
sytuacji czekamy, a jaki inny wtek zwikszy jego saldo. Pamitamy jednak, e pierwszy
wtek cakowicie zablokowa dostp do obiektu bankLock, przez co aden inny wtek nie
moe dokona depozytu. W takim przypadku do gry wchodz obiekty warunkw.
Z obiektem blokady moe by zwizanych nawet kilka warunkw. Obiekty warunkw tworzy
si za pomoc metody newCondition. Istnieje zwyczaj nadawania obiektom warunkw takich
766 Java. Podstawy
Dziki temu aktualny wtek zostaje dezaktywowany i nastpuje zdjcie blokady. To umo-
liwia dziaanie kolejnemu wtkowi, ktry, mamy nadziej, zwikszy saldo konta.
Pomidzy wtkiem, ktry oczekuje na blokad, a wtkiem, ktry wywoa metod await,
istnieje zasadnicza rnica. Ten drugi zostaje umieszczony w kolejce wtkw oczekujcych
(ang. wait set) warunku. Wtek ten nie przechodzi w stan wykonywalnoci, dopki inny
wtek nie wywoa metody signalAll na rzecz tego samego warunku.
W tym momencie wtek powinien ponownie sprawdzi warunek. Nie ma gwarancji, e teraz
zostanie on speniony. Metoda signalAll tylko sygnalizuje wtkom, e tym razem warunek
moe zosta speniony i dlatego dobrze by byo to sprawdzi.
Wane jest, aby metoda signalAll bya wywoywana take przez jaki inny wtek, poniewa
wtek wywoujcy metod await nie ma moliwoci reaktywowania samego siebie. Musi on
liczy na inne wtki. Jeli aden z nich go nie reaktywuje, nie zostanie on nigdy wicej uru-
chomiony. To moe prowadzi do nieprzyjemnych zakleszcze (ang. deadlock). Jeli pra-
wie wszystkie wtki zostan zablokowane, a ostatni aktywny wtek wywoa metod await,
nie odblokowujc reszty, nie bdzie komu zdj blokady i program zawiesi si.
Rozdzia 14. Wielowtkowo 767
Kiedy powinno si wywoywa metod signalAll? Gwna regua nakazuje zrobienie tego
zawsze wtedy, gdy stan obiektu zmieni si w taki sposb, ktry moe by korzystny dla wt-
kw oczekujcych. Na przykad wtki powinny mie moliwo sprawdzenia salda na kon-
cie za kadym razem, gdy ulegnie ono zmianie. W naszym przykadowym programie metod
signalAll wywoujemy po zakoczeniu przelewu pienidzy.
public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] < amount)
wystSrodki.await();
// przelew rodkw
. . .
wystSrodki.signalAll();
}
finally
{
bankLock.unlock();
}
}
Istnieje take metoda signal, ktra odblokowuje tylko jeden losowo wybrany wtek. Jest to
mniej obciajca czynno ni odblokowywanie wszystkich wtkw, ale wie si z ni
pewne ryzyko. Jeli losowo wybrany wtek dojdzie do wniosku, e nadal nie moe nic
zrobi, zostanie z powrotem zablokowany. Jeli aden inny wtek nie wywoa metody signal
jeszcze jeden raz, system ulegnie zakleszczeniu.
import java.util.concurrent.locks.*;
/**
* Bank z kilkoma kontami, kontrolujcy dostp za pomoc blokad
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
768 Java. Podstawy
/**
* Tworzy bank
* @param n liczba kont
* @param initialBalance saldo pocztkowe na kadym koncie
*/
public Bank(int n, double initialBalance)
{
accounts = new double[n];
for (int i = 0; i < accounts.length; i++)
accounts[i] = initialBalance;
bankLock = new ReentrantLock();
sufficientFunds = bankLock.newCondition();
}
/**
* Przelewa pienidze pomidzy kontami.
* @param from konto, z ktrego ma nastpi przelew
* @param to konto, na ktre maj zosta przelane rodki
* @param amount kwota do przelania
*/
public void transfer(int from, int to, double amount) throws InterruptedException
{
bankLock.lock();
try
{
while (accounts[from] < amount)
sufficientFunds.await();
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
sufficientFunds.signalAll();
}
finally
{
bankLock.unlock();
}
}
/**
* Zwraca sum sald wszystkich kont.
* @return saldo oglne
*/
public double getTotalBalance()
{
bankLock.lock();
try
{
double sum = 0;
Rozdzia 14. Wielowtkowo 769
return sum;
}
finally
{
bankLock.unlock();
}
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
public int size()
{
return accounts.length;
}
}
java.util.concurrent.locks.Lock 5.0
Condition newCondition()
Zwraca obiekt warunku zwizany z blokad.
java.util.concurrent.locks.Condition 5.0
void await()
Umieszcza wtek w kolejce oczekujcych do warunku.
void signalAll()
Odblokowuje wszystkie wtki znajdujce si w kolejce oczekujcych do warunku.
void signal()
Odblokowuje losowo wybrany wtek znajdujcy si w kolejce oczekujcych
do warunku.
Interfejsy Lock i Condition umoliwiaj programistom zyskanie wikszej kontroli nad bloka-
dami. W wikszoci sytuacji kontrola ta jest jednak zbdna, poniewa mona wykorzysta
mechanizm wbudowany w jzyk. Od wersji 1.0 kady obiekt w Javie posiada blokad
wewntrzn. Jeli w deklaracji metody zostanie uyte sowo kluczowe synchronized, blokada
obiektu chroni ca t metod. To znaczy, e aby j wywoa, wtek musi zaoy wewntrzn
blokad obiektu.
Z wewntrzn blokad obiektu jest zwizany jeden warunek. Metoda wait dodaje wtek do
kolejki oczekujcych, a metody notifyAll i notify odblokowuj oczekujce wtki. Innymi
sowy, wywoanie metody wait lub notifyAll jest rwnoznaczne z poniszym:
intrinsicCondition.await();
intrinsicCondition.signalAll();
Metody wait, notifyAll i notify s metodami finalnymi klasy Object. Aby unikn
konfliktw nazw, odpowiadajce im metody w interfejsie Condition zostay nazwane
await, signalAll i signal.
public synchronized void transfer(int from, int to, int amount) throws
InterruptedException
Rozdzia 14. Wielowtkowo 771
{
while (accounts[from] < amount)
wait(); // Oczekiwanie na warunek wewntrznej blokady obiektu.
accounts[from] -= amount;
accounts[to] += amount;
notifyAll(); // Powiadomienie wszystkich wtkw oczekujcych na warunek.
}
public synchronized double getTotalBalance() { . . . }
}
Jak wida, sowo kluczowe synchronized pozwala na pisanie znacznie bardziej zwizego
kodu. Oczywicie, aby go zrozumie, trzeba wiedzie, e kady obiekt posiada wewntrzn
blokad, ktra z kolei posiada wewntrzny warunek. Blokada zarzdza wtkami, ktre pr-
buj wej do metody synchronizowanej. Warunek zajmuje si wtkami, ktre wywoay
metod wait.
Synchronizowane mog by take metody statyczne. Jeli taka metoda zostanie wywoana,
uzyskuje dostp do blokady wewntrznej obiektu zwizanej z ni klasy. Jeli na przykad
klasa Bank zawieraaby statyczn metod synchronizowan, blokada obiektu Bank.class byaby
blokowana w chwili wywoania tej metody. W wyniku tego aden inny wtek nie mgby
wywoa tej ani adnej innej statycznej metody synchronizowanej tej klasy.
Czego najlepiej uywa obiektw Lock i Condition czy metod synchronizowanych? Oto
nasze zalecenia w tej kwestii:
Najlepiej nie uywa interfejsw Lock i Condition ani sowa kluczowego synchronized.
W wielu sytuacjach mona poradzi sobie przy uyciu jednego z mechanizmw
z pakietu java.util.concurrent, ktre zajmuj si dziaaniami zwizanymi
z blokowaniem. Na przykad w podrozdziale 14.6, Kolejki blokujce, opisujemy
sposb synchronizacji wtkw pracujcych nad wsplnym zadaniem za pomoc
blokowania kolejek.
Jeli sowo kluczowe synchronized sprawdza si w okrelonej sytuacji, naley
go uy. Technika ta pozwala na zmniejszenie liczby wierszy kodu i pozostawia
mniej okazji do popenienia bdu. Listing 14.9 przedstawia program symulujcy
bank zaimplementowany przy uyciu metod synchronizowanych.
Interfejsw Lock i Condition naley uywa, gdy nie mona si oby bez dodatkowych
funkcji, ktre one udostpniaj.
772 Java. Podstawy
package synch2;
/**
* Bank z kilkoma kontami, wykorzystujcy synchronizacj
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class Bank
{
private final double[] accounts;
/**
* Tworzy bank.
* @param n liczba kont
* @param initialBalance saldo pocztkowe na kadym koncie
*/
/**
* Przelewa pienidze pomidzy kontami.
* @param from konto, z ktrego ma nastpi przelew
* @param to konto, na ktre maj zosta przelane rodki
* @param amount kwota do przelania
*/
public synchronized void transfer(int from, int to, double amount) throws
InterruptedException
{
while (accounts[from] < amount)
wait();
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
notifyAll();
}
/**
* Zwraca sum sald wszystkich kont.
* @return saldo oglne
*/
return sum;
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
java.lang.Object 1.0
void notifyAll()
Odblokowuje wszystkie wtki, ktre wywoay metod wait na rzecz obiektu.
Metod t mona wywoywa wycznie w synchronizowanej metodzie
lub synchronizowanym bloku. Powoduje wyjtek IllegalMonitorStateException,
jeli aktualny wtek nie jest wacicielem blokady obiektu.
void notify()
Odblokowuje jeden losowo wybrany wtek spord tych, ktre wywoay metod
wait na rzecz obiektu. Metod t mona wywoywa wycznie
w synchronizowanej metodzie lub synchronizowanym bloku. Powoduje wyjtek
IllegalMonitorStateException, jeli aktualny wtek nie jest wacicielem blokady
obiektu.
void wait()
Przestawia wtek w stan oczekiwania, a nadejdzie odpowiednie powiadomienie.
Metod t mona wywoywa wycznie w synchronizowanej metodzie
lub synchronizowanym bloku. Powoduje wyjtek IllegalMonitorStateException,
jeli aktualny wtek nie jest wacicielem blokady obiektu.
void wait(long millis)
void wait(long millis, int nanos)
Przestawia wtek w stan oczekiwania, a nadejdzie odpowiednie powiadomienie
lub upynie okrelona ilo czasu. Metod t mona wywoywa wycznie
w synchronizowanej metodzie lub synchronizowanym bloku. Powoduje wyjtek
IllegalMonitorStateException, jeli aktualny wtek nie jest wacicielem blokady
obiektu.
Parametry: millis Liczba milisekund
nanos Liczba nanosekund; musi by mniejsza od 1 000 000
774 Java. Podstawy
W tym przypadku obiekt lock zosta utworzony tylko po to, aby mona byo uy blokady,
ktr posiada kady obiekt w Javie.
Mimo e metody get i set klasy Vector s synchronizowane, nic nam to nie daje. Istnieje mo-
liwo, e wtek zostanie wywaszczony w metodzie transfer po zakoczeniu pierwszego
wywoania metody get. Wtedy inny wtek moe w tym samym miejscu zapisa cakiem inn
warto. Mona jednak przej blokad:
public void transfer(Vector<Double> accounts, int from, int to, int amount)
{
synchronized (accounts)
{
accounts.set(from, accounts.get(from) - amount);
Rozdzia 14. Wielowtkowo 775
Technika ta zdaje egzamin, ale jest w peni uzaleniona od tego, e wszystkie metody modyfi-
kujce w klasie Vector maj wewntrzne blokady. Czy tak jest jednak naprawd? W doku-
mentacji tej klasy nic takiego nie napisano. Trzeba bardzo uwanie przestudiowa jej kod
rdowy i mie nadziej, e w przyszoci nie zostan wprowadzone mutatory niesynchroni-
zowane. Stanowi to dowd na to, e blokowanie po stronie klienta jest bardzo niepewn tech-
nik, i dlatego oglnie nie polecamy jej stosowania.
14.5.7. Monitor
Blokady i warunki stwarzaj bardzo due moliwoci, jeli chodzi o synchronizacj wtkw,
ale s mao obiektowe. Badacze przez wiele lat poszukiwali sposobw na uczynienie wie-
lowtkowoci bezpieczn technik, nie zmuszajc jednoczenie programistw do zajmowa-
nia si jawnymi blokadami. Jednym z najlepszych rozwiza w tej dziedzinie s monitory,
opracowane w latach 70. ubiegego wieku przez Pera Brincha Hansena i Tonyego Hoarea.
W Javie monitor ma nastpujce wasnoci:
Monitorem jest klasa posiadajca same pola prywatne.
Z kadym obiektem tej klasy zwizana jest blokada.
Wszystkie metody s blokowane przez t blokad. Innymi sowy, jeli klient wykona
instrukcj obj.method(), to blokada obiektu obj zostanie automatycznie zaoona
na pocztku wywoania metody i zwolniona w chwili zwrcenia przez t metod
wartoci. Poniewa wszystkie pola s prywatne, mamy pewno, e podczas gdy
jeden wtek wykonuje dziaania na nich, aden inny wtek nie ma do nich dostpu.
Z blokad moe by skojarzona dowolna liczba warunkw.
Projektanci Javy luno potraktowali adaptacj koncepcji monitorw. Kady obiekt w Javie
posiada wewntrzn blokad i wewntrzny warunek. Jeli w deklaracji metody znajduje si
sowo kluczowe synchronized, dziaa ona jak metoda monitorowa. Dostp do zmiennej warun-
kowej uzyskuje si za porednictwem metod wait, notifyAll i notify.
Pomidzy zwykym obiektem a monitorem s trzy istotne rnice majce wpyw na bezpie-
czestwo wtkw:
Pola nie musz by prywatne.
Metody nie musz by synchronizowane.
Blokada wewntrzna jest dostpna dla klientw.
776 Java. Podstawy
Ten brak poszanowania dla zabezpiecze rozwcieczy Pera Brincha Hansena. W zjadliwej
recenzji na temat wielowtkowoci w Javie napisa: Zdumiewa fakt, e pozbawiony za-
bezpiecze paralelizm w Javie jest powanie traktowany przez programistw, zwaszcza e
od wynalezienia monitorw i powstania jzyka Concurrent Pascal upyno ju p wieku.
To nie ma sensu (Javas Insecure Parallelism, ACM SIGPLAN Notices 1999, nr 34,
s. 38 45).
Problemy te nie wystpuj po zastosowaniu blokad do ochrony kodu, do ktrego mog mie
dostp rne wtki. Kompilatory musz respektowa blokady poprzez oprnianie w razie
potrzeby lokalnych pamici podrcznych i nieprzestawianie instrukcji w nieodpowiedni sposb.
Szczegowe informacje na ten temat mona znale w dokumencie Java Memory Model
and Thread Specification opracowanym przez grup JSR 133 (http://www.jcp.org/en/jsr/
detail?id=133). Znaczna cz tej specyfikacji jest bardzo skomplikowana i ma czysto tech-
niczny charakter, ale jest kilka janiejszych fragmentw. Bardziej przystpny artyku na ten
temat napisany przez Briana Goetza znajduje si pod adresem http://www-106.ibm.com/
developerworks/java/library/j-jtp02244.html.
Wyobramy sobie na przykad, e pewien obiekt ma znacznik logiczny done, ktry jest usta-
wiany przez jeden wtek, a sprawdzany przez inny. Zgodnie z wczeniejszymi informacjami
wiemy, e mona w tej sytuacji uy blokady:
private boolean done;
Rozdzia 14. Wielowtkowo 777
Uycie wewntrznej blokady wydaje si niezbyt dobrym pomysem. Metody isDone i setDone
mog zosta zablokowane, jeli inny wtek zablokuje obiekt. Jeli istnieje taka obawa, mona
zastosowa osobn blokad tylko dla tej zmiennej. To jednak zaczyna robi si coraz bar-
dziej kopotliwe.
Jest jeszcze jedna sytuacja, w ktrej mona bezpiecznie uzyska dostp do wsplnego pola
gdy pole to jest finalne:
final Map<String, Double> accounts = new HashMap<>();
Bez modyfikatora final nie byoby gwarancji, e wtki zobacz zaktualizowan warto
zmiennej accounts mogyby widzie null zamiast utworzonej struktury HashMap.
14.5.11. Zakleszczenia
Blokady i warunki nie wystarcz do rozwizania wszystkich problemw zwizanych z wielo-
wtkowoci. Rozwamy nastpujc sytuacj:
Konto 1: 200 dol.
Konto 2: 300 dol.
Wtek 1: przelew 300 dol. z konta 1 na konto 2
Wtek 2: przelew 400 dol. z konta 2 na konto 1
Jak wida na rysunku 14.6, wtki 1 i 2 s zablokowane. aden z nich nie moe kontynuowa
dziaania, poniewa salda na obu kontach s zbyt niskie.
Rysunek 14.6.
Zakleszczenie
Rozdzia 14. Wielowtkowo 779
W naszym programie zakleszczenie nie moe wystpi z prostego powodu. Kwota przelewu
nie moe przekracza 1000 dolarw. Poniewa jest sto kont, na ktrych w sumie znajduje si
100 000 dolarw, przynajmniej jedno z nich zawsze musi zawiera wicej ni 1000 dola-
rw. Dziki temu wtek przelewajcy pienidze z tego konta moe kontynuowa dziaanie.
Jeli jednak z metody run usuniemy ograniczenie wysokoci transakcji do 1000 dolarw,
zakleszczenia mog nastpi bardzo szybko. Mona to sprawdzi. Ustaw warto staej
NACCOUNTS na 10, a parametr max konstruktora klasy TransferRunnable na 2 * INITIAL_
BALANCE i uruchom program. Podziaa on przez jaki czas i zawiesi si.
Kiedy program zawiesi si, nacinij kombinacj klawiszy Ctrl+\, aby otrzyma list
wszystkich wtkw. Kademu wtkowi towarzyszy informacja ze stosu, dziki czemu
wiadomo, w ktrym miejscu jest on aktualnie zablokowany. Mona te uy narzdzia
jconsole, ktre zostao opisane w rozdziale 11., i sprawdzi dane na karcie Threads
(rysunek 14.7).
Rysunek 14.7.
Karta Threads
w jconsole
Oto jeszcze jedna sytuacja, w ktrej moe atwo doj do zakleszczenia: w programie Synch
BankTest zamie metod signalAll na signal. Po pewnym czasie program w kocu zawiesza
si (tym razem take lepiej ustawi warto NACCOUNTS na 10, aby efekt by szybciej zauwa-
alny). W przeciwiestwie do metody signalAll, ktra wysya powiadomienie do wszystkich
wtkw oczekujcych na zwikszenie funduszy, metoda signal odblokowuje tylko jeden
wtek. Jeli wtek ten nie moe kontynuowa, wszystkie wtki mog zosta zablokowane.
Przeanalizujmy scenariusz prezentujcy, jak moe doj do zakleszczenia.
Konto 1: 1990 dol.
Pozostae konta: po 990 dol.
Wtek 1: przelew 995 dol. z konta 1 na konto 2
Pozostae wtki: przelew 995 dol. ze swoich kont na inne konto
Bez wtpienia wszystkie wtki poza pierwszym s zablokowane, poniewa na ich kontach nie
ma wystarczajco duo pienidzy.
Wtedy wtek 1 wywouje metod signal, ktra odblokowuje jeden losowo wybrany wtek.
Zamy, e pado na wtek 3. Zostaje on obudzony, stwierdza, e na jego koncie nie ma
wystarczajco duo rodkw, i ponownie wywouje metod wait. Jednak wtek 1 cay czas
dziaa. Generuje now losow transakcj, na przykad tak jak poniej:
Wtek 1: przelew 997 dol. z konta 1 na konto 2
Tym razem take wtek 1 wywouje metod wait i wszystkie wtki s zablokowane. System
ulega zakleszczeniu.
rdem problemw jest tutaj metoda signal odblokowujca tylko jeden wtek, ktry moe
nie mie znaczenia dla utrzymania postpu programu (w naszym scenariuszu wtek 2 musi
kontynuowa prac, aby pobra pienidze z drugiego konta).
Niestety jzyk Java nie udostpnia adnego mechanizmu pozwalajcego unikn takich
zakleszcze lub je przerwa. Program musi by tak zaprojektowany, aby sytuacje tego typu
nie miay szans si wydarzy.
Rozdzia 14. Wielowtkowo 781
Przy pierwszym wywoaniu metody get w wtku nastpuje wywoanie metody initialValue.
Od tej pory metoda get zwraca egzemplarz nalecy do biecego wtku.
Podobny problem przedstawia generowanie liczb losowych w wielu wtkach. Klasa java.
util.Random jest bezpieczna wtkowo, ale mimo to nie dziaa wydajnie, gdy wiele wtkw
musi czeka na jeden wsplny generator.
java.lang.ThreadLocal<T> 1.2
T get()
Pobiera biec warto wtku. Jeli metoda get jest wywoywana po raz pierwszy,
warto otrzymywana jest poprzez wywoanie metody initialize.
protected initialize()
Metod t naley przesoni, aby dostarczaa warto domyln. Domylnie
zwraca null.
void set(T t)
Ustawia now warto wtku.
void remove()
Usuwa warto wtku.
java.util.concurrent.ThreadLocalRandom 7
Metody lock nie mona przerwa. Jeli wtek oczekujcy na blokad zostanie przerwany,
pozostaje on zablokowany, dopki metoda lock nie bdzie dostpna. Jeli wystpi zaklesz-
czenie, metoda lock moe nigdy nie zakoczy dziaania.
Jeli natomiast metoda tryLock zostanie wywoana z parametrem czasowym, przerwanie ocze-
kujcego wtku spowoduje wygenerowanie wyjtku InterruptedException. Jest to niewt-
pliwie poyteczna funkcja, poniewa pozwala przerywa zakleszczenia.
Rozdzia 14. Wielowtkowo 783
Mona take wywoa metod lockInterruptibly. Dziaa ona tak samo jak tryLock, tylko
bez ograniczenia czasowego.
Metoda await zwraca kontrol, jeli inny wtek aktywuje ten wtek za pomoc metody
signalAll lub signal, jeli minie okrelony czas bd gdy nastpi przerwanie tego wtku.
Jeli oczekujcy wtek zostanie przerwany, metoda await zgasza wyjtek InterruptedEx
ception. W (mao prawdopodobnej) sytuacji, w ktrej lepiej kontynuowa czekanie, naley
w zamian uy metody awaitInterruptibly.
java.util.concurrent.locks.Lock 5.0
boolean tryLock()
Prbuje zaoy blokad, nie blokujc wtku. Jeli operacja zakoczy
si powodzeniem, zwraca warto true. Metoda ta przejmuje blokad,
jeli jest ona dostpna, bez wzgldu na zasad uczciwoci i inne oczekujce wtki.
boolean tryLock(long time, TimeUnit unit)
Prbuje zaoy blokad, blokujc wtek przez czas nie duszy od okrelonego.
W razie powodzenia zwraca warto true.
void lockInterruptibly()
Zajmuje blokad i blokuje wtek na nieokrelony czas. Jeli wtek zostanie
przerwany, zgasza wyjtek InterruptedException.
java.util.concurrent.locks.Condition 5.0
java.util.concurrent.locks.ReentrantReadWriteLock 5.0
Lock readLock()
Zwraca blokad odczytu, ktr moe zakada wiele algorytmw odczytujcych,
ale aden zapisujcy.
Lock writeLock()
Zwraca blokad zapisu, ktra wyklucza wszystkie pozostae algorytmy odczytujce
i zapisujce.
Obie te metody s wycofywane. Metoda stop jest z gruntu niebezpieczna, a jeli chodzi
o suspend, to z dowiadczenia wiadomo, e czsto prowadzi do zakleszcze. W tym pod-
rozdziale wyjaniamy, dlaczego metody te sprawiaj problemy i co mona zrobi, aby ich
unikn.
Zaczniemy od metody stop. Zamyka ona wszystkie oczekujce metody, wcznie z metod
run. Jeli zostanie zastosowana na rzecz wtku, natychmiast zdejmuje on wszystkie blokady,
ktre zaoy. To moe prowadzi do uszkodzenia obiektw. Wyobramy sobie na przykad,
Rozdzia 14. Wielowtkowo 785
Wtek chccy zatrzyma inny wtek nie ma sposobu na sprawdzenie, kiedy wywoanie
metody stop jest bezpieczne, a kiedy doprowadzi do zniszczenia obiektu. Dlatego odradza si
uywania tej metody. Aby zatrzyma wtek, naley go przerwa. Przerwany wtek moe si
zatrzyma wtedy, gdy jest to bezpieczne.
Kolej na metod suspend. W przeciwiestwie do metody stop nie powoduje ona uszkodze-
nia obiektw. Jeli jednak zawieszony zostanie wtek posiadajcy blokad bdzie ona nie-
dostpna a do odwieszenia tego wtku. Jeli wtek, ktry wywoa t metod suspend,
prbuje zaoy t sam blokad, program zostaje zakleszczony zawieszony wtek czeka
na odwieszenie, a wtek, ktry go zawiesi, czeka na blokad.
Metoda paintComponent bdzie rysowa wykres kadego konta. W tym celu utworzy tablic
sald kont za pomoc metody getBalances.
Jak przekonasz si w podrozdziale 14.11, Wtki a biblioteka Swing, zarwno akcje przy-
ciskw, jak i ponowne rysowanie odbywaj si w tym samym wtku wtku dystrybucji
zdarze (ang. event dispatch thread). Przeanalizujmy nastpujcy scenariusz:
1. Jeden z wtkw przelewowych zakada blokad obiektu bank.
2. Uytkownik klika przycisk Wstrzymaj.
3. Wszystkie wtki przelewowe zostaj zawieszone. Jeden z nich cay czas trzyma
blokad na obiekcie bank.
786 Java. Podstawy
Wtek dystrybucji zdarze nie moe kontynuowa, poniewa blokada znajduje si w posia-
daniu jednego z zawieszonych wtkw. Dlatego uytkownik nie moe klikn przycisku
Wznw i wtki nigdy nie zostan odwieszone.
Kolejka blokujca (ang. blocking queue) powoduje zablokowanie wtku podczas prby
dodania elementu, jeli jest pena, lub podczas prby usunicia elementu, jeli jest pusta.
Kolejki tego typu znajduj zastosowanie w koordynacji dziaa wielu wtkw. Niektre
wtki robocze mog co jaki czas odkada porednie wyniki w kolejce blokujcej, a pozo-
stae mog je stamtd usuwa i poddawa dalszej obrbce. Kolejka automatycznie kontroluje
przebieg pracy. Jeli jeden zestaw wtkw dziaa wolniej ni drugi, ten drugi musi poczeka
na wyniki pierwszego. Jeli pierwszy zestaw dziaa szybciej od drugiego, kolejka zapenia
si, dopki drugi zestaw wtkw nada z odbieraniem. Tabela 14.1 zawiera zestawienie
metod blokujcych kolejek.
Istniej take wersje czasowe metod offer i poll. Na przykad ponisza instrukcja:
boolean success = q.offer(x, 100, TimeUnit.MILLISECONDS);
przez sto milisekund prbuje wstawi element do ogona kolejki. Jeli si jej powiedzie, zwrci
warto true, w przeciwnym przypadku, jeli nie wykona operacji w wyznaczonym czasie,
zwrci false. Podobnie instrukcja:
Object head = q.poll(100, TimeUnit.MILLISECONDS)
przez sto milisekund prbuje usun element z czoa kolejki. Jeli si jej powiedzie, zwrci
ten element, w przeciwnym przypadku, jeli nie wykona operacji w wyznaczonym czasie,
zwrci false.
Metoda put wcza blokad, jeli kolejka jest pena, a metoda take robi to samo, gdy kolejka
jest pusta. Metody te s odpowiednikami metod offer i poll bez ograniczenia czasowego.
W pakiecie java.util.concurrent znajduje si kilka wersji kolejek blokujcych. Kolejka
LinkedBlockingQueue nie posiada domylnej grnej granicy pojemnoci, ale mona j okreli.
788 Java. Podstawy
Kolejka PriorityBlockingQueue jest kolejk priorytetow, nie typu pierwszy wszed, pierw-
szy wyszed. Elementy s usuwane zgodnie z ich priorytetami. Kolejka ta ma nieograni-
czon pojemno, ale pobieranie elementw z pustej konstrukcji powoduje blokad (wicej
informacji na temat kolejek priorytetowych znajduje si w rozdziale 13.).
Metoda getDelay zwraca ilo pozostaego czasu opnienia obiektu. Warto ujemna ozna-
cza, e czas ten upyn. Elementy z tej kolejki mog zosta usunite dopiero wtedy, gdy
upynie okrelony czas opnienia. Konieczna jest take implementacja metody compareTo.
Kolejka DelayQueue uywa tej metody do sortowania elementw.
wywoanie to zostaje zablokowane do czasu, a blokad usunie inny wtek. Opisywany inter-
fejs jest zaimplementowany w klasie LinkedTransferQueue.
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class BlockingQueueTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj katalog bazowy (np. /usr/local/jdk1.6.0/src): ");
String directory = in.nextLine();
System.out.print("Podaj sowo kluczowe (np. volatile): ");
String keyword = in.nextLine();
/**
* Zadanie tworzce wyliczenie wszystkich plikw w katalogu i jego podkatalogach
*/
class FileEnumerationTask implements Runnable
{
public static File DUMMY = new File("");
private BlockingQueue<File> queue;
private File startingDirectory;
/**
* Tworzy obiekt klasy FileEnumerationTask.
* @param queue kolejka blokujca, do ktrej dodawane s pliki
* @param startingDirectory katalog, od ktrego ma si zacz zbieranie plikw
*/
public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory)
{
this.queue = queue;
this.startingDirectory = startingDirectory;
}
enumerate(startingDirectory);
queue.put(DUMMY);
}
catch (InterruptedException e)
{
}
}
/**
* Rekursywna enumeracja wszystkich plikw znajdujcych si w danym katalogu i jego podkatalogach
* @param directory katalog pocztkowy
*/
public void enumerate(File directory) throws InterruptedException
{
File[] files = directory.listFiles();
for (File file : files)
{
if (file.isDirectory()) enumerate(file);
else queue.put(file);
}
}
}
/**
* Zadanie przeszukujce pliki w celu znalezienia okrelonego sowa kluczowego
*/
/**
* Tworzy obiekt klasy SearchTask.
* @param queue kolejka, z ktrej maj by pobierane pliki
* @param keyword sowo kluczowe, ktre ma zosta znalezione
*/
public SearchTask(BlockingQueue<File> queue, String keyword)
{
this.queue = queue;
this.keyword = keyword;
}
catch (IOException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
}
}
/**
* Przeszukuje plik w celu znalezienia okrelonego sowa kluczowego i drukuje wszystkie zawierajce je linijki
* @param file plik do przeszukania
*/
Uruchamiamy take du liczb wtkw przeszukujcych. Kady taki wtek pobiera plik
z kolejki, otwiera go, drukuje wszystkie linijki zawierajce dane sowo kluczowe i pobiera
nastpny plik. Do zakoczenia aplikacji, kiedy dalsza jej praca jest ju zbdna, wykorzy-
stalimy pewn sztuczk. Wtek wyliczeniowy sygnalizuje ukoczenie pracy, umieszczajc
w kolejce atrap obiektu (przypomina to umieszczanie walizki z etykiet Ostatnia torba
na kocu tamy z walizkami na lotnisku). Kiedy wtek przeszukujcy pobierze taki obiekt,
odkada go z powrotem i koczy dziaanie.
Zwr uwag, e nie trzeba bezporednio stosowa synchronizacji. W tej aplikacji do syn-
chronizacji uywamy kolejki.
java.util.concurrent.ArrayBlockingQueue<E> 5.0
ArrayBlockingQueue(int capacity)
ArrayBlockingQueue(int capacity, boolean fair)
Tworzy kolejk blokujc o okrelonej pojemnoci i z ustawion zasad uczciwoci.
Kolejka ta jest zaimplementowana jako tablica cykliczna.
792 Java. Podstawy
java.util.concurrent.LinkedBlockingQueue<E> 5.0
java.util.concurrent.LinkedBlockingDeque<E> 6
LinkedBlockingQueue()
LinkedBlockingDeque()
Tworzy nieograniczon kolejk blokujc jedno- lub dwustronn,
zaimplementowan jako lista powizana.
LinkedBlockingQueue(int capacity)
LinkedBlockingDeque(int capacity)
Tworzy ograniczon kolejk jedno- lub dwustronn blokujc o okrelonej
pojemnoci, zaimplementowan jako lista powizana.
DelayQueue()
Tworzy nieograniczon kolejk elementw typu Delayed. Z kolejki tej mona
usuwa tylko te elementy, ktrych czas opnienia upyn.
java.util.concurrent.Delayed 5.0
java.util.concurrent.PriorityBlockingQueue<E> 5.0
PriorityBlockingQueue()
PriorityBlockingQueue(int initialCapacity)
PriorityBlockingQueue(int initialCapacity, Comparator<? super E>
comparator)
Tworzy nieograniczon priorytetow kolejk blokujc zaimplementowan
jako sterta.
Parametry: initialCapacity Pocztkowa pojemno kolejki
priorytetowej. Domylna warto to 11.
comparator Komparator uywany do porwnywania
elementw. Jeli nie zostanie podany,
elementy musz implementowa interfejs
Comparable.
java.util.concurrent.BlockingQueue<E> 5.0
E take()
Usuwa i zwraca element z czoa, w razie koniecznoci wcza blokowanie.
boolean offer(E element, long time, TimeUnit unit)
Dodaje okrelony element i zwraca warto true, jeli operacja zakoczy si
powodzeniem. W razie koniecznoci wcza blokowanie, a element zostanie
dodany lub upynie okrelony czas.
E poll(long time, TimeUnit unit)
Usuwa i zwraca element z czoa. W razie koniecznoci wcza blokowanie,
a element bdzie dostpny lub upynie okrelony czas. W razie niepowodzenia
zwraca warto null.
java.util.concurrent.BlockingDeque<E> 6
java.util.concurrent.TransferQueue<E> 7
Wspdzielon struktur danych mona ochroni za pomoc blokady, ale czsto atwiejszym
rozwizaniem jest uycie implementacji bezpiecznej wtkowo. Kolejki blokujce omwione
w poprzednim podrozdziale s przykadem takich bezpiecznych wtkowo kolekcji. Ponisze
podrozdziay opisuj inne kolekcje znajdujce si w bibliotece Javy, ktre s bezpieczne ze
wzgldu na wtki.
Kolekcje te zwracaj tak zwane sabo spjne iteratory (ang. weakly consistent iterators).
Oznacza to, e mog one (cho nie musz) odzwierciedla wszystkie modyfikacje dokonane
po ich skonstruowaniu. Nie zwracaj one jednak dwukrotnie wartoci i nie zgaszaj wyjtku
ConcurrentModificationException.
Operacj przeciwn wykonuje metoda remove (ktrej nazwa powinna chyba brzmie remove
IfPresent). Ponisze wywoanie usuwa za pomoc niepodzielnej operacji klucz i jego
warto, jeli znajduj si one w mapie.
cache.remove(key, value)
java.util.concurrent.ConcurrentLinkedQueue<E> 5.0
ConcurrentLinkedQueue<E>()
Tworzy nieograniczon kolejk nieblokujc, ktr mona bezpiecznie
przetwarza w wielu wtkach.
java.util.concurrent.ConcurrentSkipListSet<E> 6
ConcurrentSkipListSet<E>()
ConcurrentSkipListSet<E>(Comparator<? super E> comp)
Tworzy zbir uporzdkowany, do ktrego mona bezpiecznie uzyska
dostp w wielu wtkach. Pierwszy z konstruktorw wymaga, aby elementy
implementoway interfejs Comparable.
ConcurrentHashMap<K, V>()
ConcurrentHashMap<K, V>(int initialCapacity)
ConcurrentHashMap<K, V>(int initialCapacity, float loadFactor,
int concurrencyLevel)
Tworzy map haszow, do ktrej mona bezpiecznie uzyska dostp w wielu wtkach.
Parametry: initialCapacity Pocztkowa pojemno kolekcji, domylna
warto to 16.
loadFactor Kontroluje zmian rozmiaru: jeli rednie
zapenienie komrki przekracza
ten wspczynnik, rozmiar tablicy
jest zmieniany. Domylna warto to 0,75.
concurrencyLevel Przewidywana liczba wspbienych wtkw
zapisujcych.
796 Java. Podstawy
ConcurrentSkipListMap<K, V>()
ConcurrentSkipListSet<K, V>(Comparator<? super K> comp)
Tworzy uporzdkowan map, do ktrej mona uzyska bezpieczny dostp w wielu
wtkach. Pierwszy z konstruktorw wymaga, aby klucze implementoway interfejs
Comparable.
V putIfAbsent(K key, V value)
Jeli klucza nie ma jeszcze w mapie, zostaje on skojarzony z podan wartoci
i metoda zwraca warto null. W przeciwnym przypadku zwraca istniejc
warto skojarzon z tym kluczem.
boolean remove(K key, V value)
Jeli podany klucz jest aktualnie skojarzony z podan wartoci, metoda ta usuwa
je i zwraca warto true. W przeciwnym przypadku zwraca warto false.
boolean replace(K key, V oldValue, V newValue)
Jeli podany klucz jest aktualnie skojarzony ze star wartoci (oldValue), zostaje
skojarzony z now wartoci (newValue). W przeciwnym przypadku zwraca
warto false.
Metody tak powstaych kolekcji s chronione przez blokad, co umoliwia bezpieczny wt-
kowo dostp.
Rozdzia 14. Wielowtkowo 797
Naley zapewni, e aden wtek nie bdzie mia dostpu do struktury danych poprzez orygi-
nalne niesynchronizowane metody. Najprostszym sposobem jest niezapisywanie adnych
referencji do oryginalnego obiektu. Po utworzeniu kolekcji od razu naley przekaza j do
opakowania, tak jak zrobilimy to w prezentowanych przykadach.
Nadal trzeba stosowa blokowanie po stronie klienta, aby mc iterowa po kolekcji, podczas
gdy inny wtek moe j modyfikowa:
synchronized (synchHashMap)
{
Iterator<K> iter = synchHashMap.keySet().iterator();
while (iter.hasNext()) . . .;
}
Tego samego kodu musimy uy, jeli korzystamy z ptli typu for each, poniewa ptla ta
uywa iteratora. Naley pamita, e iterator zgosi wyjtek ConcurrentModificationException,
jeli w trakcie iteracji po kolekcji inny wtek j zmodyfikuje. Synchronizacja jest nadal
wymagana, dziki czemu mona wykry wspbiene modyfikacje.
java.util.Collections 1.2
Wywoanie pierwszej z metod get jest zablokowane do zakoczenia oblicze. Druga wersja
tej metody zgasza wyjtek TimeoutException, jeli obliczenia nie zakocz si przed upywem
okrelonego czasu. Obie te metody zgaszaj wyjtek InterruptedException, jeli wtek prze-
prowadzajcy obliczenia zostanie przerwany. Kiedy obliczenia zakocz si, metoda get
natychmiast zwraca warto.
Metoda isDone zwraca warto false, jeli obliczenia s jeszcze w toku, lub true w prze-
ciwnym przypadku.
Operacj mona przerwa za pomoc metody cancel. Jeli jeszcze si nie rozpocza, zosta-
nie anulowana i nigdy si nie rozpocznie. Jeli jest w toku, zostanie przerwana, gdy parametr
mayInterrupt ma warto true.
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class FutureTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj katalog bazowy (np. /usr/local/jdk1.6.0/src): ");
String directory = in.nextLine();
System.out.print("Podaj sowo kluczowe (np. volatile): ");
String keyword = in.nextLine();
/**
* Zadanie liczce pliki znajdujce si w katalogu i jego podkatalogach, zawierajce dane sowo kluczowe
*/
class MatchCounter implements Callable<Integer>
{
private File directory;
private String keyword;
private int count;
/**
800 Java. Podstawy
/**
* Przeszukuje plik w celu znalezienia danego sowa kluczowego.
* @param file plik do przeszukania
* @return warto true, jeli plik zawiera dane sowo kluczowe
*/
public boolean search(File file)
{
try
{
Rozdzia 14. Wielowtkowo 801
Oczywicie wywoanie metody get powoduje blokad do chwili, a wynik jest rzeczywicie
dostpny.
W metodzie call wykorzystujemy rekursywnie ten sam mechanizm. Dla kadego podkatalogu
tworzymy nowy obiekt MatchCounter i uruchamiamy dla niego wtek. Ponadto odkadamy
obiekty FutureTask w tablicy ArrayList<Future<Integer>>. Na kocu sumujemy wszystkie
wyniki.
for (Future<Integer> result : results)
count += result.get();
Kade wywoanie metody get powoduje blokad do chwili, a zostanie udostpniony wynik.
Oczywicie wtki dziaaj rwnolegle, dziki czemu jest dua szansa, e wszystkie wyniki
bd dostpne mniej wicej w tym samym czasie.
java.util.concurrent.Callable<V> 5.0
V call()
Uruchamia zadanie, ktre zwraca wynik.
java.util.concurrent.Future<V> 5.0
V get()
V get(long time, TimeUnit unit)
802 Java. Podstawy
Zwraca wynik, wczajc blokad, dopki nie jest on dostpny lub nie upynie
okrelona ilo czasu. Druga wersja zgasza wyjtek TimeoutException,
jeli zakoczy si niepowodzeniem.
boolean cancel(boolean mayInterrupt)
Prbuje anulowa wykonywanie zadania. Zadanie, ktre zostao ju uruchomione,
a ma parametr mayInterrupt ustawiony na true, zostanie przerwane. Jeli operacja
anulowania zakoczy si pomylnie, metoda ta zwraca warto true.
boolean isCancelled()
Zwraca warto true, jeli zadanie zostao anulowane przed ukoczeniem.
boolean isDone()
Zwraca warto true, jeli zadanie zostao ukoczone w normalny sposb, zostao
anulowane lub spowodowao wyjtek.
java.util.concurrent.FutureTask<V> 5.0
FutureTask(Callable<V> task)
FutureTask(Runnable task, V result)
Tworzy obiekt, ktry jest zarwno typu Future<V>, jak i Runnable.
Metoda Opis
newCachedThreadPool W razie potrzeby tworzy nowe wtki. Nieaktywne wtki
s przetrzymywane przez 60 sekund.
newFixedThreadPool Pula zawierajca ustalon liczb wtkw. Nieaktywne wtki
s zachowywane bezterminowo.
newSingleThreadExecutor Pula skadajca si z jednego wtku wykonujcego zadania po kolei
(podobnie do wtku dystrybucji zdarze w Swing).
newScheduledThreadPool Ustalona harmonogramowana pula wtkw. Zastpstwo
dla java.uti.Timer.
newSingleThreadScheduledExecutor Harmonogramowana pula skadajca si z jednego wtku.
Obiekt Runnable lub Callable mona przekaza do ExecutorService za pomoc jednej z poni-
szych metod:
Future<?> submit(Runnable task)
Future<T> submit(Runnable task, T result)
Future<T> submit(Callable<T> task)
Pula wykona powierzone jej zadanie przy najbliszej sposobnoci. Metoda submit zwraca obiekt
typu Future, ktry zawiera informacje o stanie zadania.
Druga wersja metody submit take przesya obiekt Runnable, a metoda get interfejsu Future
zwraca wynik operacji, gdy jest ju gotowy.
Trzecia wersja przesya obiekt Callable i zwrcony obiekt Future otrzymuje wynik oblicze,
gdy jest gotowy.
Po zakoczeniu pracy w puli wtkw naley wywoa metod shutdown. Inicjuje ona operacj
zamykajc pul. Egzekutor, ktry jest zamykany, nie przyjmuje adnych nowych zada.
804 Java. Podstawy
Po zakoczeniu wszystkich zada wtki puli zostaj zakoczone. Istnieje take metoda
shutdownNow, ktra powoduje, e pula anuluje wszystkie jeszcze niezaczte zadania i pr-
buje przerwa aktualnie uruchomione.
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class ThreadPoolTest
{
public static void main(String[] args) throws Exception
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj katalog bazowy (np. /usr/local/jdk1.6.0/src): ");
String directory = in.nextLine();
System.out.print("Podaj sowo kluczowe (np. volatile): ");
String keyword = in.nextLine();
try
{
System.out.println(result.get() + " pasujcych plikw.");
}
catch (ExecutionException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
Rozdzia 14. Wielowtkowo 805
}
pool.shutdown();
/**
* Zadanie liczce pliki w katalogu i jego podkatalogach, zawierajce dane sowo kluczowe
*/
class MatchCounter implements Callable<Integer>
{
private File directory;
private String keyword;
private ExecutorService pool;
private int count;
/**
* Tworzy obiekt typu MatchCounter.
* @param directory katalog, od ktrego ma si zacz szukanie
* @param keyword sowo kluczowe do znalezienia
* @param pool pula wtkw, do ktrej wysyane s zadania
*/
public MatchCounter(File directory, String keyword, ExecutorService pool)
{
this.directory = directory;
this.keyword = keyword;
this.pool = pool;
}
}
}
catch (InterruptedException e)
{
}
return count;
}
/**
* Przeszukuje plik w celu znalezienia danego sowa kluczowego.
* @param file plik do przeszukania
* @return warto true, jeli plik zawiera sowo kluczowe
*/
Dla celw informacyjnych program drukuje rozmiar najwikszej puli. Informacja ta nie jest
dostpna za porednictwem interfejsu ExecutorService. Z tego powodu musielimy rzutowa
obiekt puli na klas ThreadPoolExecutor.
java.util.concurrent.Executors 5.0
ExecutorService newCachedThreadPool()
Zwraca pul wtkw, ktra w razie potrzeby tworzy wtki, i koczy te,
ktre s nieaktywne przez 60 sekund.
ExecutorService newFixedThreadPool(int threads)
Zwraca pul wtkw, ktra wykonuje zadania przy uyciu okrelonej liczby
wtkw.
ExecutorService newSingleThreadExecutor()
Zwraca egzekutor, ktry wykonuje zadania kolejno w jednym wtku.
Rozdzia 14. Wielowtkowo 807
java.util.concurrent.ExecutorService 5.0
java.util.concurrent.ThreadPoolExecutor 5.0
int getLargestPoolSize()
Zwraca najwikszy rozmiar puli wtkw w czasie dziaania egzekutora.
java.util.concurrent.Executors 5.0
java.util.concurrent.ScheduledExecutorService 5.0
Metoda invokeAny przekazuje wszystkie obiekty z kolekcji obiektw typu Callable i zwraca
wynik ukoczonego zadania. Nie wiadomo, ktre to zadanie prawdopodobnie to, ktre
zostao ukoczone najwczeniej. Metody tej mona uy w algorytmie wyszukujcym, ktry
moe przyj kade rozwizanie. Wyobramy sobie na przykad, e chcemy rozoy na czyn-
niki du liczb cakowit operacja wymagana do amania szyfru RSA. Mona przeka-
za kilka zada, z ktrych kade prbuje rozkadu przy uyciu liczb z innego zakresu. Kiedy
tylko ktrekolwiek z nich ma odpowied, dalsze obliczenia mona zatrzyma.
Metoda invokeAll przesya wszystkie obiekty Callable z kolekcji i zwraca list obiektw
Future, ktre reprezentuj rozwizania wszystkich zada. Wyniki te mona przetwarza
w nastpujcy sposb:
List<Callable<T>> tasks = . . .;
List<Future<T>> results = executor.invokeAll(tasks);
for (Future<T> result : results)
processFurther(result.get());
Wad tej metody jest to, e mona niepotrzebnie czeka, jeli pierwsze zadanie zajmuje zbyt
duo czasu. Duo lepiej byoby pobiera wyniki w takiej kolejnoci, w jakiej s udostp-
niane. Mona to osign za pomoc klasy ExecutorCompletionService.
java.util.concurrent.ExecutorService 5.0
T invokeAny(Collection<Callable<T>> tasks)
T invokeAny(Collection<Callable<T>> tasks, long timeout, TimeUnit unit)
Rozdzia 14. Wielowtkowo 809
Wykonuje podane zadania i zwraca wynik jednego z nich. Druga z tych metod
zgasza wyjtek TimeoutException, jeli zostanie przekroczony dozwolony czas.
List<Future<T>> invokeAll(Collection<Callable<T>> tasks)
List<Future<T>> invokeAll(Collection<Callable<T>> tasks, long timeout,
TimeUnit unit)
Wykonuje dane zadania i zwraca wyniki ich wszystkich. Druga z tych metod
zgasza wyjtek TimeoutException, jeli zostanie przekroczony dozwolony czas.
java.util.concurrent.ExecutorCompletionService 5.0
ExecutorCompletionService(Executor e)
Tworzy obiekt typu ExecutorCompletionService, ktry przechowuje wyniki zada
okrelonego egzekutora.
Future<T> submit(Callable<T> task)
Future<T> submit(Runnable task, T result)
Przekazuje zadanie do egzekutora.
Future<T> take()
Usuwa nastpny wynik lub wcza blokad, jeli nie ma dostpnych adnych
wynikw.
Future<T> poll()
Future<T> poll(long time, TimeUnit unit)
Usuwa nastpny wynik lub zwraca warto null, jeli nie ma dostpnych adnych
wynikw. Druga wersja tej metody odczekuje okrelon ilo czasu.
Aby wykona nasze rekursywne obliczenia w odpowiedni sposb, utworzymy klas rozsze-
rzajc klas RecursiveTask<T> (jeli wynik obliczenia jest typu T) lub RecursiveAction
(jeli nie ma wyniku). Przesonimy metod compute, aby generowaa i wywoywaa czci
zadania oraz czya ich wyniki.
class Counter extends RecursiveTask<Integer>
{
. . .
protected Integer compute()
{
if (to - from < THRESHOLD)
{
bezporednie rozwizanie problemu
}
else
{
int mid = (from + to) / 2;
Counter first = new Counter(values, from, mid, filter);
Counter second = new Counter(values, mid, to, filter);
invokeAll(first, second);
return first.join() + second.join();
}
}
}
Metoda invokeAll otrzymuje liczb zada i wcza blokad, dopki wszystkie one nie zostan
wykonane. Metoda join generuje wynik. Stosujemy j do wszystkich podzada, aby otrzy-
ma sum.
Istnieje te metoda get do pobierania aktualnego wyniku, ale jest ona mniej atrak-
cyjna, poniewa moe zgasza kontrolowane wyjtki, ktrych nie moemy ponownie
zgasza w metodzie compute.
import java.util.concurrent.*;
/**
* Program demonstrujcy szkielet rozgazienie-zczenie
* @version 1.00 2012-05-20
Rozdzia 14. Wielowtkowo 811
interface Filter
{
boolean accept(double t);
}
14.10. Synchronizatory
W pakiecie java.util.concurrent znajduje si kilka klas, ktre uatwiaj zarzdzanie zbio-
rami spokrewnionych ze sob zada zobacz tabela 14.3. Algorytmy te udostpniaj gotowe
rozwizania czsto spotykanych problemw zwizanych ze wspprac pomidzy wtkami.
Majc zestaw wsppracujcych ze sob wtkw dziaajcych wedug jednego z wzorcw,
naley zamiast we wasnym zakresie tworzy zbir blokad i warunkw uy jednej
z tych klas.
14.10.1. Semafory
Z zaoenia semafor suy do zarzdzania pewn liczb zezwole (ang. permit). Aby przej
obok semafora, wtek prbuje uzyska zezwolenie, wywoujc w tym celu metod acquire.
Liczba dostpnych zezwole jest ograniczona, co pozwala na kontrol liczby wtkw, ktre
mog przej dalej. Inne wtki mog wydawa zezwolenia za pomoc metody release
(w rzeczywistoci nie istniej adne obiekty zezwole, ich licznik jest po prostu przecho-
wywany w semaforze). Jako e dostpna jest okrelona liczba zezwole, semafor ogranicza
liczb wtkw, ktre mog przej. Ponadto zezwolenie nie musi zosta zwolnione przez
wtek, ktry je uzyska. Kady wtek moe wyda dowoln liczb zezwole, a wic poten-
cjalnie moe zwikszy ich liczb powyej pocztkowego limitu.
Semafory wynalaz w 1968 roku programista o nazwisku Edsger Dijkstra, ktry potrzebowa
mechanizmu synchronizacji. Wykaza on, e semafory mog by szybkie i s na tyle wszech-
stronne, i mog suy do rozwizania wielu czsto wystpujcych problemw zwizanych
z synchronizacj. W prawie kadej ksice na temat systemw operacyjnych znajduje si opis
implementacji kolejek ograniczonych wykorzystujcych semafory.
Wyobramy sobie na przykad zestaw wtkw, ktre do wykonania swoich zada potrze-
buj pewnych danych inicjujcych. Wtki s uruchomione i czekaj przed bramk. Inny wtek
przygotowuje dane. Kiedy jest gotowy, wywouje metod countdown i wszystkie wtki konty-
nuuj swoje zadania.
Aby sprawdzi, kiedy wszystkie wtki zakoczyy swoje dziaania, mona uy kolejnego
zatrzasku. Naley zainicjowa go liczb wtkw. Kady wtek przed zakoczeniem dziaania
odejmuje jeden od licznika zatrzasku. Inny wtek, ktry zbiera wyniki tej pracy, czeka na
zatrzasku i przechodzi do dziaania, gdy wszystkie pozostae wtki zakocz swoje dziaanie.
814 Java. Podstawy
14.10.3. Bariery
Klasa CyclicBarrier suy do tworzenia obiektw nazywanych barierami (ang. barrier).
Wyobramy sobie kilka wtkw, z ktrych kady wykonuje porcj oblicze stanowic frag-
ment jednej caoci. Kiedy wszystkie czci s gotowe, ich wyniki trzeba poczy. Kiedy
wtek zakoczy swoje zadanie, zatrzymuje si na barierze. Kiedy wszystkie wtki dotr do
bariery, zostaje ona otwarta i wtki mog kontynuowa dziaanie.
Oto szczegowa analiza tego problemu. Najpierw tworzymy barier, przekazujc do niej
liczb wtkw biorcych udzia w zadaniu:
CyclicBarrier barrier = new CyclicBarrier(nthreads);
Kady z wtkw wykonuje jakie dziaania i po ich zakoczeniu wywouje na rzecz bariery
metod await:
public void run()
{
doWork();
barrier.await();
. . .
}
Jeli ktry z wtkw oczekujcych na barier zniknie, bariera zostaje zamana (wtek moe
znikn, kiedy wywoa metod await z ograniczeniem czasowym lub zostanie przerwany).
W takim przypadku metoda await wszystkich pozostaych wtkw zgasza wyjtek Broken
BarrierException. Metoda await oczekujcych wtkw zostaje natychmiast zakoczona.
Mona okreli opcjonaln akcj bariery, ktra bdzie wykonywana, kiedy wszystkie wtki
osign t barier:
Runnable barrierAction = . . .;
CyclicBarrier barrier = new CyclicBarrier(nthreads, barrierAction);
Bariera jest cykliczna (ang. cyclic), poniewa po uwolnieniu wszystkich oczekujcych wtkw
mona jej uy ponownie. Rni si ona pod tym wzgldem od zatrzasku CountDownLatch,
ktry moe zosta uyty tylko jeden raz.
Klasa Phaser pozwala na wiksz elastyczno, umoliwiajc zmienianie liczby wtkw bior-
cych udzia w fazach.
Naley jednak cile kontrolowa, jakie zadania wykonuje wtek roboczy, poniewa, co moe
by pewnym zaskoczeniem, Swing nie jest bezpieczny dla wtkw. Manipulacja elemen-
tami interfejsu uytkownika w wielu wtkach moe doprowadzi do jego awarii.
Aby sprawdzi, na czym polega ten problem, uruchom program z listingu 14.14. Kliknicie
przycisku Zy powoduje uruchomienie nowego wtku. Jego metoda run drczy pole listy
rozwijalnej, dodajc do niej i usuwajc z niej losowe wartoci.
public void run()
{
try
{
while (true)
{
int i = Math.abs(generator.nextInt());
if (i % 2 == 0)
combo.insertItemAt(new Integer(i), 0);
else if (combo.getItemCount() > 0)
combo.removeItemAt(i % combo.getItemCount());
sleep(1);
}
catch (InterruptedException e) {}
}
}
Wyprbujmy ten program. Kliknij przycisk Zy. Kliknij kilkakrotnie pole listy. Porusz pa-
skiem przewijania. Przesu okno. Jeszcze raz kliknij przycisk Zy. Poklikaj list rozwijaln.
W kocu powinien si pojawi komunikat o wyjtku (rysunek 14.8).
Co si stao? Kiedy do listy dodawany jest element, uruchamia ona zdarzenie aktualizacji
ekranu. Wtedy do akcji wchodzi kod wywietlajcy komponenty na ekranie, ktry wczytuje
816 Java. Podstawy
Rysunek 14.8.
Komunikaty
o wyjtkach
w oknie konsoli
aktualny rozmiar pola listy i przygotowuje si do wywietlenia wartoci. Jednak wtek roboczy
nie przestaje dziaa to od czasu do czasu powoduje zmniejszenie licznika wartoci na
licie. Kod odpowiedzialny za wywietlanie komponentw spodziewa si wicej wartoci
w modelu, ni ich rzeczywicie jest, i prosi o nieistniejce wartoci, co powoduje powstanie
wyjtku ArrayIndexOutOfBoundsException.
Sytuacji tej mona by byo unikn, umoliwiajc programicie zablokowanie pola listy pod-
czas jego wywietlania. Jednak projektanci biblioteki Swing postanowili nie robi nic w kie-
runku bezpieczestwa dla wtkw, i to z dwch powodw. Synchronizacja jest czasochonna,
a nikt nie chcia, aby Swing by jeszcze wolniejszy. Ponadto zesp pracujcy nad Swin-
giem wzi pod uwag dowiadczenia innych zespow, ktre pracoway nad zestawami na-
rzdzi do budowy bezpiecznych dla wtkw interfejsw. Programici wykorzystujcy tego
typu narzdzia mieli problemy z opanowaniem wymaga zwizanych z synchronizacj
i czsto tworzyli programy, ktre atwo wpaday w zakleszczenia.
Druga z wymienionych zasad w programowaniu Swing jest czsto nazywana zasad jednego
wtku. Szerzej na jej temat piszemy w sekcji 14.11.3.
Rozwizaniem tego problemu s dwie metody uytkowe, za pomoc ktrych mona w dowol-
nym wtku doda dowolne akcje do kolejki zdarze. Wyobramy sobie na przykad, e co
jaki czas chcemy w wtku aktualizowa etykiet, aby pokaza postp operacji. Nie moemy
wywoa metody label.setText bezporednio w tym wtku.
Oto wymagane czynnoci. Kod Swing umieszczamy w metodzie run klasy implementujcej
interfejs Runnable. Nastpnie tworzymy obiekt tej klasy i przekazujemy go do statycznej metody
invokeLater lub invokeAndWait. Ponisza przykadowa procedura aktualizuje tekst etykiety:
EventQueue.invokeLater(new
Runnable()
{
public void run()
{
label.setText("ukoczono " + percentage + "%");
}
});
Metoda invokeLater zwraca warto natychmiast po tym, jak zdarzenie zostanie wysane do
kolejki zdarze. Metoda run jest wykonywana asynchronicznie. Metoda invokeAndWait czeka,
a metoda run zostanie wykonana.
Obie te metody wykonuj metod run w wtku dystrybucji zdarze nie jest tworzony aden
nowy wtek.
package swing;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
/**
* Program udowadniajcy, e wtek dziaajcy rwnolegle z wtkiem dystrybucji zdarze moe
* powodowa bdy w komponentach Swing
818 Java. Podstawy
/**
* Ramka majca dwa przyciski suce do zapeniania listy w osobnym wtku. Przycisk Dobry
* wykorzystuje kolejk zdarze, a Zy modyfikuje list bezporednio.
*/
panel.add(combo);
add(panel);
Rozdzia 14. Wielowtkowo 819
pack();
}
}
/**
* Klasa modyfikujca list rozwijan poprzez dodanie do niej i usunicie z niej losowych liczb. Moe to
* spowodowa bdy,poniewa metody listy rozwijalnej nie s synchronizowane, przez co wtek roboczy
* i wtek dystrybucji zdarze uzyskuj dostp do tej listy.
*/
/**
* Klasa modyfikujca list rozwijan poprzez dodanie do niej i usunicie z niej losowych liczb.
* Aby unikn uszkodzenia tej listy, operacje edycji s przesyane do wtku dystrybucji zdarze.
*/
class GoodWorkerRunnable implements Runnable
{
private JComboBox<Integer> combo;
private Random generator;
try
{
while (true)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
int i = Math.abs(generator.nextInt());
if (i % 2 == 0) combo.insertItemAt(i, 0);
else if (combo.getItemCount() > 0) combo.removeItemAt(i
% combo.getItemCount());
}
});
Thread.sleep(1);
}
}
catch (InterruptedException e)
{
}
}
}
java.awt.EventQueue 1.1
w menu Plik jest nieaktywne, a Anuluj aktywne (rysunek 14.9). Po wczytaniu kadej linijki
tekstu aktualizowany jest licznik w pasku stanu. Po ukoczeniu adowania polecenie Otwrz
staje si z powrotem aktywne, polecenie Anuluj nieaktywne, a w pasku stanu wywietla si
napis Zakoczono.
Rysunek 14.9.
adowanie pliku
w osobnym wtku
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import javax.swing.*;
/**
* Program demonstrujcy wtek roboczy wykonujcy potencjalnie czasochonne zadanie
* @version 1.1 2007-05-18
* @author Cay Horstmann
*/
public class SwingWorkerTest
{
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new SwingWorkerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka majca obszar tekstowy pokazujcy zawarto pliku tekstowego, menu pozwalajce otworzy plik
* i anulowa proces otwierania pliku oraz wiersz stanu pokazujcy postp adowania pliku
*/
822 Java. Podstawy
public SwingWorkerFrame()
{
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
});
pack();
}
// Ponisza metoda jest wykonywana w wtku roboczym nie operuje na komponentach Swing
@Override
public StringBuilder doInBackground() throws IOException, InterruptedException
{
int lineNumber = 0;
try (Scanner in = new Scanner(new FileInputStream(file)))
{
while (in.hasNextLine())
{
String line = in.nextLine();
lineNumber++;
text.append(line);
text.append("\n");
ProgressData data = new ProgressData();
data.number = lineNumber;
data.line = line;
publish(data);
Thread.sleep(1); // Test operacji anulowania, nie ma potrzeby robienia tego w
// swoich programach
}
}
return text;
}
@Override
public void process(List<ProgressData> data)
{
if (isCancelled()) return;
StringBuilder b = new StringBuilder();
statusLine.setText("" + data.get(data.size() - 1).number);
for (ProgressData d : data)
{
b.append(d.line);
b.append("\n");
}
824 Java. Podstawy
textArea.append(b.toString());
}
@Override
public void done()
{
try
{
StringBuilder result = get();
textArea.setText(result.toString());
statusLine.setText("Zakoczono");
}
catch (InterruptedException ex)
{
}
catch (CancellationException ex)
{
textArea.setText("");
statusLine.setText("Anulowano");
}
catch (ExecutionException ex)
{
statusLine.setText("" + ex.getCause());
}
cancelItem.setEnabled(false);
openItem.setEnabled(true);
}
};
}
Aby wykona jakie dziaania w wtku roboczym, naley utworzy obiekt klasy SwingWorker
(kady taki obiekt moe by uyty tylko jeden raz). Nastpnie naley wywoa metod execute.
Metod t z reguy wywouje si na rzecz wtku dystrybucji zdarze, ale nie jest to wymogiem.
Z zaoenia obiekt klasy SwingWorker powinien zwrci jaki wynik. Dlatego klasa SwingWor
ker<T, V> implementuje interfejs Future<T>. Wynik ten mona pobra za pomoc metody
Rozdzia 14. Wielowtkowo 825
get tego interfejsu. Poniewa metoda ta wcza blokad, dopki wynik nie jest dostpny, nie
naley wywoywa jej bezporednio po metodzie execute. Dobrym rozwizaniem jest wywo-
ywanie jej dopiero wwczas, gdy wiadomo, e praca zostaa zakoczona. Zazwyczaj metod
get wywouje si w metodzie done (wywoanie metody get nie jest konieczne czasami
wystarczy przetworzenie danych postpu).
Zarwno porednie dane postpu, jak i kocowy wynik mog by dowolnego typu. Typy te
s okrelone w klasie SwingWorker jako parametry typowe. Klasa SwingWorker<T, V> tworzy
wynik typu T i dane postpu typu V.
Do anulowania zadania w toku suy metoda cancel z interfejsu Future. Kiedy zadanie jest
anulowane, metoda get zgasza wyjtek CancellationException.
Jak ju wiemy, wywoanie w wtku roboczym metody publish spowoduje wywoanie metody
process na rzecz wtku dystrybucji zdarze. Aby zwikszy wydajno, wyniki zwrcone przez
kilka wywoa metody publish mona zgrupowa w jednym wywoaniu metody process.
Metoda process odbiera obiekt List<V> zawierajcy wszystkie wyniki porednie.
Uyjemy tej techniki do wczytywania pliku tekstowego. Okazuje si, e komponent JTextArea
jest niezbyt szybki. Dodawanie linii tekstu z duego pliku tekstowego (jak The Count of
Monte Cristo) zajmuje duo czasu.
Ostateczny wynik stanowi tekst, ktry zosta wczytany do obiektu typu StringBuilder.
W zwizku z tym klasa, ktrej potrzebujemy, to SwingWorker<StringBuilder, ProgressData).
Metoda doInBackground wczytuje dane z pliku wiersz po wierszu. Po kadym wierszu wywo-
ujemy metod publish publikujc numer i zawarto aktualnej linii.
@Override public StringBuilder doInBackground() throws IOException, InterruptedException
{
int lineNumber = 0;
Scanner in = new Scanner(new FileInputStream(file));
while (in.hasNextLine())
{
String line = in.nextLine();
lineNumber++;
text.append(line);
text.append("\n");
ProgressData data = new ProgressData();
data.number = lineNumber;
data.line = line;
publish(data);
Thread.sleep(1); // Test operacji anulowania, nie ma potrzeby robienia tego w swoich programach.
826 Java. Podstawy
}
return text;
}
Po kadej linii tekstu usypiamy wtek na jedn milisekund, aby mona byo spokojnie przete-
stowa anulowanie. Oczywicie w programach przeznaczonych do uytku nie naley tego
robi, aby ich nie spowalnia. Jeli postawimy przed tym wierszem symbol komentarza,
zauwaymy, e tekst ksiki wczytuje si do szybko i jest tylko kilka wikszych aktuali-
zacji interfejsu uytkownika.
Aby program dziaa pynniej, pole tekstowe mona aktualizowa w wtku roboczym.
Nie jest to jednak moliwe w przypadku wszystkich komponentw Swing. Prezen-
tujemy ogln technik, polegajc na aktualizacji wszystkich komponentw w wtku
dystrybucji zdarze.
Metoda process ignoruje wszystkie linie tekstu poza ostatni oraz czy wszystkie linie
w jednej aktualizacji obszaru tekstowego.
@Override public void process(List<ProgressData> data)
{
if (isCancelled()) return;
StringBuilder b = new StringBuilder();
statusLine.setText("" + data.get(data.size() - 1).number);
for (ProgressData d : data) { b.append(d.line); b.append("\n"); }
textArea.append(b.toString());
}
W metodzie done obszar tekstowy jest aktualizowany kompletnym tekstem, a polecenie Anuluj
zostaje wyczone.
Warto zwrci uwag na sposb uruchomienia obiektu klasy SwingWorker w suchaczu zda-
rze elementu menu Otwrz.
javax.swing.SwingWorker<T, V> 6
abstract T doInBackground()
T metod naley przedefiniowa, aby wykonywaa zadanie w tle i zwracaa wynik
swojego dziaania.
void process(List<V> data)
T metod naley przedefiniowa, aby przetwarzaa porednie dane przetwarzania
w wtku dystrybucji zdarze.
void publish(V... data)
Przesya porednie dane postpu do wtku dystrybucji zdarze. Naley j wywoywa
w metodzie doInBackground.
Rozdzia 14. Wielowtkowo 827
void execute()
Planuje wykonanie obiektu klasy SwingWorker w wtku roboczym.
SwingWorker.StateValue getState()
Sprawdza stan obiektu SwingWorker PENDING, STARTED lub DONE.
W przeszoci zasada jednego wtku bya mniej restrykcyjna. Kady wtek mg tworzy
komponenty, ustawia ich wasnoci i dodawa je do kontenerw, jeli aden z tych kom-
ponentw nie by realizowany. Komponent jest realizowany, kiedy moe odbiera zdarze-
nia rysowania lub walidacji. W zwizku z tym problem zaczyna si w chwili wywoania na
rzecz komponentu metody setVisible(true) lub pack (!) bd w momencie dodania go do
zrealizowanego kontenera.
828 Java. Podstawy
Tamta wersja zasady jednego wtku bya bardzo dogodna. Pozwalaa na utworzenie GUI
w metodzie main, a nastpnie wywoanie metody setVisible(true) na rzecz ramki najwy-
szego poziomu. Nie byo potrzeby kopotliwego planowania obiektw implementujcych
interfejs Runnable na wtku dystrybucji zdarze.
Problemy te mona by byo odnale i naprawi, ale projektanci Swinga wybrali atwiejsze
rozwizanie. Orzekli, e bezpieczny dostp do komponentw mona uzyska wycznie
w wtku dystrybucji zdarze. Dlatego interfejs uytkownika musi by konstruowany w wtku
dystrybucji zdarze przy uyciu metody EventQueue.invokeLater, ktr ogldalimy we
wszystkich przykadowych programach.
Oczywicie istnieje mnstwo programw, ktre wci dziaaj zgodnie ze star wersj zasady
jednego wtku, czyli inicjuj interfejs uytkownika w gwnym wtku. W aplikacjach tych
istnieje ryzyko, e inicjacja interfejsu spowoduje dziaania w wtku dystrybucji zdarze bdce
w konflikcie z dziaaniami w wtku gwnym. Jak pisalimy w rozdziale 7., nikt nie chce by
tym nieszczliwcem, ktremu si to przytrafi i ktry bdzie musia powici mnstwo
czasu na odnalezienie bdu. Dlatego najlepiej cile trzyma si zasady jednego wtku.
W tym miejscu koczy si pierwszy tom Core Java. Opisano w nim podstawy jzyka Java
oraz niektre fragmenty jego API, ktre s potrzebne w wikszoci projektw programi-
stycznych. Mamy nadziej, e podobaa Ci si podr przez podstawowe zagadnienia zwi-
zane z Jav i e udao Ci si tu znale przydatne wiadomoci. Dodatkowe informacje na
temat programowania sieciowego, zaawansowanego programowania AWT i Swing, bez-
pieczestwa aplikacji czy internacjonalizacji zostay zawarte w drugim tomie.
A
Sowa kluczowe
Javy
Sowo kluczowe Opis Rozdzia
abstract Abstrakcyjna klasa lub metoda 5.
assert Lokalizacja wewntrznych bdw programu 11.
boolean Typ logiczny 3.
break Przerywa dziaanie instrukcji switch lub ptli 3.
byte Omiobitowy typ cakowitoliczbowy 3.
case Klauzula instrukcji switch 3.
catch Klauzula bloku try przechwytujca wyjtek 11.
char Typ znaku Unicode 3.
class Definicja klasy 4.
const Nieuywane
continue Przekazuje sterowanie na koniec ptli 3.
default Domylna klauzula instrukcji switch 3.
do Grna cz ptli do-while 3.
double Liczby zmiennoprzecinkowe o podwjnej precyzji 3.
else Klauzula else instrukcji if 3.
enum Wyliczenie 3.
extends Definicja klasy nadrzdnej innej klasy 4.
final Staa, klasa lub metoda, ktrej nie mona przesoni 5.
finally Zawsze wykonywana cz bloku try 11.
float Zmiennoprzecinkowa liczba o pojedynczej precyzji 3.
830 Java. Podstawy
B
C
bariera cykliczna, 814
bariery, 814 catch, 250
bazowy katalog drzewa pakietu, 187 cechy
bezpieczestwo, 25, 35 jzyka, 22, 33
typw, 639, 649 komponentu, 393
wtkw, 775 certyfikat
biaa ksiga Javy, 22 bezpieczestwa, 524
biblioteka, 33 niebezpieczny, 525
AWT, 314, 387 wasny, 524
fdlibm, 74 chwytanie typu wieloznacznego, 655
IFC, 314 ciao metody, 60
Java2D, 332, 333 czasochonne zadania, 816
JFC, 314 czcionki, 343
kolekcji, 666, 707 bezszeryfowe, 346
refleksyjna, 247 nazwy, 343
STL, 666 nazwy logiczne, 344
Swing, 315, 815 PostScript type 1, 345
biblioteki styl, 345
struktur danych, 666 TrueType, 345
zabezpiecze, 25 wysoko, 346
bit, 729
blok, 98
inicjujcy, 175 D
try-catch, 250, 578, 585 dane, 195
try-finally, 578
binarne, 26
blokada, 762, 769 wejciowe, 89
jawna, 770 wyjciowe, 91
odczytu, 783 debuger, 28, 621
uczciwa, 764 debuger JSwat, 623
wewntrzna, 770 debugowanie, 609
zapisu, 784 debugowanie aplikacji z GUI, 614
bloki synchronizowane, 774 definiowanie
blokowanie po stronie klienta, 774, 775 klasy, 148
bd klasy oglnej, 630
AssertionError, 587 kolorw, 340
pomyki o jeden, 719 suchacza, 356
ThreadDeath, 752 staej klasowej, 69
typu, 650 wtku, 745
typu cannot read, 46 wyjtkw kontrolowanych, 567
bdy zmiennej, 66, 68
danych wejciowych, 564 zmiennej obiektowej, 138
kompilacji, 49, 153, 181, 629, 646 zmiennej tablicowej, 117
programisty, 568
Skorowidz 833
etykiety, 459
HTML, 409 H
komponentw, 408 harmonogram
ewolucja Javy, 32 wykonywania wtkw, 750
zada, 696
F haso, 90, 410
hermetyzacja, 133, 155
figury hierarchia
2D, 332 dziedziczenia, 206
geometryczne, 333 dziedziczenia interfejsu Type, 660
filtr dziedziczenia klasy Component, 400
plikw, 496, 497, 505 dziedziczenia klasy JFrame, 321
rekordw, 600 interfejsw, 278
firma wyjtkw, 566, 586
Oracle, 32, 34 zdarze, 387
Sun Fellow, 30 historia Javy, 30
Sun Microsystems, 25, 34 HTML, 33
format
binarny liczby, 63
JNLP, 519 I
Unicode, 26 IDE, 39, 47
XML, 556 identyfikacja klas, 134
formatery, 600 IFC, Internet Foundation Classes, 314
formatowanie ikony, 435
danych wyjciowych, 91 ikony komunikatw, 475
daty, 95 implementacja
funkcja unexpected, 569 apletw, 533
funkcje ArrayList, 644
czysto wirtualne, 216 interfejsu, 271, 273
matematyczne, 73 klasy Bank, 770
sieciowe, 24 klasy oglnej, 629
skadowe, 60 kolejki, 667
import
G klas, 180
statyczny, 182
GC, Garbage Collector, 702 indeks, 123
generowanie indeks argumentu, 95
dokumentacji, 194 informacje
obiektw klas oglnych, 646 o klasie, 252
generyczne o typach, 28
klasy, 629 o typach czasu wykonywania, 248
listy tablicowe, 233 o typach generycznych, 659
generyczny kod tablicowy, 261 o typach obiektw, 248
graficzny interfejs uytkownika, GUI, 28, 57, 313, o uruchomionym programie, 613
391, 614 o zdarzeniach, 602
grafika, 313 inicjalizacja
grupa, 754 pl, 172
przyciskw radiowych, 416 pl statycznych, 176
wtkw, 755 pl wartociami domylnymi, 171
zada, 808 tablic, 118
GTK, 316 z podwjn klamr, 302
zmiennej obiektowej, 138
zmiennych, 68
Skorowidz 835
L
M
licencja GPL, 34
liczba makro assert, 588
klikni, 381 manifest, 512
parametrw, 244 mapa, 697
liczby akcji, 376
cakowite, 62 HashMap, 698
zmiennoprzecinkowe, 64 haszowa, 795
linia wejcia, 376
bazowa, 346, 347 wasnoci, property map, 550, 553, 728
dolna pisma, 346 mapy klawiaturowe, 376
grna pisma, 346 maska bitowa, 380
lista maszyna wirtualna, JVM, 26, 514
ArrayList, 117 opcja -verbose, 612
kluczy, 556, 698 opcja -Xlint, 612
modyfikowalna, 721 opcja -Xprof, 614
rozwijalna, combo box, 423 ME, Micro Edition, 38
wtkw, 779 meneder
listy dziennikw, 595
cykliczne, 666, 668 zabezpiecze, 522
dwukierunkowe, 25, 238, 674 menu, 432
powizane, 668, 674, 680 menu podrczne, pop-up menu, 437
tablicowe, 234, 236, 684 metadane, 32
surowe, 239 metoda, 133
z typem, 240 accept, 504
lokalizacja, 143, 596 acquire, 812
komunikatw, 596 actionPerformed, 286, 291, 357, 368, 414, 433
pliku, 546 add, 142, 237, 399, 433, 629, 677
lokalne klasy wewntrzne, 289, 296 addActionListener, 364
addAll, 629, 672, 682
addBall, 737, 742
addChangeListener, 426
addChoosableFileFilter, 504
adowanie
addComponent, 467
klas, 307
addContainerGap, 468
pliku w osobnym wtku, 821
addFirst, 683
zasobw, 516
addGap, 467
842 Java. Podstawy
showInternalInputDialog, 484 toString, 88, 118, 228, 259, 310, 424, 584, 610
showInternalMessageDialog, 482 toUpperCase, 84
showMessageDialog, 288, 474, 481, 530 transfer, 756, 761, 770, 793
showOptionDialog, 474, 476 trim, 85, 407
showSaveDialog, 495 trimToSize, 235, 236
showStatus, 548, 549 tryLock, 782, 783
shuffle, 721, 722 tryTransfer, 793
shutdown, 803 UIManager.setLookAndFeel, 366
shutdownNow, 804 uncaughtException, 754, 755
signal, 769, 780 unlock, 762, 764
signalAll, 766769 unmodifiableCollection, 713
size, 236, 672, 673 unmodifiableList, 713
sleep, 737, 742 unmodifiableSet, 713
sort, 121, 274, 720 update, 240
start, 288 userNodeForPackage, 560
startsWith, 84 userRoot, 560
stateChanged, 426 validate, 407, 408
stop, 288, 752, 784 valueOf, 114, 243, 246
store, 550, 554, 728 wait, 773
subList, 716 warning, 605
subMap, 712, 716 windowActivated, 369, 372
submit, 803, 809 windowClosed, 369, 372
subSet, 712, 716 windowClosing, 369, 370, 372
substring, 78, 84, 711 windowDeactivated, 369, 372
subtract, 115, 116 windowDeiconified, 369, 372
super.clone, 282 windowIconified, 369, 372
super.paintComponent, 330 windowOpened, 369, 372
suspend, 752, 785 windowStateChanged, 372
swap, 168, 724 writeLock, 784
swapHelper, 656 xor, 730
SwingUtilities.updateComponentTreeUI, 366 metody
synchronizedCollection, 713, 797 abstrakcyjne, 215
synchronizedList, 797 akcesora, 155
synchronizedMap, 713, 797 fabryczne, 161, 704, 803
synchronizedSet, 797 finalne, 211, 770
synchronizedSortedMap, 797 graficzne, 332
synchronizedSortedSet, 797 interfejsu
System.exit, 60, 320 Action, 373
System.out.println, 89 BlockingDeque<E>, 793
System.runFinalizersOnExit, 179 BlockingQueue<E>, 792
systemNodeForPackage, 560 Collection, 672
systemRoot, 560 Collection<E>, 672
tailMap, 712, 716 Deque<E>, 695
tailSet, 712, 716 Future<V>, 801
take, 793 GenericArrayType, 664
takeFirst, 793 Iterator<E>, 674
takeLast, 793 LinkedList<E>, 683
text, 91 List<E>, 682
Thread.getAllStackTraces, 581 ListIterator<E>, 683
ThreadLocalRandom.current, 781 Map<K, V>, 700
throwing, 594, 606 NavigableSet<E>, 694
toArray, 237, 645, 672, 718 ParameterizedType, 664
toBack, 326 Queue<E>, 695
toFront, 322, 326 SortedSet, 712
848 Java. Podstawy
N O
nadklasa, superclass, 200 obiekt, 24, 132
nadtypy, 652, 654 Action, 446
NaN, 64, 70 ActionEvent, 357
narzdzia AssertionError, 587
graficzne, 317 BasicButtonUI, 398
wiersza polece, 44 builder, 86
narzdzie ButtonGroup, 415
appletviewer, 53, 535 Callable, 803
ButtonTest, 618 ColorAction, 360
ImageViewer, 51, 500 Comparator, 693
jar, 512, 514 Console, 90
Jar Bundler, 515 Date, 138
javac, 45 DefaultButtonModel, 398
javadoc, 190195 ExecutorCompletionService, 808
javap, 294 FutureTask, 798
jconsole, 595, 613, 779 Graphics, 328, 341
jmap, 614 GridLayout, 406
Matisse, 460, 461 Handler, 595, 598, 608
OptionDialogTest, 477 Iterator, 672
ReflectionTest, 295, 296 JButton, 397
Swing graphics debugger, 614 JPanel, 402
natywna biblioteka interfejsowa, 33 JRadioButton, 415
nawiasy PaintEvent, 388
klamrowe, 59, 300 Path, 97
kwadratowe, 116 PrintWriter, 96, 97
ostre, 632 Properties, 553
puste, 234 Runnable, 759, 803
nazwa Scanner, 96
akcji, 377 System.out, 60
klasy, 45, 58, 134, 197 WeakReference, 702
konstruktora, 137 obiektw, 133
parametru, 174 autoboxing, 241
pliku, 45 hermetyzacja, 133
pliku dziennika, 598 klonowanie, 280
rejestratora, 595 kopiowanie, 280
zasobu, 516 metody, 133
zmiennej, 67 metody prywatne, 157
zmiennej typowej, 630 niszczenie, 179
nazwy polimorfizm, 204
klas komponentw Swing, 318 porwnywanie, 221
klas proxy, 311 skadowe, 133, 155
logiczne czcionek, 344 stan, 133, 134
metod, 197 tosamo, 134
rodziny czcionek, 343 waciwoci, 133
NetBeans, 38, 44, 459 zachowanie, 133
niezaleno od architektury, 26 obiekty
niezawodno, 24 blokady, 762
niezmienialno acuchw, 79 funkcyjne, 690
niszczenie obiektw, 179 klasy Class, 249
notacja wielbdzia, 58 klasy Lock, 762
klasy oglnej, 646
nasuchujce zdarze, listener objects, 287, 371
850 Java. Podstawy
wtki wczanie
stan asercji, 588
BLOCKED, 749 zegara, 293
NEW, 749 wnioskowanie o typie, 632
RUNNABLE, 749 wprowadzanie tekstu, 406
TERMINATED, 749 wskaniki, 25
TIMED WAITING, 749 do funkcji, 286
WAITING, 749 do metod, 264
synchronizowane, 763 do obiektw, 140
usunite z kolejki, 766 wspczynnik zapenienia tablicy, 686
wywaszczanie, 760 wsprzdne
zamienianie w demona, 753 figur, 333336
zamknicie, 752 kodowe znakw, 66, 81
wczytywanie zasobw, 516 siatki, 451
wejcie, 89 wstawianie komentarzy, 190
wejcie System.in, 96 wybr
wersje Javy, 32 kolorw, 505
wze, 556, 560 plikw, 495
wizanie wyciszanie wyjtkw, 586
dynamiczne, 204, 209211 wydajno, 27
wyduenie
statyczne, 209
dolne, 346
widoczno metod, 211
grne, 346
widok, view, 394, 710
wyjtek
kolekcji, 715
ArrayIndexOutOfBounds, 117
listowy elementw, 716
ArrayIndexOutOfBoundsException, 566, 816
mapy, 698
ArrayStoreException, 642, 650
podprzedziau, 717 BadCastException, 658
pola tekstowego, 394 Class.forName, 250
widoki ClassCastException, 213, 262, 650, 714
kontrolowane, 714 CloneNotSupportedException, 283
niemodyfikowalne, 712 ConcurrentModificationException, 679, 797
przedziaowe, 711 EmptyStackException, 584, 586
synchronizowane, 713 FileNotFoundException, 97, 528, 569, 648
wielkie liczby, 114 IllegalAccessException, 257
wielko liter, 58 IllegalMonitorStateException, 773
wielowtkowo, 28, 33, 735 IllegalStateException, 670, 674, 683
wielozadaniowo, 735 InterruptedException, 737, 742, 748
wiersz polece, 44, 120 IOException, 569, 572
wizualne budowanie interfejsw, 51 NoSuchElementException, 674, 695
wasne typy wyjtkw, 571 NullPointerException, 566, 586, 590
wasnoci RuntimeException, 565, 566
czcionki, 450, 454 ServletException, 575
interfejsw, 276 ThreadDeath, 785
interfejsu ButtonModel, 397 typu RuntimeException, 583
klas proxy, 311 UnavailableServiceException, 526
metody equals, 222 UnsupportedOperationException, 711715
monitorw, 775 wyjtki
ramek, 322, 551 kontrolowane, 249, 567, 646
wtkw, 752 konwersji, 629
wasno, property, 322 niekontrolowane, 250, 567, 647
waciwoci typu IOException, 569
list, 721 typu SQLException, 576
systemowe, 554 zabezpiecze, 554
wykonawcze, 565
Skorowidz 859
wyjtkw, 563
deklarowanie, 567 X
powtrne generowanie, 575 XML, 33
przechwytywanie, 250, 571
przechwytywanie wielu typw, 574
przekazywanie, 586 Z
wasne typy, 571
zgaszanie, 569 zachowanie obiektu, 133
wyjcie, 89 zagniedanie
wyjcie System.out, 96 blokw instrukcji, 98
wykres, 543 ptli, 113
wykres supkowy, 542 zakleszczenie, deadlock, 766, 778
wyliczenia, 727 zaleno, 135
wyczanie zamiana parametrw obiektowych, 168
asercji, 588 zamykanie
dziedziczenia, 211 aplikacji, 320
sprawdzania wyjtkw, 646 ramki, 320
wymazywanie typw, 637648 wtkw, 752
wymiana danych, 489 zapenianie tablicy, 237
wymuszanie rysowania, 329 zapis
wypenianie figur, 340 bdw w pliku, 611
wyraenia generyczne, 637 danych w repozytorium, 556
wyrwnywanie etykiet i pl, 462 do dziennika, 592594
WYSIWYG, 395 plikw, 96
wysyanie preferencji uytkownika, 549
rekordw, 597 zarzdca rozkadu, layout manager, 391, 400, 448
zdarze, 319 CircleLayout, 469
wyszukiwanie binarne, 722 brzegowego, 400
wycig, 756, 760 cigego, 398
wywietlanie FlowLayout, 402
elementw w przegldarce, 548 grupowego, 449
informacji, 327 niestandardowy, 469
komponentw, 614 siatkowego, 402
obrazw, 52, 351 zasada
ramki, 320 jednego wtku, 816, 827
rekordw dziennika, 604 uczciwoci, 764
tekstu, 327, 329 zamienialnoci, 207
zasobu, 515 zasig
wywaszczanie wtku, 750, 760 blokowy, 98
wywoanie pakietw, 185
dowolnych metod, 264 zmiennych, 98
innego konstruktora, 174 zasoby, resources, 515
przez nazw, 165 zastosowanie
przez referencj, 164 asercji, 589, 590
przez warto, 164 klas abstrakcyjnych, 279
setFirst(null), 655 klas wewntrznych, 294
wzorce kolejek priorytetowych, 696
nazw plikw dziennika, 599 parametrw Class<T>, 659
projektowe, 392 refleksji, 252
wzorzec typw wyliczeniowych, 246
Composite, 393 zawarto pl danych, 257
Decorator, 393 zawijanie wierszy, 411
MVC, 392396 zbir, set, 686, 697
Strategy, 393 HashSet, 684
TreeSet, 688691
uporzdkowany, 688
860 Java. Podstawy