Professional Documents
Culture Documents
pl
Ksigarnia internetowa
Lubi to! Nasza spoeczno
Spis treci
Wstp ........................................................................................................................................................13
Podzikowania .........................................................................................................................................19
Rozdzia 1. Wstp do Javy ........................................................................................................................21
1.1.
1.2.
1.3.
1.4.
1.5.
2.2.
2.3.
2.4.
Java. Podstawy
2.5.
2.6.
Spis treci
4.2.
4.3.
4.4.
4.5.
4.6.
4.7.
4.8.
4.9.
Java. Podstawy
4.9.5. Komentarze oglne ............................................................................. 192
4.9.6. Komentarze do pakietw i oglne ........................................................ 194
4.9.7. Generowanie dokumentacji .................................................................. 194
4.10. Porady dotyczce projektowania klas ................................................................. 195
5.2.
5.3.
5.4.
5.5.
5.6.
5.7.
5.8.
6.2.
6.3.
6.4.
6.5.
Spis treci
7.4.
7.5.
7.6.
7.7.
7.8.
8.2.
8.3.
8.4.
9.2.
9.3.
9.4.
9.5.
Java. Podstawy
9.6.
9.7.
Spis treci
10
Java. Podstawy
13.2. Konkretne klasy kolekcyjne ............................................................................... 674
13.2.1. Listy powizane ................................................................................. 674
13.2.2. Listy tablicowe .................................................................................. 684
13.2.3. Zbir HashSet ................................................................................... 684
13.2.4. Zbir TreeSet .................................................................................... 688
13.2.5. Porwnywanie obiektw ..................................................................... 689
13.2.6. Kolejki Queue i Deque ....................................................................... 694
13.2.7. Kolejki priorytetowe ........................................................................... 696
13.2.8. Mapy ................................................................................................ 697
13.2.9. Specjalne klasy Set i Map .................................................................. 702
13.3. Architektura kolekcji ......................................................................................... 706
13.3.1. Widoki i obiekty opakowujce ............................................................. 709
13.3.2. Operacje zbiorcze .............................................................................. 717
13.3.3. Konwersja pomidzy kolekcjami a tablicami ......................................... 718
13.4. Algorytmy ........................................................................................................ 718
13.4.1. Sortowanie i tasowanie ...................................................................... 720
13.4.2. Wyszukiwanie binarne ........................................................................ 722
13.4.3. Proste algorytmy ................................................................................ 723
13.4.4. Pisanie wasnych algorytmw .............................................................. 725
13.5. Stare kolekcje ................................................................................................. 726
13.5.1. Klasa Hashtable ................................................................................ 726
13.5.2. Wyliczenia ......................................................................................... 727
13.5.3. Mapy wasnoci ................................................................................. 728
13.5.4. Stosy ................................................................................................ 729
13.5.5. Zbiory bitw ...................................................................................... 729
Spis treci
11
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 sieciowego, bazy danych, serwisu informacyjnego, czy jakiegokolwiek innego rda. I rzeczywicie, 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 programistw, jak i uytkownikw programw napisanych w Javie. Dziki wbudowanym funkcjom
zaawansowane zadania programistyczne, takie jak programowanie sieciowe, czno pomidzy bazami danych i wielowtkowo, s znacznie prostsze.
Do tej pory pojawio si ju osiem wersji pakietu Java Development Kit. Przez ostatnich osiemnacie 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 tworzenia aplikacji, jak konstruowanie interfejsu uytkownika, zarzdzanie bazami danych, internacjonalizacja, 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 Standard Edition (SE) 7.
Tak jak w przypadku poprzednich wyda tej ksiki, to rwnie przeznaczone jest dla powanych 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 kadej 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,
obsuga wyjtkw,
programowanie generyczne,
kolekcje,
wspbieno.
Ze wzgldu na niebyway wrcz rozwj biblioteki klas Javy opisanie w jednym tomie wszystkich wasnoci tego jzyka, ktrych potrzebuje powany programista, graniczyoby z cudem.
Z tego powodu postanowilimy podzieli nasz ksik na dwa tomy. Pierwszy, ktry trzymasz w rku, opisuje podstawy jzyka Java oraz najwaniejsze zagadnienia zwizane z programowaniem interfejsu uytkownika. Tom drugi (ktry niebawem si ukae) zawiera informacje 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,
metody rodzime,
programowanie sieciowe,
Wstp
JavaBeans,
adnotacje.
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 informacjami. 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. Nastpnie 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, aplikacji graficznej i apletu. Naszymi narzdziami s czyste rodowisko JDK, edytor tekstowy
obsugujcy Jav i zintegrowane rodowisko programowania (ang. Integrated Development
Environment IDE) dla Javy.
Od rozdziau 3. zaczynamy opis jzyka programowania Java. Na pocztku zajmujemy si podstawami: zmiennymi, ptlami i prostymi funkcjami. Dla programistw jzykw C i C++ bdzie
to buka z masem, poniewa Java i C w tych sprawach w zasadzie niczym si nie rni.
Programici innych jzykw, takich jak Visual Basic, powinni bardzo starannie zapozna
si z treci tego rozdziau.
Obecnie najpopularniejsz metodologi stosowan przez programistw jest programowanie
obiektowe, a Java to jzyk w peni obiektowy. W rozdziale 4. wprowadzamy pojcie hermetyzacji (ang. encapsulation), ktra stanowi jeden z dwch filarw programowania obiektowego, oraz piszemy o mechanizmach Javy sucych do jej implementacji, czyli o klasach
i metodach. Poza opisem zasad rzdzcych jzykiem Java dostarczamy take informacji na
temat solidnego projektowania programw zorientowanych obiektowo. Na kocu powicamy
nieco miejsca doskonaemu narzdziu o nazwie javadoc, sucemu do konwersji komentarzy
zawartych w kodzie na wzajemnie poczone hiperczami strony internetowe. Osoby znajce jzyk C++ mog przejrze ten rozdzia pobienie. Programici niemajcy dowiadczenia
w programowaniu obiektowym musz si liczy z tym, e opanowanie wiedzy przedstawionej
w tym rozdziale zajmie im troch czasu.
16
Java. Podstawy
Klasy i hermetyzacja to dopiero poowa koncepcji programowania zorientowanego obiektowo.
Rozdzia 5. wprowadza drug, czyli dziedziczenie. Mechanizm ten umoliwia modyfikacj
istniejcych ju klas do wasnych specyficznych potrzeb. Jest to podstawowa technika programowania zarwno w Javie, jak i C++, a oba te jzyki maj pod tym wzgldem wiele ze sob
wsplnego. Dlatego te programici C++ mog rwnie w tym rozdziale skupi si tylko na
rnicach pomidzy tymi dwoma jzykami.
W rozdziale 6. nauczymy si posugiwa interfejsami w Javie, ktre wykraczaj poza prosty model dziedziczenia opisywany w poprzednim rozdziale. Opanowanie technik zwizanych z interfejsami da nam peny dostp do moliwoci, jakie stwarza w peni obiektowe
programowanie w Javie. Ponadto opisujemy bardzo przydatne w Javie klasy wewntrzne
(ang. inner classes). Pomagaj one w pisaniu bardziej zwizego i przejrzystego kodu.
Od rozdziau 7. zaczynamy powane programowanie. Jako e kady programista Javy powinien zna si na programowaniu GUI, w tym rozdziale opisujemy podstawy tego zagadnienia.
Nauczysz si tworzy okna, rysowa w nich, rysowa figury geometryczne, formatowa tekst
przy zastosowaniu rnych krojw pisma oraz wywietla obrazy.
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.
Rozdzia 11. powicilimy obsudze wyjtkw doskonaemu mechanizmowi pozwalajcemu radzi sobie z tym, e z dobrymi programami mog dzia si ze rzeczy. Wyjtki s skutecznym sposobem na oddzielenie kodu normalnego przetwarzania od kodu obsugujcego
bdy. Oczywicie nawet zabezpieczenie w postaci obsugi wszystkich sytuacji wyjtkowych
nie zawsze uchroni nas przed niespodziewanym zachowaniem programu. W drugiej czci tego
rozdziau zawarlimy mnstwo wskazwek dotyczcych usuwania bdw z programu. Na
kocu opisujemy krok po kroku ca sesj debugowania.
W rozdziale 12. przedstawiamy zarys programowania oglnego najwaniejszej nowoci
w Java SE 5.0. Dziki tej technice mona tworzy atwiejsze do odczytu i bezpieczniejsze
programy. Przedstawiamy sposoby stosowania cisej kontroli typw oraz pozbywania si
szpetnych i niebezpiecznych konwersji. Ponadto nauczysz si radzi sobie w sytuacjach, kiedy
trzeba zachowa zgodno ze starszymi wersjami Javy.
Wstp
17
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 rozdziale 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 programowanie 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.
Dodatek zawiera list sw zarezerwowanych w jzyku Java.
Konwencje typograficzne
Podobnie jak w wielu ksikach komputerowych, przykady kodu programw pisane s
czcionk o staej szerokoci znakw.
Tak ikon opatrzone s uwagi.
18
Java. Podstawy
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 pozosta 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 pracujcych w zapleczu. Jestem wdziczny Julie Nahil za doskonae wsparcie w dziedzinie produkcji, oraz Dmitryemu i Alinie Kirsanovom za korekt i skad. Dzikuj rwnie mojemu
wspautorowi poprzednich wyda tej ksiki Garyemu Cornellowi, ktry podj inne
wyzwania.
Dzikuj wszystkim Czytelnikom poprzednich wyda tej ksiki za informacje o enujcych
bdach, ktre popeniem, i komentarze dotyczce ulepszenia mojej ksiki. Jestem szczeglnie wdziczny znakomitemu zespoowi korektorw, ktrzy czytajc wstpn wersj tej
ksiki i wykazujc niebywa czuo na szczegy, uratowali mnie przed popenieniem
jeszcze wikszej liczby bdw.
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 University), William Higgins (IBM), Vladimir Ivanovic (PointBase), Jerry Jackson (CA Technologies), 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 Morrissey (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 Sanghera (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
Wstp do Javy
W tym rozdziale:
Pierwsze wydanie Javy w 1996 roku wywoao ogromne emocje nie tylko w prasie komputerowej, 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 komputerowego. Wracanie dzisiaj do tych wietnych czasw jest bardzo zabawne, a wic w tym
rozdziale krtko opisujemy histori jzyka Java.
22
Java. Podstawy
Za ten akapit na naszego redaktora posypay si gromy ze strony kogo bardzo wysoko postawionego w firmie Sun Microsystems, kogo nazwiska wolimy nie ujawnia. Jednak z perspektywy czasu wydaje si, e nasze przewidywania byy suszne. Java ma mnstwo bardzo poytecznych 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. Niektre 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.
prosty,
2. obiektowy,
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
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 bazowymi 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 automatycznie 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.
Wyznacznikiem prostoty s take niewielkie rozmiary. Jednym z celw Javy
jest umoliwienie tworzenia oprogramowania dziaajcego niezalenie na maych
urzdzeniach. Rozmiar podstawowego interpretera i obsugi klas wynosi okoo
40 kilobajtw. Podstawowe standardowe biblioteki i obsuga wtkw (w zasadzie
jest to samodzielne mikrojdro) to dodatkowe 175 K.
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 stosowania 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 wielodziedziczeniu, ktre w Javie zostao zastpione prostszymi interfejsami, oraz w modelu metaklas
Javy (ktry jest opisany w rozdziale 5.).
Osoby, ktre nie miay stycznoci z programowaniem obiektowym, powinny bardzo
uwanie przeczyta rozdziay 4. 6. Zawieraj one opis technik programowania
obiektowego oraz wyjanienie, czemu ta technika lepiej nadaje si do wyrafinowanych
projektw ni tradycyjne jzyki proceduralne, takie jak C lub Basic.
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. Mechanizm 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 jawnie 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 zaimplementowa wiele rnych struktur danych. Java czy w sobie to, co najlepsze w obu tych podejciach. 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 wskanika, popenienia bdu przydzielania pamici ani koniecznoci wystrzegania si przed wyciekami 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 programistw do zbadania zabezpiecze Javy, udostpniajc publicznie specyfikacj i implementacj wirtualnej maszyny Javy oraz biblioteki zabezpiecze. Wszystkie znane bdy zabezpiecze 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.
Od samego pocztku przy projektowaniu Javy starano si uniemoliwi przeprowadzanie
niektrych rodzajw atakw, takich jak:
Pewna liczba zabezpiecze zostaa dodana do Javy z biegiem czasu. Od wersji 1.1 istnieje pojcie 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
W konkurencyjnej technologii firmy Microsoft, opartej na ActiveX, jako zabezpieczenie stosuje si tylko podpisy cyfrowe. To oczywicie za mao kady uytkownik produktw firmy Microsoft moe potwierdzi, e programy od znanych dostawcw
psuj si i mog powodowa szkody. Model zabezpiecze Javy jest o niebo lepszy ni
ten oparty na ActiveX, poniewa kontroluje dziaajc aplikacj i zapobiega spustoszeniom, ktre moe ona wyrzdzi.
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 nieporozumie zwizanych z kolejnoci bajtw. acuchy s przechowywane w standardowym formacie Unicode.
Rozdzia 1.
Wstp do Javy
27
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 stosowania 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.
Na pocztku istnienia Javy wielu uytkownikw nie zgadzao si ze stwierdzeniem, e jej
wydajno jest wicej ni wystarczajca. Jednak najnowsze kompilatory JIT s tak dobre,
e mog konkurowa z tradycyjnymi kompilatorami, a czasami nawet je przeciga, poniewa
maj dostp do wikszej iloci informacji. Na przykad kompilator JIT moe sprawdza, ktra
cz kodu jest najczciej wykonywana, i zoptymalizowa j pod ktem szybkoci. Bardziej
zaawansowana technika optymalizacji polega na eliminacji wywoa funkcji (ang. inlining).
Kompilator JIT wie, ktre klasy zostay zaadowane. Moe zastosowa wstawianie kodu
funkcji w miejsce ich wywoa, kiedy biorc pod uwag aktualnie zaadowane kolekcje
klas okrelona funkcja nie jest przesonita i moliwe jest cofnicie tej optymalizacji
w razie potrzeby.
28
Java. Podstawy
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 wieloprocesorowych, 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 wielowtkowoci 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.
Niedugo po pocztkowym sukcesie Javy firma Microsoft wydaa produkt o nazwie
J++. By to jzyk programowania i maszyna wirtualna udzco podobne do Javy.
Obecnie Microsoft nie zajmuje si ju tym projektem i zwrci si w stron innego
jzyka C#, ktry rwnie przypomina Jav. Istnieje nawet jzyk J# sucy do migracji
aplikacji napisanych w J++ na maszyn wirtualn uywan przez C#. W ksice tej nie
opisujemy jzykw J++, C# i J#.
Rozdzia 1.
Wstp do Javy
29
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 przegldarce 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 zoliwego 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 przesya dane pomidzy komputerem, na ktrym zosta uruchomiony, a komputerem, z ktrego
pochodzi.
Rysunek 1.1 przedstawia dobry przykad dynamicznej strony internetowej, na ktrej wykonywane 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 stronach 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 zauroczenie 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 najpopularniejszym jzykiem do tworzenia aplikacji dziaajcych po stronie serwera, ktre generuj
strony internetowe i stanowi ich zaplecze logiczne.
30
Java. Podstawy
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 interpretuje hipertekst i wywietla wynik na ekranie monitora. W 1994 roku wikszo uytkownikw 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 niewielu aplikacji klient-serwer nalecych do gwnego nurtu, wymagajca tych dziwnych rzeczy, ktre zrobilimy, czyli niezalenoci od architektury, pracy w czasie rzeczywistym, niezawodnoci i bezpieczestwa. W wiecie stacji roboczych pojcia te nie miay wielkiego
znaczenia. Postanowilimy wic napisa przegldark internetow.
Budow przegldarki, ktra przeobrazia si w przegldark o nazwie HotJava, zajli si
Patrick Naughton i Jonathan Payne. Przegldark HotJava naturalnie napisano w jzyku
Java, poniewa jej celem byo zaprezentowanie ogromnych moliwoci, ktre stwarza ten
jzyk. Programici pamitali jednak te o czym, co obecnie nazywamy apletami, i dodali
moliwo uruchamiania kodu wbudowanego w strony internetowe. 23 maja 1995 roku owoc
tej pracy, majcej na celu udowodnienie wartoci Javy, ujrza wiato dzienne w magazynie
SunWorld. Sta si on kamieniem wgielnym szalonej popularnoci Javy, ktra trwa do
dzisiaj.
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 aplikacji. Oczywicie mona byo za jej pomoc stworzy nerwowo poruszajcy si tekst w obszarze 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, uzupeniono najbardziej oczywiste braki, znacznie ulepszono refleksj i dodano model zdarze dla
programowania GUI. Jednak nadal moliwoci byy raczej ograniczone.
Wielkim wydarzeniem na konferencji JavaOne w 1998 roku byo ogoszenie, e niebawem
pojawi si Java 1.2. Zastpiono w niej dziecinne narzdzia do obrbki grafiki i tworzenia
GUI wyrafinowanymi i skalowalnymi wersjami, ktre znacznie przybliay spenienie obietnicy: Napisz raz, uruchamiaj wszdzie w stosunku do poprzednich wersji. Trzy dni po jej
wydaniu (!), w grudniu 1998 roku, dzia marketingu firmy Sun zmieni nazw Java 1.2 na
bardziej chwytliw Java 2 Standard Edition Software Development Kit Version 1.2.
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 oczywicie poprawiono wiele bdw. W tym samym czasie ucicha wrzawa wok apletw i aplikacji dziaajcych po stronie klienta, a Java staa si najczciej wybieran platform do tworzenia aplikacji dziaajcych po stronie serwera.
32
Java. Podstawy
Pierwsza wersja Javy, w ktrej wprowadzono znaczce zmiany w jzyku programowania
Java w stosunku do wersji 1.1, miaa numer 5 (pierwotnie by to numer 1.5, ale na konferencji JavaOne w 2004 roku podskoczy do pitki). Po wielu latach bada dodano typy sparametryzowane (ang. generic types), ktre mona z grubsza porwna do szablonw w C++.
Sztuka polegaa na tym, aby przy dodawaniu tej funkcji nie zmienia nic w maszynie wirtualnej. Niektre z dodanych funkcji zostay zaczerpnite z jzyka C#: ptla for each, moliwo automatycznej konwersji typw prostych na referencyjne i odwrotnie (ang. autoboxing)
oraz metadane.
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.
W centrach danych zaczto rzadziej korzysta ze specjalistycznego sprztu serwerowego,
przez co firma Sun Microsystems wpada w tarapaty i w 2009 roku zostaa wykupiona przez
Oracle. Rozwj Javy zosta na duszy czas wstrzymany. Jednak w 2011 roku firma Oracle
opublikowaa kolejn wersj jzyka z drobnymi ulepszeniami o nazwie Java 7. Powaniejsze zmiany przeoono do wersji Java 8, ktrej ukazanie si jest planowane na 2013 rok.
Tabela 1.1 przedstawia ewolucj jzyka Java i jego biblioteki. Jak wida, rozmiar interfejsu
programistycznego (API) rs w rekordowym tempie.
Rok
1.0
1996
Powstanie jzyka
211
1.1
1997
Klasy wewntrzne
477
1.2
1998
Brak
1524
1.3
2000
Brak
1840
1.4
2002
Asercje
2723
5.0
2004
3279
2006
Brak
3793
2011
4024
Rozdzia 1.
Wstp do Javy
33
34
Java. Podstawy
Po pojawieniu si jzyka C# Java idzie w zapomnienie.
Jzyk C# przej wiele dobrych pomysw od Javy, jak czysto jzyka programowania,
maszyna wirtualna czy automatyczne usuwanie nieuytkw. Jednak z niewiadomych przyczyn 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.
Java jest wasnoci jednej firmy i dlatego naley jej unika.
Po utworzeniu Javy firma Sun Microsystems udzielaa darmowych licencji na Jav dystrybutorom i uytkownikom kocowym. Mimo e firma ta sprawowaa pen kontrol nad Jav,
w proces tworzenia nowych wersji jzyka i projektowania nowych bibliotek zostao zaangaowanych wiele firm. Kod rdowy maszyny wirtualnej i bibliotek by zawsze oglnodostpny, 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 ukadach 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. Najczciej uywane czci kodu dziaaj tak szybko, jakby byy napisane w C++, a w niektrych 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.
Wszystkie programy pisane w Javie dziaaj na stronach internetowych.
Wszystkie aplety Javy dziaaj wewntrz przegldarki. Takie s z zaoenia aplety s to
programy napisane w Javie, ktre dziaaj wewntrz okna przegldarki. Jednak wikszo
programw pisanych w Javie to samodzielne aplikacje, dziaajce poza przegldark internetow. W rzeczywistoci wiele programw w Javie dziaa po stronie serwera i generuje kod
stron WWW.
Rozdzia 1.
Wstp do Javy
35
36
Java. Podstawy
rodowisko
programistyczne Javy
W tym rozdziale:
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 programistycznym. Opisalimy jedno dostpne bezpatnie rodowisko, w ktrym mona kompilowa 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 napisanych w tym jzyku. Jeli opanujesz techniki opisywane w tym rozdziale i wybierzesz odpowiednie dla siebie narzdzia programistyczne, moesz przej do rozdziau 3., od ktrego
zaczyna si opis jzyka programowania Java.
38
Java. Podstawy
Akronim
Objanienie
JDK
Standard Edition
SE
Enterprise Edition
EE
Micro Edition
ME
Java 2
J2
SDK
Update
NetBeans
Znamy ju skrt JDK oznaczajcy Java Development Kit. eby nie byo za atwo, informujemy, e wersje od 1.2 do 1.4 tego pakietu miay nazw Java SDK (ang. Software Development 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 uytkownikw 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.
39
Czasami mona te spotka termin Java 2, ktry zosta ukuty w 1998 roku przez dzia marketingu 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 zdecydowano 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 Standard 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.
Na zakoczenie trzeba doda, e mniejsze zmiany wprowadzane w celu naprawienia usterek
przez firm Oracle nazywane s aktualizacjami (ang. updates). Na przykad pierwsza aktualizacja pakietu programistycznego dla Java SE 7 ma oficjaln nazw JDK 7u1, ale jej
wewntrzny numer wersji to 1.7.0_01. Aktualizacje nie musz by instalowane na bazie starszych wersji zawieraj najnowsze wersje caego pakietu JDK.
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.
W trakcie instalacji sugerowany jest domylny katalog na pliki, w ktrego nazwie
znajduje si numer wersji pakietu JDK, np. jdk1.7.0. Na pierwszy rzut oka wydaje
si to niepotrzebn komplikacj, ale spodobao nam si to z tego wzgldu, e w ten
sposb o wiele atwiej mona zainstalowa nowe wydanie JDK do testowania.
Uytkownikom systemu Windows odradzamy akceptacj domylnej cieki ze spacjami
w nazwie, jak C:\Program Files\jdk1.7.0. Najlepiej usun z tej cieki cz Program
Files.
W tej ksice katalog instalacji okrelamy mianem jdk. Kiedy na przykad piszemy o katalogu jdk/bin, mamy na myli ciek typu /usr/local/jdk1.7.0/bin lub C:\jdk1.7.0\bin.
W systemie Unix (wliczajc Linux, Mac OS X i Solaris) sposb edycji cieki dostpu
zaley od uywanej powoki. Uytkownicy powoki Bourne Again (ktra jest
domylna dla systemu Linux) musz na kocu pliku ~/.bashrc lub ~/.bash.profile
doda nastpujcy wiersz:
export PATH=jdk/bin:$PATH
40
Java. Podstawy
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
Rozdzia 2.
41
Jeli zamiast tego ukae si komunikat typu javac: polecenie nie zostao znalezione lub
Nazwa nie jest rozpoznawana jako polecenie wewntrzne lub zewntrzne, program wykonywalny lub plik wsadowy, trzeba wrci do pocztku i dokadnie sprawdzi swoj instalacj.
Aby otworzy okno konsoli w systemie Windows, naley postpowa zgodnie z nastpujcymi 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
42
Java. Podstawy
Plik src.zip zawiera kod rdowy wszystkich bibliotek publicznych. Wicej rde
(dla kompilatora, maszyny wirtualnej, metod rodzimych i prywatnych klas pomocniczych) mona znale na stronie http://jdk7.java.net.
Rozdzia 2.
Struktura katalogw
Opis
jdk
demo
Przykadowe programy
docs
include
jre
lib
Pliki biblioteki
src
43
Dla uczcych si Javy najwaniejsze s katalogi docs i src. Katalog docs zawiera dokumentacj biblioteki Javy w formacie HTML. Mona j przeglda za pomoc dowolnej przegldarki internetowej, jak chociaby Firefox.
W swojej przegldarce dodaj do ulubionych stron docs/api/index.html. W trakcie poznawania platformy Java bdziesz do niej czsto zaglda.
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.
44
Java. Podstawy
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 bezpatnie. 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. Oczywicie 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 wygodne, teraz zalecamy uywanie wanie nich.
Podsumowujc, naszym zdaniem kady powinien zna podstawy obsugi narzdzi JDK, a po
ich opanowaniu przej na zintegrowane rodowisko programistyczne.
Rozdzia 2.
45
to znaczy, e uywasz bardzo starej wersji kompilatora Javy. Uytkownicy starszych wersji
Javy musz zastpi powysz ptl nastpujc:
for (int i = 0; i < greeting.length; i++)
System.out.println(greeting[i]);
Program Welcome jest niezwykle prosty. Wywietla tylko wiadomo w konsoli. Jego kod rdowy przedstawia listing 2.1 (sposb dziaania tego kodu opisujemy w nastpnym rozdziale).
Listing 2.1. Welcome/Welcome.java
/**
* Program ten wywietla wiadomo powitaln od autorw.
* @version 1.20 2004-02-28
* @author Cay Horstmann
*/
public class Welcome
{
public static void main(String[] args)
{
String[] greeting = new String[3];
greeting[0] = "Witaj, czytelniku!";
greeting[1] = "Pozdrowienia od Caya Horstmanna";
greeting[2] = "i Gary'ego Cornella";
for (String g : greeting)
System.out.println(g);
}
}
46
Java. Podstawy
Jeli kompilator javac zgosi bd typu cannot read: Welcome.java, naley sprawdzi,
czy plik ten znajduje si w odpowiednim katalogu.
W systemie Unix naley sprawdzi wielko liter w nazwie pliku Welcome.java.
W systemie Windows naley uy polecenia dir w oknie konsoli, nie w Eksploratorze.
Niektre edytory tekstu (zwaszcza Notatnik) dodaj na kocu nazwy kadego
pliku rozszerzenie .txt. Jeli program Welcome.java by edytowany za pomoc
Notatnika, to zosta zapisany jako Welcome.java.txt. Przy domylnych ustawieniach
systemowych Eksplorator dziaa w zmowie z Notatnikiem i ukrywa rozszerzenie
.txt, poniewa naley ono do znanych typw plikw. W takim przypadku trzeba
zmieni nazw pliku za pomoc polecenia ren lub zapisa go ponownie, ujmujc
nazw w cudzysowy: "Welcome.java".
Jeli po wpisaniu polecenia java Welcome maszyna wirtualna nie moe znale
klasy Welcome, naley sprawdzi, czy kto nie ustawi w systemie zmiennej
rodowiskowej CLASSPATH (ustawianie na poziomie globalnym nie jest dobrym
pomysem, ale niektre sabej jakoci instalatory oprogramowania w systemie
Windows tak wanie robi). Zmienn t mona usun tymczasowo w oknie
konsoli za pomoc polecenia:
set CLASSPATH=
Rozdzia 2.
47
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
rda).
5. Kliknij przycisk Finish (zakocz), aby utworzy projekt.
6. Aby otworzy projekt, kliknij znajdujcy si w lewym panelu obok okna projektu
48
Java. Podstawy
Rysunek 2.6.
Konfiguracja
projektu
w Eclipse
Rysunek 2.7.
Edycja kodu
rdowego
w Eclipse
Rozdzia 2.
49
Rysunek 2.8.
Uruchamianie
programu
w rodowisku
Eclipse
50
Java. Podstawy
Rysunek 2.9.
Komunikaty
o bdach
w Eclipse
Rozdzia 2.
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 umoliwia wizualnego budowania interfejsw, a wic cay kod widoczny na listingu 2.2 trzeba napisa rcznie. Pisaniem takich programw graficznych zajmiemy si w rozdziaach od 7. do 9.
Listing 2.2. ImageViewer/ImageViewer.java
import
import
import
import
java.awt.EventQueue;
java.awt.event.*;
java.io.*;
javax.swing.*;
/**
* 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
private JLabel label;
private JFileChooser chooser;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
public ImageViewerFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Uycie etykiety do wywietlenia obrazw.
label = new JLabel();
add(label);
// Dodawanie opcji wyboru obrazu.
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// Pasek menu.
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
JMenuItem openItem = new JMenuItem("Otwrz");
menu.add(openItem);
openItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Wywietlenie okna dialogowego wyboru pliku.
int result = chooser.showOpenDialog(null);
// Jeli plik zosta wybrany, ustawiamy go jako ikon etykiety.
if (result == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
label.setIcon(new ImageIcon(name));
}
}
});
JMenuItem exitItem = new JMenuItem("Zakocz");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
}
}
Rozdzia 2.
53
Pierwsze polecenie ju znamy suy do uruchamiania kompilatora Javy. W tym przypadku skompilowalimy plik z kodem rdowym o nazwie WelcomeApplet.java na plik
z kodem bajtowym o nazwie WelcomeApplet.class.
Jednak tym razem nie uruchamiamy programu java, tylko program appletviewer. Jest to specjalne narzdzie dostpne w pakiecie JDK, ktre umoliwia szybkie przetestowanie apletu.
Program ten przyjmuje na wejciu pliki HTML, a nie pliki klas Javy. Zawarto pliku WelcomeApplet.html przedstawia listing 2.3.
Listing 2.3. WelcomeApplet.html
<html>
<head>
<title>WelcomeApplet</title>
</head>
<body>
<hr/>
<p>
Ten aplet pochodzi z ksiki
<a href="http://www.horstmann.com/corejava.html">Java. Podstawy</a>,
ktrej autorami s <em>Cay Horstmann</em> i <em>Gary Cornell</em>,
wydanej przez wydawnictwo Helion.
</p>
<applet code="WelcomeApplet.class" width="400" height="200">
<param name="greeting" value ="Witaj, czytelniku!"/>
54
Java. Podstawy
</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.
Oczywicie aplety s przeznaczone do uruchamiania w przegldarkach internetowych, ale niestety w wielu z nich obsuga tych obiektw jest standardowo wyczona. Informacje dotyczce
konfiguracji najpopularniejszych przegldarek do obsugi Javy mona znale pod adresem
http://java.com/en/download/help/enable_browser.xml. Majc odpowiednio skonfigurowan
przegldark, moesz w niej uruchomi nasz aplet.
1.
Uruchom przegldark.
WelcomeApplet.html.
4. Przegldarka wywietli aplet wraz z dodatkowym tekstem. Rezultat bdzie podobny
Jak wida, aplikacja ta jest zdolna do interakcji z internetem. Kliknicie przycisku Cay Horstmann 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 moliwoci wysyania poczty e-mail ani wywietlania stron internetowych, wic ignoruje nasze
Rozdzia 2.
55
Kod apletu przedstawia listing 2.4. Na razie wystarczy rzuci tylko na niego okiem. Do pisania
apletw wrcimy w rozdziale 10.
Listing 2.4. WelcomeApplet.java
import
import
import
import
java.awt.*;
java.awt.event.*;
java.net.*;
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());
JLabel label = new JLabel(getParameter("greeting"), SwingConstants.
CENTER);
label.setFont(new Font("Serif", Font.BOLD, 18));
add(label, BorderLayout.CENTER);
JPanel panel = new JPanel();
JButton cayButton = new JButton("Cay Horstmann");
cayButton.addActionListener(makeAction("http://www.horstmann.com"));
panel.add(cayButton);
JButton garyButton = new JButton("Gary Cornell");
garyButton.addActionListener(makeAction("mailto:gary_cornell@
apress.com"));
panel.add(garyButton);
add(panel, BorderLayout.SOUTH);
}
});
}
56
Java. Podstawy
private ActionListener makeAction(final String urlString)
{
return new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
try
{
getAppletContext().showDocument(new URL(urlString));
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
}
};
}
}
Podstawowe elementy
jzyka Java
W tym rozdziale:
Komentarze
Typy danych
Zmienne
Operatory
acuchy
Wejcie i wyjcie
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 programowanie, 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 porednictwem 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 modyfikatora dostpu (ang. access modifier). Okrela ono, jaki rodzaj dostpu do tego kodu maj
inne czci programu. Wicej informacji na temat modyfikatorw dostpu zawarlimy w rozdziale 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.
Zgodnie ze standardow konwencj nazewnicz (ktrej przykadem jest nazwa klasy
FirstSample) nazwy klas powinny si skada z rzeczownikw pisanych wielk liter. Jeli
nazwa klasy skada si z kilku sw, kade z nich powinno by napisane wielk liter; notacja
polegajca na stosowaniu wielkich liter wewntrz nazw jest czasami nazywana notacj wielbdzi (ang. camel case lub CamelCase).
Plik zawierajcy kod rdowy musi mie tak sam nazw jak klasa publiczna oraz rozszerzenie .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.
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 polecenia (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 wiedz 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 gosowa na swj ulubiony bd. Bdy o najwikszej liczbie gosw maj najwiksz szans
poprawienia w kolejnej wersji pakietu JDK.
Zauwa, e w kodzie rdowym uyto nawiasw klamrowych. W Javie, podobnie jak w C
i C++, klamry oddzielaj poszczeglne czci (zazwyczaj nazywane blokami) kodu programu. Kod kadej metody w Javie musi si zaczyna od otwierajcej klamry {, a koczy
zamykajc klamr }.
60
Java. Podstawy
Styl stosowania nawiasw klamrowych wywoa niepotrzebn dyskusj. My stosujemy styl
polegajcy na umieszczaniu dopeniajcych si klamer w tej samej kolumnie. Jako e kompilator ignoruje biae znaki, mona stosowa dowolny styl nawiasw klamrowych. Wicej do
powiedzenia na temat stosowania klamer bdziemy mieli przy okazji omawiania ptli.
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 tajemnic. 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. Programici 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.
61
W tym przypadku wywoalimy metod println i przekazalimy jej argument w postaci acucha. Metoda ta wywietla zawarto parametru w konsoli. Nastpnie koczy wiersz wyjciowy, dziki czemu kade wywoanie metody println wywietla dane w oddzielnym wierszu.
Zwr uwag, e w Javie, podobnie jak w C i C++, acuchy naley ujmowa w cudzysowy wicej informacji na temat acuchw znajduje si w dalszej czci tego rozdziau.
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 uwzgldniane 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!");
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.
Wreszcie, trzeci rodzaj komentarza suy do automatycznego generowania dokumentacji. Ten
rodzaj komentarza zaczyna si znakami /** i koczy */. Jego zastosowanie przedstawia
listing 3.1. Wicej informacji na temat tego rodzaju komentarzy i automatycznego generowania dokumentacji znajduje si w rozdziale 4.
Listing 3.1. FirstSample.java
/**
* Jest to pierwszy przykadowy program w rozdziale 3.
* @version 1.01 1997-03-22
* @author Gary Cornell
*/
public class FirstSample
{
public static void main(String[] args)
{
62
Java. Podstawy
System.out.println("Nie powiemy Witaj, wiecie!");
}
}
Liczba bajtw
int
short
od 32 768 do 32 767
long
byte
od 128 do 127
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.
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 procesorze 32-bitowym moe powodowa bd przekroczenia zakresu liczby cakowitej na procesorze 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 Java 7 wprowadzono dodatkowo moliwo zapisu liczb w formacie binarnym, do
czego suy przedrostek 0b. Przykadowo 0b1001 to inaczej 9. Ponadto rwnie od tej wersji
jzyka mona w literaach liczbowych stosowa znaki podkrelenia, np. 1_000_000 (albo
0b1111_0100_0010_0100_0000) milion. Znaki te maj za zadanie uatwi czytanie kodu
ludziom. Kompilator Javy je usuwa.
W jzykach C i C++ typ int to liczba cakowita, ktrej rozmiar zaley od urzdzenia 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 Windows 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.
Liczba bajtw
Zakres
float
double
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 przypadkw 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 groszach, 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 zastosowanie 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).
Liczby zmiennoprzecinkowe mona podawa w zapisie szesnastkowym. Na przykad 0,125, czyli 23, mona zapisa jako 0x1.0p-3. Wykadnik potgi w zapisie
szesnastkowym to p, a nie e (e jest cyfr szesnastkow). Zauwa, e mantysa jest
w notacji szesnastkowej, a wykadnik w dziesitnej. Podstaw wykadnika jest 2, nie 10.
dodatnia nieskoczono,
ujemna nieskoczono,
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.
Stae Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY i Double.NaN (oraz
ich odpowiedniki typu float) reprezentuj wymienione specjalne wartoci, ale s
rzadko uywane. Nie mona na przykad wykona takiego sprawdzenia:
if (x == Double.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))
Liczby zmiennoprzecinkowe nie nadaj si do oblicze finansowych, w ktrych niedopuszczalny jest bd zaokrglania (ang. roundoff error). Na przykad instrukcja
System.out.println(2.0 - 1.1) da wynik 0.8999999999999999 zamiast spodziewanego 0.9. Tego typu bdy spowodowane s tym, e liczby zmiennoprzecinkowe s
reprezentowane w systemie binarnym. W systemie tym nie ma dokadnej reprezentacji
uamka 1/10, podobnie jak w systemie dziesitnym nie istnieje dokadna reprezentacja
uamka 1/3. Aby wykonywa precyzyjne obliczenia numeryczne bez bdu zaokrglania,
naley uy klasy BigDecimal, ktra jest opisana w dalszej czci tego rozdziau.
Rozdzia 3.
65
Nazwa
Warto Unicode
\b
Backspace
\u0008
\t
Tabulacja
\u0009
\n
\u000a
\r
Powrt karetki
\u 000d
\"
Cudzysw
\u 0022
\'
Apostrof
\u0027
\\
Lewy ukonik
\u005c
Aby w peni zrozumie typ char, trzeba pozna system kodowania znakw Unicode. Unicode 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 wschodnioeuropejskich (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 rnych 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 potrzebuj dwch bajtw.
Unicode ma za zadanie rozwiza te problemy. Kiedy w latach osiemdziesitych XX wieku
podjto prby unifikacji, wydawao si, e dwubajtowy stay kod by wicej ni wystarczajcy do zakodowania znakw uywanych we wszystkich jzykach wiata. W 1991 roku
wiato dzienne ujrza Unicode 1.0. Wykorzystywana w nim bya prawie poowa wszystkich
dostpnych 65 536 kodw. Java od samego pocztku uywaa znakw 16-bitowego systemu
Unicode, co dawao jej du przewag nad innymi jzykami programowania, ktre stosoway znaki omiobitowe.
66
Java. Podstawy
Niestety z czasem nastpio to, co byo nieuchronne. Unicode przekroczy liczb 65 536
znakw, gwnie z powodu dodania bardzo duych zbiorw ideogramw uywanych w jzykach 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).
Kodowanie UTF-16 to sposb reprezentacji wszystkich wsprzdnych kodowych znakw
za pomoc kodw o rnej dugoci. Znaki w podstawowej przestrzeni s 16-bitowymi
wartociami o nazwie jednostek kodowych (ang. code units). Znaki dodatkowe s kodowane jako kolejne pary jednostek kodowych. Kada z wartoci nalecych do takiej pary
naley do zakresu 2048 nieuywanych wartoci BMP, zwanych obszarem surogatw
(ang. surrogates area) zakres pierwszej jednostki kodowej to U+D800 U+DBFF, a drugiej
U+DC00 U+DFFF. Jest to bardzo sprytne rozwizanie, poniewa od razu wiadomo, czy jednostka kodowa reprezentuje jeden znak, czy jest pierwsz lub drug czci znaku dodatkowego. Na przykad matematyczny symbol oznaczajcy zbir liczb cakowitych ma wsprzdn kodow U+1D56B i jest kodowany przez dwie jednostki kodowe U+D835 oraz U+DD6B (opis
algorytmu kodowania UTF-16 mona znale na stronie http://en.wikipedia.org/wiki/UTF-16).
W Javie typ char opisuje jednostk kodow UTF-16.
Zdecydowanie odradzamy posugiwania si w programach typem char, jeli nie ma koniecznoci wykonywania dziaa na jednostkach kodowych UTF-16. Prawie zawsze lepszym rozwizaniem jest traktowanie acuchw (ktre opisujemy w podrozdziale 3.6, acuchy)
jako abstrakcyjnych typw danych.
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.
67
W jzyku C++ zamiast wartoci logicznych mona stosowa liczby, a nawet wskaniki. 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)
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 wyraenia cakowitoliczbowego x = 0 nie mona przekonwertowa na warto logiczn.
double salary;
int vacationDays;
long earthPopulation;
boolean done;
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).
Kilka deklaracji mona umieci w jednym wierszu:
int i, j;
Nie polecamy jednak takiego stylu pisania kodu. Dziki deklarowaniu kadej zmiennej oddzielnie programy s atwiejsze do czytania.
68
Java. Podstawy
Wreszcie, deklaracje w Javie mona umieszcza w dowolnym miejscu w kodzie. Na przykad poniszy kod jest poprawny:
double salary = 65000.0;
System.out.println(salary);
int vacationDays = 12;
3.4.2. Stae
Stae oznaczamy sowem kluczowym final. Na przykad:
Rozdzia 3.
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;
3.5. Operatory
Znane wszystkim operatory arytmetyczne +, , * i / su w Javie odpowiednio do wykonywania operacji dodawania, odejmowania, mnoenia i dzielenia. Operator / oznacza dzielenie
cakowitoliczbowe, jeli obie liczby s typu cakowitoliczbowego, oraz dzielenie zmiennoprzecinkowe 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 wynikiem dzielenia zmiennoprzecinkowego przez zero jest nieskoczono lub warto NaN.
Binarne operatory arytmetyczne w przypisaniach mona wygodnie skraca. Na przykad zapis:
x+= 4;
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 wirtualnej 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;
Wiele procesorw Intel warto wyraenia x * y zapisuje w 80-bitowym rejestrze. Nastpnie wykonywane jest dzielenie przez z, a wynik z powrotem obcinany do 64 bitw. Tym
sposobem otrzymujemy dokadniejsze wyniki i unikamy przekroczenia zakresu wykadnika.
Ale wynik moe by inny, ni gdyby obliczenia byy cay czas wykonywane w 64 bitach.
Z tego powodu w pierwszych specyfikacjach wirtualnej maszyny Javy by zapisany wymg,
aby wszystkie obliczenia porednie uyway zmniejszonej precyzji. Nie przepadaa za tym
caa spoeczno programistyczna. Obliczenia o zmniejszonej precyzji mog nie tylko
powodowa przekroczenie zakresu, ale s te wolniejsze ni obliczenia o zwikszonej
precyzji, poniewa obcinanie bitw zajmowao czas. W zwizku z tym opracowano aktualizacj jzyka Java majc na celu rozwiza problem sprzecznych wymaga dotyczcych optymalizacji wydajnoci i powtarzalnoci wynikw. Projektanci maszyny wirtualnej
mog obecnie stosowa zwikszon precyzj w obliczeniach porednich. Jednak metody
oznaczone sowem kluczowym strictfp musz korzysta ze cisych dziaa zmiennoprzecinkowych, ktre daj powtarzalne wyniki.
Na przykad metod main mona oznaczy nastpujco:
public static strictfp void main(String[] args)
W takim przypadku wszystkie instrukcje znajdujce si w metodzie main uywaj ograniczonych oblicze zmiennoprzecinkowych. Jeli oznaczymy w ten sposb klas, wszystkie
jej metody bd stosowa obliczenia zmiennoprzecinkowe o zmniejszonej precyzji.
Sedno problemu ley w dziaaniu procesorw Intel. W trybie domylnym obliczenia porednie mog uywa rozszerzonego wykadnika, ale nie rozszerzonej mantysy (chipy
Intela umoliwiaj obcinanie mantysy niepowodujce strat wydajnoci). W zwizku z tym
gwna rnica pomidzy trybem domylnym a cisym jest taka, e obliczenia cise
mog przekroczy zakres, a domylne nie.
Musz jednak uspokoi tych, u ktrych na ciele wystpia gsia skrka w trakcie lektury tej
uwagi. Przekroczenie zakresu liczby zmiennoprzecinkowej nie zdarza si na co dzie
w zwykych programach. W tej ksice nie uywamy sowa kluczowego strictfp.
Rozdzia 3.
71
m
n
a
b
=
=
=
=
7;
7;
2 * ++m;
2 * n++;
// a ma warto 16, a m 8
// b ma warto 14, a n 8
72
Java. Podstawy
Operatorem koniunkcji logicznej w Javie, podobnie jak w C++, jest &&, a alternatywy logicznej ||. 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 pierwszego. 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
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.
Podobnie warto wyraenia wyraenie1 || wyraenie2 ma automatycznie warto true, jeli
pierwsze wyraenie ma warto true. Warto drugiego nie jest obliczana.
W Javie dostpny jest te czasami przydatny operator trjargumentowy w postaci ?:. Wartoci 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.
Operatory & i | zastosowane do wartoci logicznych zwracaj wartoci logiczne.
S one podobne do operatorw && i ||, tyle e do obliczania wartoci wyrae z ich
uyciem nie jest stosowana metoda na skrty. A zatem wartoci obu argumentw s
zawsze obliczane przed zwrceniem wyniku.
Rozdzia 3.
73
Mona te uywa tak zwanych operatorw przesunicia, w postaci >> i <<, ktre przesuwaj 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;
Ostatni z operatorw bitowych >>> odpowiada za przesunicie bitowe w prawo z wypenieniem zerami, podczas gdy operator >> przesuwa bity w prawo i do ich wypenienia uywa
znaku liczby. Nie ma operatora <<<.
Argument znajdujcy si po prawej stronie operatorw przesunicia jest redukowany modulo do 32 bitw (chyba e argument po lewej stronie jest typu long;
w takim przypadku argument z prawej strony jest redukowany modulo do 64 bitw).
Na przykad warto wyraenia 1 << 35 jest taka sama jak 1 << 3, czyli 8.
W jzykach C i C++ nie ma gwarancji, e operator >> wykonuje przesunicie arytmetyczne (wypenienie bitem znaku), a nie przesunicie logiczne (wypenienie
zerami). Implementatorzy mog na wasn rk wybra takie dziaanie, ktre jest bardziej efektywne. Oznacza to, e operator >> w C++ jest zdefiniowany tylko dla liczb nieujemnych. Java jest wolna od tej wieloznacznoci.
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.
Klasa Math udostpnia take metody obliczajce funkcje trygonometryczne:
Math.sin
Math.cos
74
Java. Podstawy
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
Mona unikn stosowania przedrostka Math przed metodami i staymi matematycznymi, umieszczajc poniszy wiersz kodu na pocztku pliku rdowego:
import static java.lang.Math.*;
Na przykad:
System.out.println("Pierwiastek kwadratowy z \u03C0 wynosi " + sqrt(PI));
Rozdzia 3.
75
Rysunek 3.1.
Dozwolone
konwersje
pomidzy typami
liczbowymi
W przeciwnym razie, jeli ktry z operandw jest typu float, drugi zostanie
przekonwertowany na typ float.
W przeciwnym razie, jeli ktry z operandw jest typu long, drugi zostanie
przekonwertowany na typ long.
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 przekonwertowa 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.
Aby zaokrgli liczb zmiennoprzecinkow do najbliszej liczby cakowitej (co w wikszoci przypadkw bardziej si przydaje), naley uy metody Math.round:
double x = 9.997;
int nx = (int) Math.round(x);
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.
Wizanie
[] . () (wywoanie metody)
lewe
prawe
* / %
lewe
+ -
lewe
lewe
lewe
== !=
lewe
&
lewe
lewe
lewe
&&
lewe
||
lewe
?:
prawe
prawe
Rozdzia 3.
77
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.
Bardziej szczegowy opis typw wyliczeniowych znajduje si w rozdziale 5.
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 wbudowanego 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);
3.6.2. Konkatenacja
W Javie, podobnie jak w wikszoci innych jzykw programowania, mona czy (konkatenowa) acuchy za pomoc znaku +.
String expletive = "brzydkie sowo";
String PG13 = "usunito";
String message = expletive + PG13;
jest w peni poprawny i wydrukowaby to, co potrzeba (przy zachowaniu odpowiednich odstpw, gdy po sowie brzmi znajduje si spacja).
Rozdzia 3.
79
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
Kiedy zastpimy komunikat greeting jakim innym acuchem, Java wykona z grubsza
takie dziaania:
char* temp = malloc(6);
strncpy(temp, greeting, 3);
strncpy(temp + 3, "kaj", 3);
greeting = temp;
Czy to nie spowoduje wycieku pamici? Przecie oryginalny acuch zosta umieszczony
na stercie. Na szczcie Java automatycznie usuwa nieuywane obiekty. Jeli dany blok
pamici nie jest ju potrzebny, zostanie wyczyszczony.
Typ String Javy duo atwiej opanowa programistom jzyka C++, ktrzy uywaj klasy
string zdefiniowanej w standardzie ISO/ANSI tego jzyka. Obiekty klasy string w C++
take automatycznie przydzielaj i czyszcz pami. Zarzdzanie pamici odbywa si
w sposb jawny za porednictwem konstruktorw, operatorw przypisania i destruktorw. Poniewa w C++ acuchy s zmienialne (ang. mutable), mona zmienia w nich
poszczeglne znaki.
"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 acuchowe. acuchy bdce na przykad wynikiem operacji wykonywanych za pomoc operatora + albo metody substring nie s wspdzielone. W zwizku z tym nigdy nie uywaj
Rozdzia 3.
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.
82
Java. Podstawy
skadajce si z jednej jednostki kodowej. Reprezentacje znakw dodatkowych skadaj si
z par jednostek kodowych.
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
Znak
char ch = sentence.charAt(1)
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++;
Rozdzia 3.
83
Zwraca warto ujemn, jeli acuch znajduje si przed innym (other) acuchem
w kolejnoci sownikowej, warto dodatni, jeli znajduje si za nim, lub 0,
jeli acuchy s identyczne.
84
Java. Podstawy
int length()
String toLowerCase()
String toUpperCase()
Rozdzia 3.
85
String trim()
Usuwa wszystkie biae znaki z pocztku i koca acucha. Zwraca wynik jako
nowy acuch.
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 wszystkich 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
Rozdzia 3.
Rysunek 3.4.
Zestawienie
metod klasy
String
Rysunek 3.5.
Szczegowy opis
metody klasy
String
87
88
Java. Podstawy
Po zoeniu acucha wywoujemy metod toString. Zwrci ona obiekt klasy String zawierajcy sekwencj znakw znajdujc si w obiekcie builder.
String completedString = builder.toString();
StringBuilder()
int length()
Dodaje acuch c.
StringBuilder append(char c)
String toString()
Rozdzia 3.
89
W tym przypadku zastosowanie metody nextLine zostao podyktowane tym, e dane na wejciu mog zawiera spacje. Aby odczyta jedno sowo (ograniczone spacjami), naley wywoa ponisz metod:
String firstName = in.next();
90
Java. Podstawy
* Ten program demonstruje pobieranie danych z konsoli.
* @version 1.10 2004-02-10
* @author Cay Horstmann
*/
public class InputTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
// Pobranie pierwszej porcji danych.
System.out.print("Jak si nazywasz? ");
String name = in.nextLine();
// Pobranie drugiej porcji danych.
System.out.print("Ile masz lat? ");
int age = in.nextInt();
// Wydruk danych w konsoli.
System.out.println("Witaj uytkowniku" + name + ". W przyszym roku bdziesz
mie " + (age + 1) + "lat.");
}
}
Scanner(InputStream in)
Rozdzia 3.
91
String nextLine()
String text()
int nextInt()
double nextDouble()
boolean hasNext()
boolean hasNextInt()
boolean hasNextDouble()
Sprawdza, czy dana sekwencja znakw jest liczb cakowit, czy liczb
zmiennoprzecinkow.
java.lang.System 1.0
wydrukuje:
3333.3333333333335
Problemy zaczynaj si wtedy, gdy chcemy na przykad wywietli liczb dolarw i centw.
92
Java. Podstawy
W pierwotnych wersjach Javy formatowanie liczb sprawiao sporo problemw. Na szczcie
w wersji Java SE 5 wprowadzono zasuon ju metod printf z biblioteki C. Na przykad
wywoanie:
System.out.printf("%8.2f", x);
Kady specyfikator formatu, ktry zaczyna si od znaku %, jest zastpowany odpowiadajcym mu argumentem. Znak konwersji znajdujcy si na kocu specyfikatora formatu okrela
typ wartoci do sformatowania: f oznacza liczb zmiennoprzecinkow, s acuch, a d liczb
cakowit dziesitn. Tabela 3.5 zawiera wszystkie znaki konwersji.
Dodatkowo mona kontrolowa wygld sformatowanych danych wyjciowych za pomoc
kilku znacznikw. Tabela 3.6 przedstawia wszystkie znaczniki. Na przykad przecinek dodaje
separator grup. To znaczy:
System.out.printf("%, .2f", 10000.0 / 3.0);
wydrukuje:
3 333,33
Mona stosowa po kilka znacznikw naraz, na przykad zapis "%,(.2f" oznacza uycie
separatorw grup i ujcie liczb ujemnych w nawiasy.
Za pomoc znaku konwersji s mona formatowa dowolne obiekty. Jeli obiekt
taki implementuje interfejs Formattable, wywoywana jest jego metoda formatTo.
W przeciwnym razie wywoywana jest metoda toString w celu przekonwertowania obiektu
na acuch. Metoda toString opisana jest w rozdziale 5., a interfejsy w rozdziale 6.
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());
Aby program zadziaa, na pocztku kodu rdowego naley wstawi wiersz import java.util.Date;
przyp. tum.
Rozdzia 3.
93
Typ
Przykad
159
9f
237
Liczba zmiennoprzecinkowa
15.9
1.59e+01
0x1.fccdp3
acuch
Witaj
Znak
Warto logiczna
true
Kod mieszajcy
42628b2
tx
Data i godzina
Symbol procenta
Przeznaczenie
Przykad
+3333,33
spacja
| 3333,33|
003333,33
|3333,33 |
(3333,33)
3 333,33
# (dla formatu f)
3 333,
0xcafe
159 9F
<
94
Java. Podstawy
Typ
Przykad
2007-11-26
11/26/07
15:25:10
03:52:55 PM
15:25
2007
07
20
listopad
b lub h
lis
02
09
poniedziaek
Pn
069
18
18
06
05
19
046
047000000
Rozdzia 3.
95
Typ
Przykad
PM
pm
+0100
Strefa czasowa
CET
1196089646
1196089667265
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 nierozsdnym rozwizaniem. Dlatego w acuchu formatujcym mona poda indeks argumentu,
ktry ma by sformatowany. Indeks musi si znajdowa bezporednio po symbolu % i koczy 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 specyfikacji 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 schemat opisujcy skadni specyfikatorw formatu.
Niektre z zasad formatowania s zwizane z okrelon lokalizacj. Na przykad
w Niemczech separatorem dziesitnym jest przecinek, a zamiast Poniedziaek
wywietla si Montag. Kontrola funkcji midzynarodowych programu zostaa opisana
w drugim tomie, w rozdziale 5.
96
Java. Podstawy
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 konstruktorowi nazw pliku:
PrintWriter out = new PrintWriter("mojplik.txt");
Jeli plik nie istnieje, mona uy metod print, println lub printf, podobnie jak w przypadku 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.
97
katalogiem pocztkowym bdzie aktualny katalog okna konsoli. W zintegrowanym rodowisku programistycznym katalog pocztkowy jest okrelany przez IDE. Lokalizacj tego
katalogu mona sprawdzi za pomoc poniszego wywoania:
String dir = System.getProperty("user.dir");
Jeli nie moesz si poapa w lokalizacji plikw, moesz zastosowa cieki bezwzgldne,
takie jak "c:\\mojkatalog\\mojplik.txt" lub "/home/ja/mojkatalog/mojplik.txt".
public static void main(String[] args) throws FileNotFoundException
{
Scanner in = new Scanner(Paths.get("mojplik.txt"));
. . .
}
Wiemy ju, jak odczytywa i zapisywa pliki zawierajce dane tekstowe. Bardziej zaawansowane zagadnienia, jak obsuga rnych standardw kodowania znakw, przetwarzanie
danych binarnych, odczyt katalogw i zapis plikw archiwum zip, zostay opisane w rozdziale 1. drugiego tomu.
Przy uruchamianiu programu w wierszu polece mona uy waciwej danemu
systemowi skadni przekierowywania w celu dodania dowolnego pliku do wejcia
System.in i wyjcia System.out:
java MyProg < mojplik.txt > output.txt
Scanner(Path p)
Scanner(String data)
PrintWriter(String fileName)
98
Java. Podstawy
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.
99
W C++ mona wewntrz bloku ponownie zdefiniowa zmienn wczeniej zdefiniowan na zewntrz tego bloku. Ta definicja wewntrzna przesania wtedy definicj zewntrzn. Moe to by jednak rdem bdw i z tego powodu operacja taka nie
jest dozwolona w Javie.
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 >=
{
performance =
bonus = 100 +
}
else
{
performance =
bonus = 0;
}
target)
"rednio";
0.01 * (yourSales - target);
"Sabo";
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 poprzedzajcemu je if. W zwizku z tym w instrukcji:
if (x <= 0) if (x == 0) sign = 0; else sign = -1;
Rozdzia 3.
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 okrelonej stopie oprocentowania wpaconych pienidzy.
W ciele ptli zwikszamy licznik i aktualizujemy biec kwot uzbieranych pienidzy, a
ich suma przekroczy wyznaczon kwot.
while (balance < goal)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
years++;
}
System.out.println(years + "lat.");
(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 jestemy gotowi przej na emerytur:
do
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
year++;
// Drukowanie aktualnego stanu konta.
. . .
Rozdzia 3.
103
Rysunek 3.10.
Diagram
przepywu
sterowania
instrukcji while
Ptla jest powtarzana, dopki uytkownik podaje odpowied N (zobacz rysunek 3.11). Ten program 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.
Listing 3.3. Retirement.java
import java.util.*;
/**
* 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
Rozdzia 3.
balance += interest;
years++;
}
System.out.println("Moesz przej na emerytur za " + years + " lat.");
}
}
105
106
Java. Podstawy
Rysunek 3.12.
Diagram
przepywu
sterowania
ptli for
Na pierwszym miejscu z reguy znajduje si inicjacja licznika. Drugie miejsce zajmuje warunek, 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.
107
Naley zachowa szczegln ostrono przy porwnywaniu w ptli liczb zmiennoprzecinkowych. Ptla for w takiej postaci:
for (double x = 0; x != 10; x += 0.1) . . .
moe si nigdy nie skoczy. Warto kocowa nie zostanie osignita ze wzgldu na
bd zwizany z zaokrglaniem. Na przykad w powyszej ptli warto x przeskoczy
z wartoci 9.99999999999998 na 10.09999999999998, poniewa liczba 0,1 nie ma dokadnej reprezentacji binarnej.
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++)
i.
{
. . .
}
108
Java. Podstawy
Listing 3.5 przedstawia typowy przykad zastosowania ptli for.
Ten program oblicza szanse wygrania na loterii. Jeli na przykad loteria polega na wybraniu szeciu liczb z przedziau 1 50, to istnieje (50*49*48*47*46*45)/(1*2*3*4*5*6) moliwych kombinacji, co oznacza, e nasze szanse s jak 1 do 15 890 700. Powodzenia!
W oglnym przypadku losowania k liczb ze zbioru n istnieje:
n (n 1) (n 2) ... (n k 1)
1 2 3 ... k
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.
Listing 3.5. LotteryOdds.java
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);
System.out.print("Ile liczb ma by wylosowanych? ");
int k = in.nextInt();
System.out.print("Jaka jest grna granica przedziau losowanych liczb? ");
int n = in.nextInt();
/*
* 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;
System.out.println("Twoje szanse to 1 do " + lotteryOdds + ". Powodzenia!");
}
}
Rozdzia 3.
109
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 etykiet nie zostanie dopasowana, nastpi wykonanie czci oznaczonej przez etykiet default
jeli taka istnieje.
Etykiety case mog by:
wyraeniami staymi typu char, byte, short lub int (oraz odpowiednich klas
opakowujcych: Character, Byte, Short i Integer ich opis znajduje si
w rozdziale 4.);
staymi wyliczeniowymi;
acuchami od Java SE 7.
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;
. . .
Rozdzia 3.
111
Dziki temu ustawieniu kompilator bdzie zgasza wszystkie przypadki alternatyw niezawierajcych na kocu instrukcji break.
Gdy bdziesz chcia wykona bloki case po kolei, oznacz otaczajc je metod adnotacj @SuppressWarnings("fallthrough"). Dziki temu dla tej metody nie bd zgaszane ostrzeenia. (Adnotacje to technika przekazywania informacji do kompilatora lub
innego narzdzia przetwarzajcego kod rdowy Java lub pliki klas. Ich szczegowy
opis znajduje si w rozdziale 13. drugiego tomu).
Size sz = . . .;
switch (sz)
{
case SMALL:
// Nie trzeba byo pisa Size.SMALL.
. . .
break;
. . .
}
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 wiksz 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 zauway, 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.
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.
Co ciekawe, etykiet mona doda do kadej instrukcji, nawet instrukcji warunkowej if i instrukcji blokowej:
etykieta:
{
. . .
if (warunek) break etykieta;
// Wychodzi z bloku.
. . .
}
// Przechodzi do tego miejsca, jeli zostanie wykonana instrukcja break.
W zwizku z tym, jeli tsknisz za instrukcj goto i moesz umieci blok bezporednio 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.
}
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 operatorw arytmetycznych, jak + czy *. Zamiast nich trzeba uywa odpowiednich metod, jak add
i multiply, dostpnych w klasach wielkich liczb:
BigInteger c = a.add(b);
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2)));
// c = a + b
// 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!
Program z listingu 3.5 oblicza warto nastpujcego wyraenia:
lotteryOdds = lotteryOdds * (n - i + 1) / i;
Przy uyciu wielkich liczb odpowiednikiem tej instrukcji jest ponisza instrukcja:
lotteryOdds = lotteryOdds.multiply(BigInteger.valueOf(n - i + 1)).divide(BigInteger.
valueOf(i));
Rozdzia 3.
java.math.BigInteger 1.1
Zwraca warto 0, jeli liczba BigInteger jest rwna liczbie other, warto
ujemn, jeli liczba BigInteger jest mniejsza od liczby other, lub liczb dodatni
w przeciwnym przypadku.
115
116
Java. Podstawy
java.math.BigDecimal 1.1
Zwraca sum, rnic, iloczyn, iloraz i reszt liczb BigDecimal i other. Obliczenie
ilorazu jest moliwe tylko po podaniu sposobu zaokrglania. Na przykad tryb
RoundingMode.HALF_UP jest znany nam wszystkim ze szkoy (cyfry od 0 do 4
zaokrglamy w d, a od 5 do 9 w gr). Ten sposb zaokrglania jest odpowiedni
do typowych oblicze. Opis pozostaych trybw zaokrglania znajduje si
w dokumentacji API.
Zwraca warto 0, jeli liczba BigDecimal jest rwna liczbie other, warto
ujemn, jeli liczba BigDecimal jest mniejsza od liczby other, lub liczb dodatni
w przeciwnym przypadku.
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 elementw i nawiasw kwadratowych []) i nazwy zmiennej. Poniej znajduje si przykadowa deklaracja tablicy zdolnej do przechowywania liczb cakowitych:
int[] a;
Powysza instrukcja tylko deklaruje zmienn a. Nie inicjuje jej jednak tablic. Do utworzenia 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.
Elementy tablicy s numerowane od 0 (tu od 0 do 99). Tablic mona zapeni wartociami na przykad za pomoc ptli:
Rozdzia 3.
117
int[] a;
int a[];
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 elementy). 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
Na przykad ponisza ptla:
for (int element : a)
System.out.println(element);
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 znajduje wiele zastosowa, na przykad kiedy nie chcemy przemierza caej kolekcji danych lub
musimy uy indeksu w ptli.
Istnieje jeszcze prostsza metoda na wydrukowanie wszystkich elementw tablicy.
Polega na uyciu metody toString klasy Arrays. Odwoanie Arrays.toString(a)
zwrci acuch skadajcy si ze wszystkich elementw tablicy ujtych w nawiasy kwadratowe i rozdzielonych przecinkami, np. [2, 3, 5, 7, 11, 13]. Ponisze wywoanie
drukuje zawarto tej tablicy:
System.out.println(Arrays.toString(a));
Naley zauway, e w przypadku zastosowania tej skadni nie uywa si operatora new.
Mona nawet zainicjowa tablic anonimow:
new int[] { 17, 19, 23, 29, 31, 37 }
Rozdzia 3.
119
Powysze wyraenie przydziela pami dla nowej tablicy i zapenia j wartociami podanymi 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 zwikszania 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
// C++
// C++
Rozdzia 3.
121
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".
122
Java. Podstawy
System.out.print("Ile liczb musisz wylosowa? ");
int k = in.nextInt();
System.out.print("Jaka jest najwiksza liczba? ");
int n = in.nextInt();
// Zapenienie tablicy liczbami 1 2 3 . . . n.
int[] numbers = new int[n];
for (int i = 0; i < numbers.length; i++)
numbers[i] = i + 1;
// Losowanie k liczb i zapisanie ich w drugiej tablicy.
int[] result = new int[k];
for (int i = 0; i < result.length; i++)
{
// Losowanie indeksu z zakresu od 0 do n1.
int r = (int) (Math.random() * n);
// Pobranie elementu z losowej lokalizacji.
result[i] = numbers[r];
i-ty wynik bdzie liczb przechowywan w indeksie i. Pocztkowo jest to i+1, ale niebawem 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.
123
Naszym celem jest to, aby za kadym razem by losowany indeks, a nie rzeczywiste wartoci. Indeks ten wskazuje na element tablicy zawierajcej wartoci, ktre nie zostay jeszcze
wylosowane.
Po wylosowaniu k liczb sortujemy zawarto tablicy result:
Arrays.sort(result);
for (int r : result)
System.out.println(r);
java.util.Arrays 1.2
Zwraca tablic tego samego typu co tablica a, majc rozmiar length albo end-start
i zapenion wartociami z tablicy a.
Parametry:
start
end
length
124
Java. Podstawy
Parametry:
start
end
Zwraca warto true, jeli tablice maj takie same rozmiary i jeli wartoci
na odpowiadajcych sobie pozycjach pasuj do siebie.
Parametry:
a, b
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 wielowymiarowej, ktra nie wymaga wywoania new. Na przykad:
int[][] magicSquare =
{
{16, 3, 2, 13},
{5, 10, 11, 8},
Rozdzia 3.
125
11%
12%
13%
14%
15%
10 000,00
10 000,00
10 000,00
10 000,00
10 000,00
10 000,00
11 000,00
11 100,00
11 200,00
11 300,00
11 400,00
11 500,00
12 100,00
12 321,00
12 544,00
12 769,00
12 996,00
13 225,00
13 310,00
13 676,31
14 049,28
14 428,97
14 815,44
15 208,75
14 641,00
15 180,70
15 735,19
16 304,74
16 889,60
17 490,06
16 105,10
16 850,58
17 623,42
18 424,35
19 254,15
20 113,57
17 715,61
18 704,15
19 738,23
20 819,52
21 949,73
23 130,61
19 487,17
20 761,60
22 106,81
23 526,05
25 022,69
26 600,20
21 435,89
23 045,38
24 759,63
26 584,44
28 525,86
30 590,23
23 579,48
25 580,37
27 730,79
30 040,42
32 519,49
35 178,76
{9, 6, 7, 12},
{4, 15, 14, 1}
};
Dostp do elementw takiej tablicy uzyskujemy za pomoc dwch indeksw, np. balances
[i][j].
Przykadowy program zapisuje jednowymiarow tablic o nazwie interest zawierajc stopy
oprocentowania i dwuwymiarow tablic o nazwie balances zawierajc stany rodkw
dla kadego roku i kadej stopy procentowej. Pierwszy wiersz tablicy inicjujemy saldem
pocztkowym:
for (int j = 0; j < balance[0].length; j++)
balances[0][j] = 10000;
126
Java. Podstawy
Ptla typu for each nie sprawdza automatycznie wszystkich elementw tablicy
dwuwymiarowej. Przechodzi tylko przez wiersze, ktre s tablicami jednowymiarowymi. Aby dotrze do wszystkich elementw tablicy dwuwymiarowej a, naley zagniedzi jedn ptl w drugiej:
for (double[] row : a)
for (double value : row)
Dziaania na wartociach
Rozdzia 3.
127
System.out.println();
}
}
128
Java. Podstawy
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.
Jako e do poszczeglnych wierszy tablic mona uzyska dostp, mona zamienia je
miejscami!
double[] temp = balances[i];
balances[i] = balances[i + 1];
balances[i + 1] = temp;
Rwnie atwe jest tworzenie tablic postrzpionych (ang. ragged arrays), czyli takich, w ktrych wiersze maj rne dugoci. Oto typowy przykad. Utworzymy tablic, w ktrej element w i-tym wierszu i j-tej kolumnie jest rwny liczbie moliwych wynikw loterii polegajcej na losowaniu j liczb spord i liczb.
1
1
1
1
1
1
1
1
2
3
4
5
6
1
3 1
6 4 1
10 10 5 1
15 20 15 6 1
Jako e j nie moe by wiksze od i, powstaje macierz trjktna. Wiersz i-ty ma i+1 elementw (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;
}
Rozdzia 3.
129
// Java
// C++
ani nawet z:
double (*balances)[6] = new double[10][6];
// C++
// C++
130
Java. Podstawy
Obiekty i klasy
W tym rozdziale:
Parametry metod
Konstrukcja obiektw
Pakiety
cieka klas
Komentarze dokumentacyjne
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 kontynuacja nauki Javy bez znajomoci technik obiektowych byaby niemoliwa.
Programici jzyka C++ odkryj w tym rozdziale (podobnie jak w poprzednim) duo podobiestw 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
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.
Wiemy ju, e wszystko, co piszemy w Javie, znajduje si w jakiej klasie. W standardowej
bibliotece znajduje si kilka tysicy klas o tak rnym przeznaczeniu jak wspomaganie projektowania interfejsu, obsuga dat i kalendarzy czy programowanie sieciowe. Niemniej
konieczne jest tworzenie wasnych klas do opisu obiektw rozwizujcych problemy zwizane z konkretnym programem.
Rozdzia 4.
Obiekty i klasy
133
Rysunek 4.1.
Programowanie
proceduralne
a programowanie
obiektowe
4.1.2. Obiekty
Aby sprawnie porusza si w wiecie programowania obiektowego, naley zna trzy podstawowe cechy obiektu:
134
Java. Podstawy
Tosamo obiektu jak odrni obiekt od innych obiektw, ktre mog mie
te same zachowanie i stan.
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).
Item (produkt),
Order (zamwienie),
Payment (patno),
Account (konto)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.
Nastpnie szukamy czasownikw. Do zamwienia dodajemy (ang. add) produkty. Zamwienie mona wysa (ang. ship) albo anulowa (ang. cancel). Patnoci s dokonywane
(ang. apply) na rzecz zamwie. Dla kadego z tych czasownikw trzeba znale obiekt, ktry
jest odpowiedzialny za wykonywanie tych dziaa. Jeli na przykad do zamwienia dodawany jest nowy produkt, to powinien w t operacj zaangaowa si obiekt klasy Order,
poniewa ma informacje na temat zapisywania i sortowania produktw. To znaczy, e metoda
add powinna by metod klasy Order i przyjmowa jako parametr obiekt klasy Item.
Oczywicie regua rzeczownika i czasownika jest tylko praktyczn zasad. W podjciu
decyzji, ktre rzeczowniki i czasowniki naley wykorzysta w nazwach przy budowie klasy,
moe pomc tylko dowiadczenie.
zaleno (uywa),
agregacja (zawiera),
dziedziczenie (jest).
Zwizek zalenoci (czyli uywa) jest najbardziej oczywisty, a zarazem oglny. Na przykad 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 terminologii inynierii oprogramowania okrela si to mianem skojarzenia, czyli stopniem powizania 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.
Niektrzy badacze metod programowania traktuj pojcie agregacji pogardliwie
i preferuj bardziej oglny zwizek asocjacji. Z punktu widzenia modelowania jest
to zrozumiae, ale dla programistw zwizek zawiera jest bardzo adekwatnym pojciem. Jest jeszcze jeden powd, dla ktrego wolimy agregacj standardowa notacja oznaczania asocjacji jest mniej jasna (zobacz tabela 4.1).
136
Java. Podstawy
Konektor UML
Dziedziczenie
Dziedziczenie interfejsu
Zaleno
Agregacja
Asocjacja
Asocjacja skierowana
Dziedziczenie (czyli zwizek jest) wyraa zwizek pomidzy klas ogln i klas specjaln.
Na przykad klasa RushOrder (szybkie zamwienie) dziedziczy po klasie Order. Wyspecjalizowana klasa RushOrder ma specjalne metody do obsugi priorytetw i inn metod do
obliczania opat za transport, ale pozostae jej metody, jak dodawanie produktw i pobieranie 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 (dziedziczenie 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
To wyraenie tworzy nowy obiekt. Obiekt ten jest inicjowany aktualn dat i godzin.
Niektrzy mog si zastanawia, czemu do reprezentacji dat uywa si klas zamiast
(jak w niektrych jzykach programowania) typu wbudowanego. Na przykad jzyk
Visual Basic ma typ wbudowany, dziki czemu programista moe napisa dat w nastpujcym formacie: #6/1/1995#. Na pierwszy rzut oka wydaje si to cakiem dobrym rozwizaniem zamiast przejmowa si klasami, programista moe uy typu wbudowanego.
Naley jednak zada pytanie, czy rozwizanie zastosowane w jzyku Visual Basic jest
dobre. W niektrych krajach format daty to miesic/dzie/rok, a w innych dzie/miesic/
rok. Czy projektanci jzyka przewidzieli tak ewentualno? Jeli nie wykonaj swojej
pracy dobrze, jzyk bdzie pozostawa w nieadzie, a nieszczliwi programici nie bd
mogli nic z tym zrobi. Przy zastosowaniu klas obowizek projektowania zostaje przerzucony na twrc biblioteki. Jeli klasa ma wady, inni programici mog z atwoci napisa wasn klas rozszerzajc lub zastpujc klas systemow (dowd: biblioteka dat
w Javie ma kilka wad i trwaj prace nad jej popraw zobacz http://jcp.org/en/jsr/
detail?id=310).
138
Java. Podstawy
Obiekt mona przekaza do metody:
System.out.println(new Date());
Metod mona te wywoa na rzecz tworzonego obiektu. Jedn z metod klasy Date jest
toString. Zwraca ona reprezentacj acuchow daty. Poniej przedstawiono sposb wywoania 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 utworzonego 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 adnych metod klasy Date. Ponisza instrukcja:
s = deadline.toString();
// jeszcze nie
spowodowaaby bd kompilacji.
Konieczna jest uprzednia inicjacja zmiennej deadline. S dwie moliwoci. Mona oczywicie 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 zmiennej deadline.
Aby zaznaczy, e zmienna obiektowa nie odwouje si do adnego obiektu, naley jej warto ustawi na null.
deadline = null;
. . .
if (deadline != null)
System.out.println(deadline);
// Bd wykonania programu!
140
Java. Podstawy
// Java
jest rwnoznaczne z:
Date* birthday;
// C++
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++
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 uywa, 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()
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.
142
Java. Podstawy
GregorianCalendar now = new GregorianCalendar();
int month = now.get(Calendar.MONTH);
int weekday = now.get(Calendar.DAY_OF_WEEK);
Dodatkowo do obiektu kalendarza mona doda dowoln liczb dni, tygodni, miesicy itd.:
deadline.add(Calendar.MONTH, 3);
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
Wt
6
13
20
27
r
7
14
21
28
Cz Pt So N
1
2 3
4
8
9 10 11
15 16 17 18
22 23 24 25
29 30*
Biecy dzie jest oznaczony gwiazdk (*). Jak wida, program musi wiedzie, jak obliczy
dugo miesica oraz dzie tygodnia.
Przeanalizujmy najwaniejsze czci tego programu. Najpierw tworzymy obiekt kalendarza,
ktry inicjujemy aktualn dat:
GregorianCalendar d = new GregorianCalendar();
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);
Metoda getFirstDayOfWeek pobiera pierwszy dzie tygodnia w biecej lokalizacji. Odpowiednie wcicie uzyskujemy poprzez odjcie 1 od dnia w obiekcie kalendarza, a dojdziemy
do pierwszego dnia tygodnia.
int firstDayOfWeek = d.getFirstDayOfWeek();
int indent = 0;
while (weekday != firstDayOfWeek)
144
Java. Podstawy
{
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 pierwszego 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);
Rozdzia 4.
Obiekty i klasy
145
146
Java. Podstawy
}
while (d.get(Calendar.MONTH) == month);
// Ptla koczy dziaanie, jeli d jest pierwszym dniem nastpnego miesica.
// Wydruk kocowego znaku nowego wiersza w razie potrzeby.
if (weekday != firstDayOfWeek) System.out.println();
}
}
GregorianCalendar()
GregorianCalendar(int year, int month, int day, int hour, int minutes,
int seconds)
year
rok
month
day
dzie
hour
minutes
seconds
field
Rozdzia 4.
Obiekty i klasy
Calendar.AM_PM, Calendar.HOUR,
Calendar.HOUR_OF_DAY,
Calendar.MINUTE, Calendar.SECOND,
Calendar.MILLISECOND,
Calendar.ZONE_OFFSET, Calendar.DST_OFFSET
field
value
Nowa warto
void set(int year, int month, int day, int hour, int minutes, int seconds)
year
rok
month
day
dzie
hour
minutes
seconds
field
amount
int getFirstDayOfWeek()
time
punkt w czasie
Date getTime()
147
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.
Przyjrzyjmy si poniszej, bardzo uproszczonej wersji klasy Employee, ktr mona wykorzysta 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 programie, ktry przedstawia listing 4.2.
Listing 4.2. EmployeeTest/EmployeeTest.java
import java.util.*;
/**
* 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];
staff[0] = new Employee("Jarosaw Rybiski", 75000, 1987, 12, 15);
staff[1] = new Employee("Katarzyna Remiszewska ", 50000, 1989, 10, 1);
staff[2] = new Employee("Krystyna Kuczyska ", 40000, 1990, 3, 15);
// Zwikszenie pensji wszystkich pracownikw o 5%.
for (Employee e : staff)
e.raiseSalary(5);
// Drukowanie informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()
+ ",hireDay="
+ e.getHireDay());
}
}
class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
150
Java. Podstawy
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// W klasie GregorianCalendar stycze ma numer 0.
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
W programie tym tworzymy tablic o nazwie Employee i wstawiamy do niej trzy obiekty
reprezentujce pracownikw:
Employee[] staff = new Employee[3];
staff[0] = new Employee("Jarosaw Rybiski", . . .);
staff[1] = new Employee("Katarzyna Remiszewska", . . .);
staff[2] = new Employee("Krystyna Kuczyska", . . .);
Rozdzia 4.
Obiekty i klasy
151
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 zawierajcej 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.
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 funkcjonalno tego narzdzia.
152
Java. Podstawy
public double getSalary()
public Date getHireDay()
public void raiseSalary(double byPercent)
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).
Dane, ktrymi bdziemy manipulowa w obiekcie klasy Employee, s przechowywane w trzech
polach:
private String name;
private double salary;
private Date hireDay;
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.
Jeli na przykad utworzymy obiekt klasy Employee w poniszy sposb:
new Employee("James Bond", 100000, 1950, 1, 1);
wartoci pl bd nastpujce:
name = "James Bond";
salary = 100000;
hireDay = January 1, 1950;
Rozdzia 4.
Obiekty i klasy
153
Midzy konstruktorami a pozostaymi metodami jest zasadnicza rnica. Konstruktor mona wywoa tylko przy uyciu operatora new. Nie mona za pomoc konstruktora zmieni
wartoci pl. Na przykad poniszy zapis spowoduje bd kompilacji:
james.Employee("James Bond", 250000, 1950, 1, 1);
// bd
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 wywoywany 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.
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. Niektrzy 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.
154
Java. Podstawy
salary += raise;
}
ustawia now warto zmiennej skadowej salary obiektu, na rzecz ktrego zostanie wywoana. Spjrzmy na ponisze wywoanie:
number007.raiseSalary(5);
Spowoduje ono zwikszenie o 5% wartoci zmiennej skadowej number007.salary. A dokadniej powysze wywoanie wykonuje nastpujce instrukcje:
double raise = number007.salary * 5 / 100;
number007.salary += raise;
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.
Sowo kluczowe this odnosi si we wszystkich metodach do parametru niejawnego. Metod
raiseSalary mona by byo napisa nastpujco:
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent / 100;
this.salary += raise;
}
Niektrzy programici wol ten styl pisania kodu, poniewa wyranie odrnia on zmienne
skadowe od zmiennych lokalnych.
W jzyku C++ metody z reguy deklaruje si poza klas:
void Employee::raiseSalary(double byPercent)
{
. . .
}
// inline w C++
W Javie wszystkie metody s zdefiniowane w klasie. Nie s jednak z tego powodu metodami wstawianymi. Znalezienie okazji do wstawienia treci metody jest zadaniem maszyny
wirtualnej. Kompilator JIT szuka wywoa krtkich metod, ktre s czsto uywane, ale
nie s przesaniane, i optymalizuje je.
Rozdzia 4.
Obiekty i klasy
155
S to oczywicie przykady metod akcesora. Poniewa zwracaj wartoci skadowych obiektw, czasami nazywane s metodami dostpu do pl (ang. field accessor).
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 konstruktora 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 poszuka bdu w tej metodzie. Gdyby pole salary byo publiczne, rdo problemw z nim mogoby si znajdowa wszdzie.
Czasami konieczne jest pobranie i ustawienie wartoci skadowej obiektu. Do tego celu potrzebne
s trzy rzeczy:
prywatne pole,
To oznacza wicej pracy ni w przypadku utworzenia publicznego pola danych, ale metoda
ta ma te zalety.
Po pierwsze, przy wprowadzaniu zmian wewntrz implementacji wystarczy tylko zmiana
w kodzie metod klasy. Jeli na przykad sposb przechowywania nazwisk zmieni si na
nastpujcy:
String firstName;
String lastName;
156
Java. Podstawy
firstName + " " + lastName
W ten sposb amiemy zasad hermetyzacji! Spjrzmy na poniszy niesforny fragment kodu:
Employee harry = . . .;
Date d = harry.getHireDay();
double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
d.setTime(d.getTime() - (long) tenYearsInMilliSeconds);
// Dodajmy Harryemu 10 lat stau pracy.
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 zaskoczeniem. Uzyskuje jednak te dostp do prywatnych skadowych obiektu boss. Jest to dozwolone, poniewa obiekt boss jest typu Employee, a metody klasy Employee maj dostp do prywatnych pl wszystkich obiektw tej klasy.
W C++ obowizuje ta sama zasada. Kada metoda danej klasy ma dostp do
prywatnych skadowych tej samej klasy, nie tylko parametru niejawnego.
158
Java. Podstawy
korzystne moe by rozbicie kodu wykonujcego obliczenia na kilka osobnych metod
pomocniczych. Zazwyczaj nie powinny one wchodzi w skad interfejsu publicznego
mog by zbyt blisko biecej implementacji lub wymaga specjalnego protokou albo specjalnej kolejnoci wywoywania. Takie metody najlepiej implementowa jako prywatne.
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.
Rozdzia 4.
Obiekty i klasy
159
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 konkretnego obiektu.
W niektrych obiektowych jzykach programowania pola statyczne s nazywane
polami klasowymi (ang. class field). Termin statyczny jest niezbyt udan pozostaoci po jzyku C++.
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++;
160
Java. Podstawy
Wielokrotnie ju sygnalizowalimy, e nie naley deklarowa pl jako publicznych, poniewa kady moe je zmodyfikowa. Natomiast nie mamy nic przeciwko staym publicznym
(tzn. polom opatrzonych sowem kluczowym final). Ze wzgldu na to, e out to staa, nie
mona przypisa do niej innego strumienia:
System.out = new PrintStream(. . .);
// Bd out to staa.
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 podrozdzia 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 wywoania potrzebna by bya referencja do obiektu typu Employee.
Do wywoania metody statycznej mona uy obiektu. Jeli na przykad harry jest
obiektem klasy Employee, zamiast wywoania Employee.getNextId() trzeba by byo
uy harry.getNextId(). Taki sposb zapisu wydaje nam si mylcy. Metoda getNextId
w ogle nie bierze pod uwag obiektu harry przy obliczaniu wyniku. Zalecamy wywoywanie metod statycznych przy uyciu nazwy klasy, a nie nazwy obiektu.
kiedy metoda nie wymaga dostpu do stanu obiektu, poniewa wszystkie potrzebne
jej parametry s dostarczane w postaci parametrw jawnych (na przykad Math.pow);
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.
162
Java. Podstawy
Dlaczego klasa NumberFormat nie wykorzystuje konstruktora? S ku temu dwa powody:
Konstruktorom nie mona zmienia nazw. Konstruktor zawsze nazywa si tak samo
jak klasa. W tym przypadku jednak byy potrzebne dwie nazwy dla przypadku
stylu walutowego (currency) i procentowego (percent).
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.
Listing 4.3. StaticTest/StaticTest.java
/**
* Ten program demonstruje uycie metod statycznych.
* @version 1.01 2004-02-19
* @author Cay Horstmann
*/
public class StaticTest
{
public static void main(String[] args)
{
// Wstawienie do tablicy staff trzech obiektw reprezentujcych pracownikw.
Employee[] staff = new Employee[3];
staff[0] = new Employee("Tomasz", 40000);
staff[1] = new Employee("Dariusz", 60000);
staff[2] = new Employee("Grzegorz", 65000);
Rozdzia 4.
Obiekty i klasy
Kada klasa moe zawiera metod main, co jest bardzo przydatne przy przeprowadzaniu 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
163
164
Java. Podstawy
name = n;
salary = s;
id = 0;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public int getId()
{
return id;
}
public void setId()
{
id = nextId;
nextId++;
}
public static int getNextId()
{
return nextId;
}
i
java StaticTest
Rozdzia 4.
Obiekty i klasy
165
lokalizacj zmiennej dostarczonej przez wywoujcego. W zwizku z tym metoda moe zmodyfikowa warto zmiennej przekazanej przez referencj, ale nie moe tego zrobi ze zmienn
przekazan przez warto. Okrelenia wywoanie przez s standardowo uywane w terminologii programistycznej do opisu parametrw metod i dotycz nie tylko Javy; jest jeszcze 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 najstarszych 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 przekazanych do niej zmiennych.
Przeanalizujmy na przykad ponisze wywoanie:
double percent = 10;
harry.raiseSalary(percent);
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)
{
x = 3 * x;
}
// nie dziaa
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.
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.
referencje do obiektw.
Wiemy ju, e metoda nie moe zmieni wartoci parametru typu podstawowego. Z parametrami obiektowymi jest inaczej. Mona z atwoci utworzy metod, ktra potraja pensj
pracownika:
public static void tripleSalary(Employee x)
{
x.raiseSalary(200);
}
// dziaa
166
Java. Podstawy
Rysunek 4.6.
Modyfikacja
parametru
liczbowego
nie ma staego
efektu
Rozdzia 4.
Obiekty i klasy
167
// nie dziaa
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 zakoczeniu 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 przekazywane przez warto.
168
Java. Podstawy
Oto zestawienie zasad dotyczcych tego, co mona, a czego nie mona robi z parametrami
metod w Javie:
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.
Na zakoczenie program prezentuje niepowodzenie metody swap:
Rozdzia 4.
Obiekty i klasy
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.
Listing 4.4. ParamTest/ParamTest.java
/**
* Ten program demonstruje przekazywanie parametrw w Javie.
* @version 1.00 2000-01-27
* @author Cay Horstmann
*/
public class ParamTest
{
public static void main(String[] args)
{
/*
* Test 1. Metody nie mog modyfikowa parametrw liczbowych.
*/
System.out.println("Testowanie tripleValue:");
double percent = 10;
System.out.println("Przed: percent=" + percent);
tripleValue(percent);
System.out.println("Po: percent=" + percent);
/*
* 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());
169
170
Java. Podstawy
}
public static void tripleValue(double x)
// nie dziaa
{
x = 3 * x;
System.out.println("Koniec metody: x=" + x);
}
public static void tripleSalary(Employee x)
// dziaa
{
x.raiseSalary(200);
System.out.println("Koniec metody: salary=" + x.getSalary());
}
public static void swap(Employee x, Employee y)
{
Employee temp = x;
x = y;
y = temp;
System.out.println("Koniec metody: x=" + x.getName());
System.out.println("Koniec metody: y=" + y.getName());
}
}
class Employee
// Uproszczona klasa Employee.
{
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
Rozdzia 4.
Obiekty i klasy
171
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);
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 rnych typach zwrotnych.
172
Java. Podstawy
Wemy jako przykad klas Employee. Wyobramy sobie, e nie okrelilimy w konstruktorze 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();
Rozdzia 4.
Obiekty i klasy
173
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()
To przypisanie nastpuje przed wywoaniem konstruktora. Skadnia ta jest szczeglnie przydatna, jeli wszystkie konstruktory klasy musz ustawi okrelon skadow na t sam warto.
Warto inicjujca nie musi by staa. W poniszym przykadzie pole jest inicjowane wywoaniem metody. Wemy pod uwag klas Employee, w ktrej kady pracownik ma swj identyfikator 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;
}
. . .
}
// C++
W Javie skadnia taka nie jest potrzebna, poniewa obiekty nie maj podobiektw, tylko
wskaniki do innych obiektw.
174
Java. Podstawy
Wad tej metody jest to, e stosowane w niej nazwy nic nie mwi o przeznaczeniu parametrw.
Niektrzy programici przed nazw kadego parametru stawiaj przedrostek a:
public Employee(String aName, double aSalary)
{
name = aName;
salary = aSalary;
}
Jest to cakiem dobre rozwizanie. Ju na pierwszy rzut oka wiadomo, jakie jest przeznaczenie
kadego z parametrw.
Inna czsto stosowana sztuczka wykorzystuje fakt, e zmienne parametryczne przesaniaj
skadowe obiektw o tej samej nazwie. Jeli na przykad zostanie wywoany parametr o nazwie
salary, to salary bdzie si odnosi do parametru, a nie skadowej obiektu. Aby uzyska dostp
do tej skadowej, trzeba wtedy napisa this.salary. Przypomnijmy sobie, e this oznacza
parametr niejawny, to znaczy obiekt, ktry jest konstruowany. Poniej znajduje si przykad:
public Employee(String name, double salary)
{
this.name = name;
this.salary = salary;
}
Rozdzia 4.
Obiekty i klasy
175
176
Java. Podstawy
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 prociej 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 szczegowo 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 uzalenione od kolejnoci deklaracji pl danych, byaby nieco dziwna i podatna na bdy.
Pole statyczne mona zainicjowa, podajc warto pocztkow lub korzystajc ze statycznego 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 wykonywane w takiej kolejnoci, w jakiej znajduj si w deklaracji klasy.
Oto czarodziejska sztuczka w jzyku Java, dziki ktrej zaimponujesz swoim
wsppracownikom. Mona napisa program Witaj, wiecie! bez metody main.
public class Hello
{
static
{
System.out.println("Witaj, wiecie");
}
}
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,
konstruktor domylny,
178
Java. Podstawy
Employee[] staff = new Employee[3];
staff[0] = new Employee("Hubert", 40000);
staff[1] = new Employee(60000);
staff[2] = new Employee();
// Wydruk informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary="
+ e.getSalary());
}
}
class Employee
{
private static int nextId;
private int id;
private String name = "";
private double salary;
Rozdzia 4.
Obiekty i klasy
179
return name;
java.util.Random 1.0
Random()
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 wywoywana automatycznie.
4.7. Pakiety
Wygodnym sposobem na organizacj pracy i oddzielenie wasnych klas od pozostaych jest
umieszczanie klas w tak zwanych pakietach (ang. packages).
Standardowa biblioteka Javy skada si z wielu pakietw, do ktrych nale java.lang,
java.util, java.net itd. Standardowe pakiety Javy maj struktur hierarchiczn. Mona je
umieszcza jedne w drugich, podobnie jak w przypadku katalogw i podkatalogw. Wszystkie
standardowe pakiety Javy znajduj si w pakietach java i javax.
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 nastpnie podzieli na podpakiety o nazwach typu com.horstmann.corejava.
Kompilator nie rozpoznaje adnych powiza pomidzy pakietami i podpakietami. Na przykad pakiety java.util i java.util.jar nie maj ze sob nic wsplnego. Kady z nich jest
niezalenym pakietem klas.
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 przykad 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 automatycznie zastpowane list importw konkretnych klas, np.:
import java.util.ArrayList;
import java.util.Date;
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.
W wikszoci przypadkw programista nie interesuje si zbytnio importowanymi pakietami.
Jedyna sytuacja, w ktrej taka uwaga jest potrzebna, wystpuje wtedy, gdy dochodzi do konfliktu nazw. Na przykad zarwno pakiet java.util, jak i java.sql maj klas Date.
Wyobramy sobie, e napisalimy program importujcy oba te pakiety.
import java.util.*;
import java.sql.*;
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 kadym 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!");
exit(0);
// tj. System.out
// 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))
Rozdzia 4.
Obiekty i klasy
183
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.
Pliki rdowe naley umieszcza w podkatalogu odpowiadajcym penej nazwie pakietu.
Na przykad wszystkie pliki pakietu com.horstmann.corejava powinny si znale w podkatalogu com/horstmann/corejava (w systemie Windows com\horstmann\corejava). Kompilator umieszcza pliki klas w takiej samej strukturze katalogw.
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
W tym przypadku kompilacj i uruchomienie klas naley przeprowadzi w katalogu bazowym, czyli tym, ktry zawiera katalog com:
javac com/mycompany/PayrollApp.java
java com.mycompany.PayrollApp
184
Java. Podstawy
Zauwa, e kompilator dziaa na plikach (z rozszerzeniem .java), podczas gdy interpreter
Javy uruchamia klasy.
Rozdzia 4.
Obiekty i klasy
185
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
Kompilator nie sprawdza struktury katalogw w czasie kompilacji plikw rdowych. Jeli mamy na przykad plik rdowy, na pocztku ktrego znajduje si ponisza dyrektywa:
package com.mycompany;
186
Java. Podstawy
oznaczone jako private, gdy w przeciwnym przypadku bd widoczne w caym pakiecie.
To oczywicie oznacza amanie zasad hermetyzacji. Niestety bardzo atwo mona zapomnie
o wpisaniu sowa kluczowego private. Poniszy przykad pochodzi z klasy Window dostpnej w pakiecie java.awt wchodzcym w skad JDK:
public class Window extends Container
{
String warningString;
. . .
}
Naley zauway, e zmienna warningString nie jest prywatna! Oznacza to, e metody wszystkich 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 czasem 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 niedouczeni programici mog napisa kod, ktry bdzie modyfikowa zmienne o zasigu pakietowym. Na przykad w pierwszych wersjach Javy mona byo z atwoci przemyci dodatkow 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 ustawienie 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 zapiecztowany, nie mona do niego doda adnych nowych klas. W rozdziale 10. opisujemy techniki tworzenia plikw JAR zawierajcych pakiety zapiecztowane.
188
Java. Podstawy
Od Java SE 6 do okrelenia katalogu z plikami JAR mona uy symbolu wieloznacznego:
/home/user/classdir:.:/home/user/archives/'*'
lub
c:\classdir;.;c:\archives\*
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.
Kompilator javac zawsze szuka plikw w biecym katalogu, natomiast program
uruchamiajcy Java Virtual Machine przeszukuje biecy katalog tylko wtedy, kiedy
w ciece klas znajduje si katalog .. Problemu nie ma, jeli cieka klas nie zostaa
ustawiona, poniewa wtedy zawiera tylko katalog .. Jeli cieka klas zostanie ustawiona bez katalogu ., programy bd kompiloway si bez problemw, ale nie bd
chciay dziaa.
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
/home/user/classdir/com/horstmann/corejava/Employee.class,
a w kodzie rdowym uyto klasy Employee. Najpierw kompilator prbuje kolejno znale
klasy java.lang.Employee (poniewa pakiet java.lang jest zawsze importowany domylnie), 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 kompilator 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 kompilowany 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
190
Java. Podstawy
Niektrzy zalecaj cakowite pominicie cieki klas poprzez umieszczenie wszystkich plikw JAR w katalogu jre/lib/ext. Jest to bardzo za rada, i to z dwch powodw. Archiwa rcznie adujce inne klasy nie dziaaj poprawnie, kiedy znajduj si
w katalogu rozszerze (wicej informacji na temat programw adujcych klasy znajduje
si w rozdziale 9. drugiego tomu). Ponadto programici czsto zapominaj o plikach, ktre
umiecili tam kilka miesicy wczeniej. Potem zachodz w gow, czemu loader klas
ignoruje ich wspaniae klasy, podczas gdy ten po prostu aduje dawno zapomniane klasy
z katalogu rozszerze.
pakietw,
pl publicznych i chronionych.
Znaczenie sowa kluczowego protected zostao opisane w rozdziale 5., a interfejsy w rozdziale 6.
Kada z wymienionych konstrukcji moe (i powinna) by opatrzona komentarzem. Komentarz
powinien si znajdowa bezporednio nad tym, czego dotyczy. Pocztek komentarza okrela
sekwencja znakw /**, a koniec */.
W komentarzu mona umieci dowolny tekst oraz specjalne znaczniki dokumentacyjne.
Znaczniki dokumentacyjne rozpoczynaj si od znaku @, np. @author czy @param.
Pierwsze zdanie komentarza powinno by streszczeniem. Narzdzie javadoc automatycznie
generuje strony streszcze zawierajce te zdania.
Rozdzia 4.
Obiekty i klasy
191
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 jednak unika nagwkw <h1> i poziomych kresek <hr>, poniewa mog wchodzi w interakcje z formatowaniem dokumentu.
Jeli w komentarzach znajduj si odnoniki do innych plikw, takich jak obrazy
(mog to by wykresy lub rysunki przedstawiajce elementy interfejsu uytkownika),
naley pliki te umieci w podkatalogu folderu zawierajcego plik rdowy o nazwie
doc-files. Narzdzie javadoc kopiuje katalogi doc-files i ich zawarto z katalogw rdowych do katalogw dokumentacji. Nazwa katalogu doc-files musi si znale w ciece
odnonika, np. <img src="doc-files/uml.png" alt="Diagram UML" />.
Jednak wikszo IDE automatycznie dodaje gwiazdki i zmienia ich ustawienie w odpowiedzi na zmiany w amaniu wierszy.
192
Java. Podstawy
Dodaje pozycj do sekcji Parameters metody. Opis moe zajmowa kilka wierszy
i zawiera znaczniki HTML. Wszystkie znaczniki @param dotyczce jednej metody
powinny si znajdowa w jednym miejscu.
@return opis
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;
Dodaje pozycj Author. Jeli jest kilku autorw, mona zastosowa kilka
znacznikw @author.
@version tekst
Rozdzia 4.
Obiekty i klasy
193
@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>
@see odwoanie
Znacznikw @see mona wstawi kilka, ale wszystkie musz by w jednym miejscu.
194
Java. Podstawy
polecenie:
javadoc -d docDirectory nazwaPakietu
Rozdzia 4.
Obiekty i klasy
195
utworzy odnoniki do wszystkich standardowych klas bibliotecznych w dokumentacji na stronie internetowej firmy Oracle.
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.
Informacje na temat pozostaych opcji mona znale w internetowej dokumentacji narzdzia javadoc pod adresem http://docs.oracle.com/javase/1.5.0/docs/guide/javadoc.
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.
Java nie inicjuje zmiennych lokalnych, ale zmienne skadowe obiektw. Nie naley
pozwala na inicjacj zmiennych wartociami domylnymi, tylko inicjowa
je jawnie, podajc warto domyln lub wartoci domylne we wszystkich
konstruktorach.
196
Java. Podstawy
3. Nie naley stosowa zbyt wielu rnych podstawowych typw danych
w jednej klasie.
Jeli klasa zawiera kilka powizanych ze sob zmiennych tego samego typu,
naley je zastpi now klas. Dziki temu kod klas jest bardziej zrozumiay
i atwiejszy w modyfikacji. Na przykad ponisze pola klasy Customer mona
zastpi now klas o nazwie Address:
private
private
private
private
String street;
String city;
String state;
int zip;
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.
CardDeck() { . . . }
void shuffle() { . . . }
int getTopValue() { . . . }
int getTopSuit() { . . . }
void draw() { . . . }
Rozdzia 4.
Obiekty i klasy
197
198
Java. Podstawy
Dziedziczenie
W tym rozdziale:
Klasa ArrayList
Klasy wyliczeniowe
Refleksja
Rozdzia 4. wprowadzi pojcia klas i obiektw. Ten rozdzia wprowadza kolejne zagadnienie
o fundamentalnym znaczeniu dla programowania obiektowego dziedziczenie (ang. inheritance). Z zaoenia technika ta umoliwia tworzenie nowych klas na bazie klas ju istniejcych. 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 wtpienia 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.
Przedrostki nad- i pod- pochodz od sposobu opisu zbiorw w informatyce teoretycznej i matematyce. Zbir wszystkich pracownikw zawiera zbir wszystkich kierownikw, czyli zbir pracownikw jest nadzbiorem zbioru kierownikw. Albo w drug
stron zbir kierownikw jest podzbiorem zbioru pracownikw.
Rozdzia 5.
Dziedziczenie
201
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 przypadku 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 proste 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();
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.
Musimy zaznaczy, e odwoujemy si do metody getSalary klasy Employee, a nie klasy,
w ktrej si znajdujemy. Do tego celu suy sowo kluczowe super. Instrukcja:
super.getSalary()
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;
}
Niektrzy programici uwaaj, e sowo kluczowe super jest odpowiednikiem referencji this. Nie jest to jednak precyzyjne porwnanie, poniewa sowo super nie jest
referencj do obiektu. Nie mona na przykad przypisa jego wartoci do innej zmiennej
obiektowej. super to sowo kluczowe, ktre nakazuje kompilatorowi wywoanie metody
z nadklasy.
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
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)
: Employee(n, s, year, month, day)
{
bonus = 0;
}
// C++
204
Java. Podstawy
Wstawiamy do niej obiekty zwykych pracownikw i kierownikw:
staff[0] = boss;
staff[1] = new Employee("Henryk Kwiatek", 50000, 1989, 10, 1);
staff[2] = new Employee("Artur Kwiatkowski", 40000, 1990, 3, 15);
wybiera odpowiedni metod getSalary. Warto zauway, e zmienna e jest zadeklarowana jako typ Employee, ale rzeczywistym typem, do ktrego odwouje si ta zmienna, moe
by albo Employee, albo Manager.
Kiedy zmienna e odwouje si do obiektu klasy Employee, wywoanie e.getSalary() wywouje
metod getSalary klasy Employee. Jeli jednak e odwouje si do obiektu klasy Manager,
metoda getSalary jest wywoywana z klasy Manager. Maszyna wirtualna rozpoznaje, do jakiego
typu odwouje si zmienna e, dziki czemu moe wywoa odpowiedni metod.
Moliwo odwoywania si przez obiekty (jak zmienna e) do wielu rnych typw nosi
nazw polimorfizmu (ang. polymorphism). Automatyczny dobr odpowiednich metod w trakcie dziaania programu nazywa si wizaniem dynamicznym (ang. dynamic binding). Oba
te zagadnienia zostay szczegowo opisane w tym rozdziale.
W Javie nie ma potrzeby deklarowania metody jako wirtualnej. Wizanie dynamiczne
jest dziaaniem domylnym. Aby metoda nie bya wirtualna, naley uy sowa kluczowego final (opis sowa kluczowego final znajduje si dalej w tym rozdziale).
Listing 5.1 przedstawia program demonstrujcy rnic naliczania pensji dla obiektw klasy
Employee (listing 5.2) i Manager (listing 5.3).
Listing 5.1. inheritance/ManagerTest.java
package inheritance;
/**
* Ten program demonstruje dziedziczenie.
* @version 1.21 2004-02-21
* @author Cay Horstmann
*/
Rozdzia 5.
Dziedziczenie
205
206
Java. Podstawy
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
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.
Mona na przykad przypisa obiekt podklasy do zmiennej nadklasy.
Employee e;
e = new Employee(. . .);
e = new Manager(. . .);
208
Java. Podstawy
W tym przypadku zmienne staff[0] i boss odwouj si do tego samego obiektu. Jednak
dla kompilatora staff[0] jest obiektem wycznie klasy Employee.
Oznacza to, e mona uy poniszego wywoania:
boss.setBonus(5000);
// OK
// bd
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 przypisanie jest niedozwolone:
Manager m = staff[i];
// bd
Powd jest jasny nie wszyscy pracownicy s kierownikami. Gdyby powysze przypisanie udao si i zmienna m byaby referencj do obiektu klasy Employee, ktry nie jest kierownikiem, to moliwe byoby te wywoanie m.setBonus(), a to z kolei spowodowaoby
bd czasu wykonania.
W Javie moliwa jest konwersja tablic referencji do obiektw podklasy na tablic
referencji do obiektw nadklasy bez rzutowania. Spjrzmy na ponisz tablic obiektw klasy Manager:
Manager[] managers = new Manager[10];
// OK
Mona pomyle, czemu nie. Przecie kady kierownik jest te pracownikiem. Jednak dzieje
si tu co zaskakujcego. Pamitajmy, e zmienne managers i staff s referencjami do
tej samej tablicy. Spjrzmy teraz na ponisz instrukcj:
staff[0] = new Employee("Henryk Kwiatek", ...);
Kompilator nie zgosi adnego sprzeciwu przy kompilacji tego przypisania, ale zmienne
staff[0] i manager[0] s t sam referencj, a wic wyglda na to, e awansowalimy
zwykego pracownika na stanowisko kierownicze. Byaby to bardzo za sytuacja. Wywoanie managers[0].setBonus(1000) usiowaoby uzyska dostp do nieistniejcego pola
i spowodowaoby uszkodzenie okolicznej pamici.
Aby unikn takiego zniszczenia, wszystkie tablice pamitaj, z jakim typem zostay
utworzone, i pilnuj, aby przechowywane w nich byy tylko odpowiednie referencje.
Na przykad tablica utworzona za pomoc instrukcji new Manager[10] pamita, e jest
tablic kierownikw. Prba zapisania w niej referencji do typu Employee spowoduje wyjtek ArrayStoreException.
Rozdzia 5.
Dziedziczenie
209
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
210
Java. Podstawy
4. Kiedy program dziaa i wywouje metod za pomoc wizania dynamicznego,
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)
Rozdzia 5.
Dziedziczenie
211
Finalna moe te by metoda w klasie. W takim przypadku nie mona jej przesoni w adnej 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 odpowiedzialnoci 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
Wedug niektrych programistw wszystkie metody powinny by finalne, chyba e istnieje
dobry powd, dla ktrego potrzebny jest w danym przypadku polimorfizm. W C++ i C#
metody nie s polimorficzne, dopki jawnie si tego nie zada. Moe to jest w pewnym
sensie skrajne podejcie, ale zgadzamy si, e przy projektowaniu hierarchii klas naley
powanie rozway moliwo zastosowania modyfikatora final dla klas i metod.
Na pocztku istnienia Javy niektrzy programici uywali sowa kluczowego final w nadziei,
e unikn narzutu powodowanego przez wizanie dynamiczne. Jeli metoda nie jest przesonita i jest krtka, kompilator moe zoptymalizowa jej wywoywanie poprzez proces
polegajcy na wstawieniu jej kodu w miejsce wywoania (ang. inlining). Na przykad wywoanie 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 jednak metoda getName moe by przesonita w innej klasie, kompilator nie moe zastosowa
wstawiania kodu, poniewa nie wie, jak dziaa ta przesonita wersja.
Na szczcie kompilator JIT w maszynie wirtualnej spisuje si lepiej ni zwyky kompilator.
Wie dokadnie, ktre klasy s rozszerzeniem danej klasy, i ma moliwo sprawdzenia, czy
dana metoda jest przesonita w ktrej z tych klas. Jeli metoda jest krtka, czsto wywoywana i nie jest przesonita, kompilator JIT moe zastosowa inlining. Co si stanie, jeli
maszyna wirtualna zaaduje inn podklas, ktra przesania nasz metod? Wtedy proces
inliningu musi zosta cofnity. Jest to operacja powolna, ale zdarza si bardzo rzadko.
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 oznaczajca rzutowanie. Na przykad:
double x = 3.405;
int nx = (int) x;
W powyszym kodzie warto zmiennej x zostaa przekonwertowana na typ cakowitoliczbowy, co spowodowao utrat czci uamkowej.
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 skadowych obiektw klasy Manager, konieczne by byo ich przekonwertowanie z powrotem na
Rozdzia 5.
Dziedziczenie
213
// Bd
I wreszcie, kompilator nie pozwoli na rzutowanie, ktre nie ma szans powodzenia. Na przykad
rzutowanie:
Date c = (Date) staff[1];
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 wskazuje 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 pomysem. 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 moliwo, 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 rzutowania i operatora instanceof.
Skadnia rzutowania w Javie pochodzi ze starego i zego jzyka C, natomiast
dziaanie tego mechanizmu jest podobne do bezpiecznego rzutowania dynamic_
cast w C++. Na przykad:
Manager boss = (Manager) staff[1];
// Java
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]);
if (boss != NULL) . . .
// C++
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;
}
}
216
Java. Podstawy
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 podklasa 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 abstrakcyjnych.
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")
W C++ klasa jest abstrakcyjna, jeli zawiera co najmniej jedn funkcj czysto wirtualn.
Nie ma w tym jzyku specjalnego sowa kluczowego okrelajcego klas abstrakcyjn.
Rozdzia 5.
Dziedziczenie
217
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 referencji typu Person wstawiane s obiekty klas Employee i Student:
Person[] people = new Person[2];
people[0] = new Employee(. . .);
people[1] = new Student(. . .);
218
Java. Podstawy
return name;
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 abstrakcyjnej 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 zdefiniowa metody getDescription w podklasach Employee i Student? Gdybymy tak zrobili, niemoliwe byoby wywoanie metody getDescription na rzecz zmiennej p, poniewa kompilator zezwala na wywoywanie tylko tych metod, ktre s zdefiniowane w danej klasie.
Metody abstrakcyjne s bardzo wanym elementem jzyka programowania Java. Najczciej wystpuj w interfejsach. Wicej informacji na temat interfejsw zawiera rozdzia 6.
220
Java. Podstawy
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.
W praktyce z pl chronionych naley korzysta ostronie. Zamy, e nasza klasa, ktra
zawiera pola chronione, jest uywana przez innych programistw. Ci programici mog
tworzy bez naszej wiedzy klasy dziedziczce po naszej klasie i w ten sposb uzyska dostp
do chronionych pl naszej klasy. W takiej sytuacji zmiana implementacji owej nadklasy
pocigaaby za sob problemy u wspomnianych programistw. Takie dziaanie jest sprzeczne
z ide OOP, ktra opiera si na hermetyzacji danych.
Wicej sensu ma tworzenie chronionych metod. Metoda moe by chroniona, jeli jej uycie 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. Poniewa 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 internetowej (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 wartoci. Aby zrobi z niej uytek, trzeba posiada wiedz na temat oryginalnego typu i wykona 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 podstawowe 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*.
222
Java. Podstawy
Employee other = (Employee) otherObject;
// Sprawdzenie, czy pola maj identyczne wartoci.
return name.equals(other.name)
&& salary == other.salary
&& hireDay.equals(other.hireDay);
}
}
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 pozostaych 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 nalec 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 nastpujce wasnoci:
Rozdzia 5.
1.
Dziedziczenie
223
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 wywoanie 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 instanceof 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).
Z naszego punktu widzenia moliwe s dwa odrbne scenariusze:
224
Java. Podstawy
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 zatrudnienia, ale rne dodatki do pensji, chcemy, aby byy uznane za rne. Dlatego uylimy testu
getClass.
Zamy jednak, e do porwnywania uylimy identyfikatorw pracownikw. Ten rodzaj
porwnania jest odpowiedni dla wszystkich podklas. W takim przypadku moglibymy zastosowa operator instanceof, a metod Employee.equals zadeklarowa jako finaln.
Standardowa biblioteka Javy zawiera ponad 150 implementacji metody equals.
Znajduj si wrd nich wersje z operatorem instanceof, wywoaniem getClass,
przechwytywaniem wyjtku ClassCastException i nierobice nic. W dokumentacji API
klasy java.sql.Timestamp mona znale notatk, w ktrej implementatorzy sami ze
wstydem przyznaj, e zapdzili si w kozi rg. Klasa java.sql.Timestamp dziedziczy po
klasie java.util.Date, ktrej metoda equals wykorzystuje operator instanceof. Nie da
si przesoni metody equals, aby bya dokadna i symetryczna.
Rozdzia 5.
Dziedziczenie
225
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)
Zostanie zgoszony bd, poniewa ta metoda nie przesania adnej metody w nadklasie Object.
java.util.Arrays 1.2
Zwraca warto true, jeli tablice s rwne pod wzgldem liczby elementw
i elementy te s takie same na odpowiadajcych sobie pozycjach w obu tablicach.
Tablice te mog przechowywa elementy nastpujcych typw: Object, int, long,
short, char, byte, boolean, float, double.
java.util.Objects 7
Zwraca warto true, jeli a i b s null, false jeli a lub b jest null,
oraz a.equals(b) w pozostaych przypadkach.
226
Java. Podstawy
to dwie rne liczby. Tabela 5.1 przedstawia trzy przykadowe kody mieszajce zwrcone
przez metod hashCode klasy String.
Kod mieszajcy
Witaj
83588971
Henryk
-2137002381
Kwiatek
1350142454
Metoda hashCode znajduje si w klasie Object. Dlatego kady obiekt ma domylny kod mieszajcy, 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());
Kod mieszajcy
2556
sb
20526976
2556
tb
205271144
Naley zauway, e acuchy s i t maj takie same kody, poniewa kody mieszajce acuchw 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.
W przypadku przedefiniowania metody equals naley take przedefiniowa metod hashCode
dla obiektw, ktre uytkownicy mog wstawia do tablicy mieszajcej (ang. hash table)
tablice mieszajce zostay opisane w rozdziale 13.
Metoda hashCode powinna zwraca liczb cakowit (moe by ujemna). Aby kody mieszajce rnych obiektw byy rne, wystarczy uy kombinacji kodw mieszajcych pl tych
obiektw.
Poniej znajduje si przykadowa metoda hashCode klasy Employee:
Rozdzia 5.
Dziedziczenie
227
class Employee
{
public int hashCode()
{
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
. . .
}
Od Java 7 mona tu dokona dwch udoskonale. Po pierwsze, lepiej jest uy zabezpieczonej przed null metody Objects.hashCode, ktra zwraca 0, jeli jej argument jest null, albo
wynik wywoania hashCode na tym argumencie w pozostaych przypadkach.
public int hashCode()
{
return 7 * Objects.hashCode(name)
+ 11 * new Double(salary).hashCode()
+ 13 * Objects.hashCode(hireDay);
}
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.
Jeli pola s typu tablicowego, mona uy metody Arrays.hashCode, ktra oblicza
kod mieszajcy zoony z kodw mieszajcych elementw tablicy.
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
228
Java. Podstawy
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 metodzie toString, nazw t mona pobra za pomoc wywoania getClass().getName().
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
Rozdzia 5.
Dziedziczenie
229
{
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().
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 standardowej 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 klasach 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);
Rozdzia 5.
Dziedziczenie
231
232
Java. Podstawy
if (getClass() != otherObject.getClass()) return false;
// Teraz wiadomo, e otherObject jest typu Employee i nie jest null
Employee other = (Employee) otherObject;
// Sprawdzenie, czy pola maj identyczne wartoci
return Objects.equals(name, other.name) && salary == other.salary &&
Objects.equals(hireDay, other.hireDay);
}
public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}
public String toString()
{
return getClass().getName() + "[name=" + name + ",salary=" + salary +
",hireDay=" + hireDay
+ "]";
}
}
Rozdzia 5.
Dziedziczenie
233
{
return super.hashCode() + 17 * new Double(bonus).hashCode();
}
public String toString()
{
return super.toString() + "[bonus=" + bonus + "]";
}
}
java.lang.Object 1.0
Class getClass()
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()
String getName()
Class getSuperClass()
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 operatorem 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 usunito 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 miejsce 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 przyblieniu, przed zapenieniem listy naley wywoa metod ensureCapacity.
staff.ensureCapacity(100);
// Pojemno 100
// Rozmiar 100
Pomidzy pojemnoci listy tablicowej a rozmiarem tablicy jest dua rnica. Tablica
o rozmiarze 100 zawiera 100 miejsc, w ktrych moe przechowywa dane. Lista tablicowa 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 odpowiednikiem 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 zbierania 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 operatorw, 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>()
ArrayList<T>(int initialCapacity)
initialCapacity
obj
Element do dodania
int size()
capacity
void trimToSize()
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);
list.set(0, x);
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());
Czasami moliwe jest wzicie tego, co najlepsze, z tablic i list tablicowych elastycznoci
i wygodnego dostpu do elementw. Trzeba zastosowa nastpujc sztuczk. Naley utworzy 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);
238
Java. Podstawy
Podobnie ze rodka listy mona usun dowolny element:
Employee e = staff.remove(n);
Rozdzia 5.
Dziedziczenie
239
index
obj
Nowa warto
T get(int index)
index
index
obj
Nowy element
T remove(int index)
index
240
Java. Podstawy
Majc ponisz star klas:
public class EmployeeDB
{
public void update(ArrayList list) { ... }
public ArrayList find(String query) { ... }
}
Jeli natomiast obiekt surowej klasy ArrayList zostanie przypisany do typu z parametrem,
zostanie wywietlone ostrzeenie.
ArrayList<Employee> result = employeeDB.find(query);
// Powoduje ostrzeenie
Rozdzia 5.
Dziedziczenie
241
Jeli natomiast obiekt klasy Integer zostanie przypisany do wartoci int, zostanie automatycznie odpakowany. To znaczy kompilator przekonwertuje:
int n = list.get(i);
na:
int n = list.get(i).intValue();
242
Java. Podstawy
Automatyczne opakowywanie i odpakowywanie dziaa nawet w przypadku operacji arytmetycznych. Mona na przykad zastosowa do referencji do obiektu osonowego operator
inkrementacji:
Integer n = 3;
n++;
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 obiektw osonowych za pomoc metody equals.
Specyfikacja automatycznego opakowywania wymaga, aby typy boolean, char 127
oraz short i int w przedziale 128 do 127 byy opakowywane w ustalone obiekty.
Jeli na przykad w powyszym fragmencie kodu a i b zostayby zainicjowane wartoci 100,
porwnywanie musiaoby zakoczy si powodzeniem.
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
Niektrzy programici uwaaj, e klas osonowych mona uywa do implementacji metod, ktre mog modyfikowa parametry liczbowe. S jednak w bdzie.
Pamitamy z rozdziau 4., e w Javie nie mona napisa metody zwikszajcej parametr liczbowy, poniewa parametry s zawsze przekazywane do metod przez warto.
public static void triple(int x)
{
x = 3 * x;
}
// nie zadziaa
// modyfikacja lokalnej zmiennej
// nie zadziaa
int intValue()
Zwraca warto obiektu Integer jako liczb typu int (przesania metod intValue
z klasy Number).
244
Java. Podstawy
Number parse(String s)
i
System.out.printf("%d %s", n, "widgets");
dotycz tej samej metody, mimo e pierwsze z nich ma dwa parametry, a drugie trzy.
Definicja metody printf wyglda nastpujco:
public class PrintStream
{
public PrintStream printf(String fmt, Object... args) { return format(fmt, args); }
}
W powyszym kodzie trzykropek () jest czci kodu Javy. Okrela on, e metoda moe
przyjmowa dowoln liczb obiektw (parametr fmt jest obowizkowy).
Metoda printf w rzeczywistoci przyjmuje dwa parametry acuch formatu i tablic
Object[], ktra zawiera wszystkie pozostae parametry (jeli zostanie podana warto typu
podstawowego, jak int, mechanizm automatycznego opakowywania zamieni j na obiekt). Musi
ona przeskanowa acuch fmt i dopasowa i-ty specyfikator formatu do wartoci args[i].
Innymi sowy, z punktu widzenia programisty implementujcego metod printf typ parametru Object jest dokadnie tym samym co Object[].
Kompilator musi przekonwertowa kade wywoanie metody printf, pakujc parametry do
tablicy i wykonujc w razie potrzeby automatyczne opakowywanie:
System.out.printf("%d %s", new Object[] { new Integer(n), "widgets" } );
Rozdzia 5.
Dziedziczenie
245
Mona definiowa wasne metody ze zmienn liczb parametrw. Parametry te mog by kadego 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.
Ostatnim parametrem metody o zmiennej liczbie parametrw moe by tablica.
Na przykad:
System.out.printf("%d %s", new Object[] { new Integer(1), "widgets" } );
W zwizku z tym mona przedefiniowa istniejc funkcj, ktrej ostatni parametr jest
tablic, na metod ze zmienn liczb parametrw, nie uszkadzajc istniejcego kodu.
Na przykad w ten sposb rozszerzono metod MessageFormat.format w Java SE 5.0.
Mona nawet zadeklarowa metod main w nastpujcy sposb:
public static void main(String... args)
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 ==.
Do typu wyliczeniowego mona doda konstruktory, metody i pola. Oczywicie konstruktory
s wywoywane tylko wwczas, gdy s konstruowane stae wyliczeniowe. Na przykad:
public enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private String abbreviation;
private Size(String abbreviation) { this.abbreviation = abbreviation; }
246
Java. Podstawy
public String getAbbreviation() { return abbreviation; }
}
Wszystkie typy wyliczeniowe s podklasami klasy Enum. Dziedzicz po niej kilka metod.
Do najbardziej przydatnych naley metoda toString, ktra zwraca nazw staej wyliczeniowej. Na przykad wywoanie Size.SMALL.ToString() zwraca acuch SMALL.
Przeciwiestwem metody toString jest statyczna metoda valueOf. Na przykad ponisza
instrukcja ustawia s na Size.SMALL.
Size s = (Size) Enum.valueOf(Size.class, "SMALL");
Kady typ wyliczeniowy ma statyczn metod values, ktra zwraca wszystkie wartoci wyliczenia. Na przykad wywoanie:
Size[] values = Size.values();
Rozdzia 5.
Dziedziczenie
247
}
}
enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private Size(String abbreviation) { this.abbreviation = abbreviation; }
public String getAbbreviation() { return abbreviation; }
private String abbreviation;
}
java.lang.Enum<E> 5.0
String toString()
int ordinal()
Zwraca ujemn liczb cakowit, jeli staa wyliczeniowa wystpuje przed other,
zero jeli this == other, lub liczb dodatni w przeciwnym przypadku.
Kolejno staych jest okrelana przez deklaracj enum.
5.7. Refleksja
Biblioteka refleksyjna dostarcza bogaty i zaawansowany zestaw narzdzi do pisania programw, 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 przyzwyczajeni 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.
Program, ktry moe analizowa funkcjonalno klas, nazywa si programem refleksyjnym.
Mechanizm refleksji ma bardzo due moliwoci. W kolejnych podrozdziaach opisujemy jego
nastpujce zastosowania:
248
Java. Podstawy
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
Obiekt klasy Class odpowiadajcy nazwie wybranej klasy mona utworzy za pomoc statycznej 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
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.
Od Java SE 5.0 klasa Class jest sparametryzowana. Na przykad Employee.class
jest typu Class<Employee>. Nie bdziemy dry tego tematu, poniewa jeszcze
bardziej skomplikowalibymy i tak ju wystarczajco abstrakcyjn koncepcj. Dla praktycznych celw mona zignorowa parametr typu i pracowa na surowym typie Class.
Wicej informacji na ten temat znajduje si w rozdziale 13.
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 przechowywanej 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.
Rozdzia 5.
Dziedziczenie
251
}
catch(Exception e)
{
Procedura obsugi wyjtku
}
Na przykad:
try
{
String name = . . .;
Class cl = Class.forName(name);
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
Object newInstance()
args
java.lang.Throwable 1.0
void printStackTrace()
252
Java. Podstawy
int hashCode();
int compareTo(java.lang.Object);
int compareTo(java.lang.Double);
boolean equals(java.lang.Object);
java.lang.String toString();
static java.lang.String toString(double);
static java.lang.Double valueOf(java.lang.String);
static boolean isNaN(double);
boolean isNaN();
static boolean isInfinite(double);
boolean isInfinite();
byte byteValue();
short shortValue();
int intValue();
long longValue();
float floatValue();
double doubleValue();
Rozdzia 5.
public
public
public
public
static
static
static
static
double
native
native
native
Dziedziczenie
253
parseDouble(java.lang.String);
long doubleToLongBits(double);
long doubleToRawLongBits(double);
double longBitsToDouble(long);
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 program bdziemy wykorzystywa w kolejnym rozdziale, w ktrym bdziemy zaglda do wntrza
klas wewntrznych generowanych automatycznie przez kompilator.
Listing 5.13. reflection/ReflectionTest.java
package reflection;
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();
for (Constructor c : constructors)
{
String name = c.getName();
System.out.print("
");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "(");
// Drukowanie typw parametrw.
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
/**
* Drukuje wszystkie metody klasy.
* @param cl klasa
*/
public static void printMethods(Class cl)
{
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods)
{
Class retType = m.getReturnType();
String name = m.getName();
System.out.print("
");
// Drukowanie modyfikatorw, typu zwrotnego i nazwy metody.
Rozdzia 5.
Dziedziczenie
255
256
Java. Podstawy
Class getDeclaringClass()
Zwraca obiekt klasy Class reprezentujcy klas, ktra definiuje dany konstruktor,
metod lub pole.
int getModifiers()
String getName()
Rozdzia 5.
Dziedziczenie
257
Teraz pjdziemy o krok dalej i dobierzemy si do zawartoci pl danych. Oczywicie zawarto okrelonego pola obiektu o znanych w trakcie pisania programu typie i nazwie mona
podejrze bez trudu. Jednak refleksja umoliwia uzyskanie informacji o polach obiektw,
ktre w czasie kompilacji nie byy jeszcze znane.
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 spowoduje 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.
Przy standardowych ustawieniach mechanizm refleksji honoruje mechanizmy ochronne Javy.
Jeli jednak program nie dziaa pod kontrol menedera zabezpiecze, mona omin ustawienia ochrony dostpu. W tym celu naley wywoa metod setAccessible na rzecz obiektu
klasy Field, Method lub Constructor. Na przykad:
f.setAccessible(true);
Metoda setAccessible naley do klasy AccessibleObject, ktra jest wspln nadklas klas
Field, Method i Constructor. Zostaa ona utworzona z myl o debugerach, schowkach i podobnych 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.
Oczywicie wartoci, ktre mona sprawdzi, mona te ustawi. Wywoanie f.set(obj,
value) ustawia pole f obiektu obj na warto value.
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 znajduje si w kolejnym podrozdziale.
Za pomoc metody toString mona zajrze do rodka kadego obiektu. Na przykad
wywoanie:
ArrayList<Integer> squares = new ArrayList<>();
for (int i = 1; i <= 5; i++) squares.add(i * i);
System.out.println(new ObjectAnalyzer().toString(squares));
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][][]
260
Java. Podstawy
import
import
import
import
java.lang.reflect.Array;
java.lang.reflect.Field;
java.lang.reflect.Modifier;
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
boolean isAccessible()
Field[] getFields()
Field[] getDeclaredFields()
Zwraca pole o danej nazwie zadeklarowane w klasie lub tablic wszystkich pl.
java.lang.reflect.Field 1.1
Ustawia pole reprezentowane przez obiekt Field w obiekcie obj na now warto.
262
Java. Podstawy
Employee[] a = new Employee[100];
. . .
// Tablica jest pena.
a = Arrays.copyOf(a, 2 * a.length);
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.
Pierwsz warto mona uzyska za pomoc wywoania Array.getLength(a). Statyczna metoda
getLength klasy Array zwraca rozmiar tablicy. Aby sprawdzi typ elementw nowej tablicy:
1.
Rozdzia 5.
Dziedziczenie
263
Naley zauway, e metoda copyOf moe powiksza tablice kadego typu, nie tylko przechowujce 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.
Listing 5.16. arrays/CopyOfTest.java
package arrays;
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));
String[] b = { "Tomek", "Daniel", "Henryk" };
b = (String[])goodCopyOf(b);
System.out.println(Arrays.toString(b));
/**
* 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
System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength));
return newArray;
}
/**
* 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
Rozdzia 5.
Dziedziczenie
265
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.
Wrd niestandardowych rozszerze dodanych przez firm Microsoft do pochodzcego od Javy jzyka J++ (i jego nastpcy C#) znajduje si jeszcze inny typ wskanikw do metod o nazwie delegacja (ang. delegate). Nie jest to to samo co klasa Method
opisywana w tym podrozdziale. Bardziej przydatne od delegacji s jednak klasy wewntrzne
(ktre wprowadzamy w kolejnym rozdziale).
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 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)
266
Java. Podstawy
public static native double java.lang.Math.sqrt(double)
1.0000 |
1.0000
2.0000 |
1.4142
3.0000 |
1.7321
4.0000 |
2.0000
5.0000 |
2.2361
6.0000 |
2.4495
7.0000 |
2.6458
8.0000 |
2.8284
9.0000 |
3.0000
10.0000 |
3.1623
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.
Wyrwnanie funkcji Math.sqrt zostao uzyskane poprzez ustawienie f na:
Math.class.getMethod("sqrt", double.class)
/**
Rozdzia 5.
Dziedziczenie
267
268
Java. Podstawy
java.lang.reflect.Method 1.1
Dziedziczenie umoliwia zaoszczdzenie wielu wierszy kodu, ale niestety jest czsto
naduywane. Wyobramy sobie na przykad, e potrzebujemy klasy o nazwie
Contractor (pracownik kontraktowy). Pracownik kontraktowy ma imi i nazwisko
oraz dat zatrudnienia, ale nie ma staej pensji. Zamiast tego otrzymuje
wynagrodzenie zalene od liczby przepracowanych godzin i nie pracuje na tyle
dugo, aby dosta podwyk. Istnieje wic pokusa, aby utworzy podklas Contractor
klasy Employee i doda pole hourlyWage (stawka godzinowa).
class Contractor extends Employee
{
private double hourlyWage;
. . .
}
Rozdzia 5.
Dziedziczenie
269
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);
270
Java. Podstawy
if (x jest typu 1)
dziaanie1(x);
else if (x jest typu 2)
dziaanie2(x);
Interfejsy
i klasy wewntrzne
W tym rozdziale:
Interfejsy
Klonowanie obiektw
Klasy wewntrzne
Klasy poredniczce
272
Java. Podstawy
Rozdzia zamyka opis obiektw porednich, implementujcych dowolne interfejsy. Obiekty
te to bardzo wyspecjalizowane narzdzia, ktrych uywaj programici narzdzi systemowych. Przy pierwszej lekturze tej ksiki mona ten podrozdzia pomin.
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 nalee do klas, ktre implementuj interfejs Comparable.
Interfejs Comparable wyglda nastpujco:
public interface Comparable
{
int compareTo(Object other);
}
Oznacza to, e kada klasa implementujca powyszy interfejs musi mie metod compareTo,
ktra przyjmuje parametr typu Object i zwraca liczb cakowit.
Od Java SE 5.0 interfejs Comparable jest typem sparametryzowanym:
public interface Comparable<T>
{
int compareTo(T other);
// Parametr jest typu T.
}
Nadal mona uywa surowego typu Comparable bez parametru typu, ale wtedy
konieczne jest wykonywanie na wasn rk rzutowania parametru metody compareTo
na odpowiedni typ.
Rozdzia 6.
273
Nastpnie naley zdefiniowa metod compareTo. Powiedzmy, e chcemy porwnywa pracownikw pod wzgldem wysokoci ich pensji. Poniej znajduje si odpowiednia implementacja metody compareTo:
public int compareTo(Object otherObject)
{
Employee other = (Employee) otherObject;
return Double.compare(salary, other.salary);
}
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.
W deklaracji metody compareTo w interfejsie nie uyto sowa kluczowego public,
poniewa wszystkie metody w interfejsie s publiczne. Natomiast w implementacji interfejsu sowo to musi si pojawi w deklaracji metody. W przeciwnym przypadku
kompilator uzna, e metoda jest widoczna w obrbie pakietu co jest domylnym
zachowaniem dla klas. Nastpnie kompilator zgasza, e nadano mniejsze uprawnienia
dostpu.
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);
}
. . .
}
274
Java. Podstawy
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 wiadomo, e identyfikatory nie mog mie wartoci ujemnych lub e ich maksymalna warto 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 zaimplementowa metod compareTo. To nadzwyczaj rozsdne. Musi istnie jaki sposb na porwnywanie 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.
Mona si spodziewa, e metoda sort klasy Arrays przyjmuje tablic Comparable[],
dziki czemu kompilator moe zgasza bdy, jeli metoda ta zostanie wywoana
przy uyciu tablicy zawierajcej obiekty nieimplementujce interfejsu Comparable. Niestety tak nie jest. W zamian metoda sort przyjmuje tablic Object[] i stosuje mao eleganckie rzutowanie:
// Kod z biblioteki standardowej niezalecany
if (((Comparable) a[i]).compareTo(a[j]) > 0)
{
// Zamiana miejscami obiektw a[i] i a[j]
. . .
}
Jeli obiekt a[i] nie naley do klasy implementujcej interfejs Comparable, maszyna
wirtualna zgasza wyjtek.
Rozdzia 6.
275
Listing 6.1 przedstawia peny kod programu sortujcego tablic egzemplarzy klasy Employee,
ktra z kolei zostaa zaprezentowana na listingu 6.2.
Listing 6.1. interfaces/EmployeeSortTest.java
package interfaces;
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];
staff[0] = new Employee("Henryk Kwiatek", 35000);
staff[1] = new Employee("Karol Kowalski", 75000);
staff[2] = new Employee("Tadeusz Nowak", 38000);
Arrays.sort(staff);
// Drukowanie informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
}
}
276
Java. Podstawy
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
/**
* 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
Porwnuje obiekt z obiektem other i zwraca liczb ujemn, jeli pierwszy obiekt
jest mniejszy od drugiego, zero, jeli obiekty s rwne, lub liczb dodatni
w przeciwnym przypadku.
java.util.Arrays 1.2
Zwraca ujemn liczb cakowit, gdy x < y, zero, gdy x i y s rwne, oraz dodatni
liczb cakowit w pozostaych przypadkach.
java.lang.Double 7
Zwraca ujemn liczb cakowit, gdy x < y, zero, gdy x i y s rwne, oraz dodatni
liczb cakowit w pozostaych przypadkach.
// bd
Rozdzia 6.
277
// nie
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 pracownicy. Jednak odwrotne wywoanie y.compareTo(x) spowoduje wyjtek ClassCastException.
Jest to taka sama sytuacja jak w przypadku metody equals, ktr opisywalimy w rozdziale 5. Rozwizanie jest rwnie takie samo. Istniej dwa osobne scenariusze.
Jeli porwnywanie w podklasach opiera si na innych zasadach, naley zabroni porwnywania 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
278
Java. Podstawy
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 najwyszego 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.
}
Rozdzia 6.
279
// czemu nie?
// bd
// OK
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).
Interfejsy maj wikszo zalet dziedziczenia wielokrotnego, a s pozbawione wad zwizanych z komplikacj i efektywnoci jzyka.
Jzyk C++ obsuguje dziedziczenie wielokrotne z wszystkimi jego komplikacjami, jak
bazowe klasy wirtualne, zasady dominacji i poprzeczne rzutowanie wskanikw. Niewielu programistw jzyka C++ korzysta z dziedziczenia wielokrotnego, a niektrzy twierdz nawet, e nie powinno si go uywa w ogle. Inni programici zalecaj wykorzystywa dziedziczenie wielokrotne tylko w stylu mix-in. W takim przypadku gwna klasa
bazowa opisuje obiekt macierzysty, a dodatkowe klasy bazowe (tzw. mix-ins) mog
dostarcza dodatkowe cechy. Ten styl przypomina znan z Javy metod jednej klasy
bazowej i dodatkowych interfejsw. Jednak klasy dodatkowe w C++ mog dodawa
domylne zachowania, podczas gdy interfejsy w Javie nie.
280
Java. Podstawy
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.
281
Czy takie pytkie kopiowanie w czym przeszkadza? To zaley. Jeli podobiekt wspdzielony 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.
W przypadku kadej klasy naley podj nastpujce decyzje:
1.
zmienialnych podobiektw.
3. Czy metoda clone nie powinna by wywoywana.
282
Java. Podstawy
Trzecia z wymienionych opcji jest domylna. Aby mc wybra ktr z pozostaych, klasa musi:
1.
W takim przypadku uycie interfejsu Cloneable nie ma nic wsplnego z normalnym zastosowaniem interfejsw. W szczeglnoci nie okrela on metody clone metoda ta jest
dziedziczona po klasie Object. Interfejs jest tu jedynie znacznikiem informujcym, e projektant klasy wie, na czym polega klonowanie. Obiekty s tak wraliwe na punkcie klonowania, e generuj kontrolowany wyjtek, jeli tylko obiekt dajcy sklonowania nie implementuje interfejsu Cloneable.
Interfejs Cloneable jest jednym z kilku interfejsw znacznikowych (ang. tagging
interface, marker interface) Javy. Przypomnijmy, e typowym zastosowaniem interfejsw, jak Comparable, jest zapewnienie, e okrelona klasa zawiera implementacj
okrelonej metody lub metod. Interfejs znacznikowy nie ma adnych metod, a jego jedynym celem jest zezwolenie na uycie operatora instanceof do sprawdzenia typu:
if (obj instanceof 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.
283
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
zwizanych, przerzucajc si na inn metod. W kadym razie klonowanie jest znacznie
rzadziej wykonywan operacj, ni si wydaje. Implementacj metody clone zawiera mniej
ni 5 procent wszystkich klas w bibliotece standardowej.
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, poniewa metoda clone wykonuje kopiowanie gbokie.
Wszystkie typy tablicowe maj publiczn (niechronion) metod clone. Mona za
jej pomoc tworzy nowe tablice zawierajce kopie wszystkich elementw. Na
przykad:
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
int[] cloned = (int[]) luckyNumbers.clone();
cloned[5] = 12;
// Nie zmienia elementu luckyNumbers[5].
Rozdzia 6.
{
285
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();
286
Java. Podstawy
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 instrukcje, 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.
287
Toolkit.getDefaultToolkit().beep();
Zatrzymajmy si na chwil przy parametrze ActionEvent metody actionPerformed. Dostarcza on informacji dotyczcych zdarzenia, jak np. obiekt rdowy, ktry je wygenerowa
wicej na ten temat zostao napisane w rozdziale 8. W tym przypadku jednak dane zdarzenia nie maj znaczenia, a wic mona bez obaw zignorowa wspomniany parametr.
Nastpnie utworzymy obiekt naszej klasy i uyjemy go w konstruktorze klasy Timer.
ActionListener listener = new TimePrinter();
Timer t = new Timer(10000, listener);
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).
Na koniec uruchamiamy zegar.
t.start();
288
Java. Podstawy
t.start();
JOptionPane.showMessageDialog(null, "Zamkn program?");
System.exit(0);
}
}
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
Toolkit.getDefaultToolkit().beep();
}
}
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.
Warto zauway, e klasa javax.swing.Timer zostaa w tym programie zaimportowana bezporednio. Ma to na celu zapobiec konfliktowi klas javax.swing.Timer i java.util.Timer,
ktre su do planowania zada wykonywanych w tle.
javax.swing.JOptionPane 1.2
void start()
void stop()
Rozdzia 6.
289
void beep()
Klas wewntrzn mona ukry przed innymi klasami tego samego pakietu.
Poniewa temat ten jest nieco skomplikowany, rozbijemy go na kilka kolejno omwionych
czci:
opis lokalnych klas wewntrznych (ang. local inner class), ktre maj dostp
do zmiennych lokalnych otaczajcych je klas;
290
Java. Podstawy
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 publicznych. 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++.
public void start() { . . . }
public class TimePrinter implements ActionListener
// klasa wewntrzna
{
. . .
}
}
Rozdzia 6.
291
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 referencj do obiektu zewntrznego outer. W takiej sytuacji metoda actionPerformed jest rwnowana 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();
}
Referencja klasy zewntrznej jest ustawiana w konstruktorze. Kompilator modyfikuje konstruktory wszystkich klas wewntrznych, dodajc parametr referencji do obiektu klasy
zewntrznej. Poniewa klasa TimePrinter nie definiuje adnego konstruktora, kompilator
syntetyzuje konstruktor domylny, w wyniku czego powstaje poniszy kod:
292
Java. Podstawy
public TimePrinter(TalkingClock clock)
{
outer = clock;
}
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);
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.
Klas TimePrinter mona byo zadeklarowa jako prywatn. W takim przypadku
obiekty tej klasy mogyby by tworzone wycznie przez metody klasy TalkingClock.
Tylko klasy wewntrzne mog by prywatne. Zwyke klasy zawsze maj zasig pakietowy lub publiczny.
Listing 6.6. innerClass/InnerClassTest.java
package innerClass;
import
import
import
import
import
java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
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();
// Niech program dziaa, dopki uytkownik nie wcinie przycisku OK.
JOptionPane.showMessageDialog(null, "Zamkn program?");
System.exit(0);
}
}
/**
* Zegar drukujcy informacje o czasie w rwnych odstpach czasu.
Rozdzia 6.
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();
}
public class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
}
okrela referencj do klasy zewntrznej. Na przykad metoda actionPerformed klasy wewntrznej TimePrinter moe wyglda nastpujco:
public void actionPerformed(ActionEvent event)
{
. . .
if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}
294
Java. Podstawy
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 bezporednio 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();
Rozdzia 6.
295
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
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 wasnorcznie? 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
{
. . .
public void start()
{
ActionListener listener = new TimePrinter(this);
Timer t = new Timer(interval, listener);
t.start();
}
}
class TimePrinter implements ActionListener
{
private TalkingClock outer;
. . .
public TimePrinter(TalkingClock clock)
{
outer = clock;
}
}
// bd
296
Java. Podstawy
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 wirtualna 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;
public TalkingClock(int, boolean);
static boolean access$0(TalkingClock);
public void start();
}
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).
Metod t wywouj metody klasy wewntrznej. Instrukcja:
if (beep)
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.
Rozdzia 6.
297
Oczywicie tego konstruktora nie da si wywoa, dlatego istnieje jeszcze jeden o zasigu
pakietowym:
TalkingClock$TimePrinter(TalkingClock, TalkingClock$1)
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.
298
Java. Podstawy
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
Naley zauway, e klasa TalkingClock nie musi ju zawiera pola beep. Odwouje si do
zmiennej parametrycznej beep metody start.
Moe nie jest to zaskakujce. Wiersz:
if (beep) . . .
znajduje si w caoci w metodzie start, wic dlaczego nie powinien mie dostpu do wartoci
zmiennej beep?
Aby to sprawdzi, przeanalizujemy uwanie przepyw sterowania.
1.
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);
public void actionPerformed(java.awt.event.ActionEvent);
final boolean val$beep;
final TalkingClock this$0;
}
Zwrmy uwag na parametr boolean w konstruktorze i zmienn obiektow val$beep. W trakcie tworzenia obiektu warto beep jest przekazywana do konstruktora i zapisywana w polu
o nazwie val$beep. Kompilator wykrywa obecno zmiennych lokalnych, tworzy odpowiadajce im pola obiektowe oraz kopiuje te zmienne do konstruktora, po czym wykorzystuje je
do inicjacji utworzonych pl.
Rozdzia 6.
299
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.
Wiemy ju, e zmienne finalne mog by uywane w charakterze staych:
public static final double SPEED_LIMIT = 90;
Sowo kluczowe final mona stosowa do zmiennych lokalnych, obiektowych i statycznych. Jego znaczenie jest zawsze takie samo: do danej zmiennej mona przypisa warto tylko jeden raz. Nie mona jej pniej zmieni.
Nie ma jednak koniecznoci inicjowania zmiennej finalnej po jej zdefiniowaniu. Na przykad finalna zmienna parametryczna beep jest inicjowana po wywoaniu metody start
(jeli metoda jest wywoywana kilka razy, kade wywoanie tworzy wasny parametr beep).
Zmienna obiektowa val$beep dostpna w klasie wewntrznej TalkingClock$1Time
Printer jest ustawiana jeden raz w konstruktorze klasy wewntrznej. Zmienna finalna,
ktra nie zostaa zainicjowana, nazywa si pust zmienn finaln (ang. blank final
variable).
Zmienna counter nie moe by zadeklarowana jako finalna, poniewa musi by aktualizowana. 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.
Kiedy klasy wewntrzne zostay wynalezione, prototyp kompilatora automatycznie wykonywa transformacj wszystkich zmiennych lokalnych, ktre byy modyfikowane w klasie
wewntrznej. Jednak niektrzy programici nie byli zadowoleni z tego, e kompilator za
ich plecami tworzy obiekty na stercie. Dlatego zdecydowano si na wprowadzenie ograniczenia ze sowem kluczowym final. Niewykluczone, e w przyszych wersjach Javy decyzja ta zostanie zmieniona.
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 { }.
Oglna skadnia jest nastpujca:
new NadTyp(parametry konstrukcyjne)
{
Metody i dane klasy wewntrznej.
}
NadTyp w tym przypadku moe by interfejsem, np. ActionListener. Klasa wewntrzna implementuje 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.
301
Aby zauway rnic pomidzy tworzeniem obiektu klasy a tworzeniem obiektu anonimowej 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.
java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
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);
// Niech program dziaa, dopki uytkownik nie wcinie przycisku OK.
JOptionPane.showMessageDialog(null, "Zamkn program?");
System.exit(0);
302
Java. Podstawy
}
}
/**
* 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. double 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 anonimow. 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 podklasy 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;
Rozdzia 6.
303
W danych dziennika albo debugera czsto zamieszcza si nazw biecej klasy, np.:
System.err.println("Co si stao w " + getClass());
Jednak w przypadku metody statycznej to si nie uda. Wywoanie getClass() jest tosame 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;
public Pair(double f, double s)
{
first = f;
second = s;
}
public double getFirst() { return first; }
public double getSecond() { return second; }
}
304
Java. Podstawy
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 potencjalnego 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 statycznej 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 niejawnego 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.
305
Listing 6.8 przedstawia kompletny kod rdowy klasy ArrayAlg i klasy zagniedonej Pair.
Listing 6.8. staticInnerClass/StaticInnerClassTest.java
package staticInnerClass;
/**
* 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);
}
}
Rozdzia 6.
307
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 parametrw 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:
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.:
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.
W programie na listingu 6.9 obiekty proxy s wykorzystywane do ledzenia wyszukiwania
binarnego. Zapeniamy tablic obiektami proxy liczb cakowitych od 1 do 1000. Nastpnie za
pomoc metody binarySearch klasy Arrays w tablicy tej wyszukujemy jedn losow liczb.
Na koniec wywietlamy pasujcy element.
Object[] elements = new Object[1000];
// Zapenienie tablicy obiektami proxy liczb cakowitych od 1 do 1000.
for (int i = 0; i < elements.length; i++)
{
Integer value = i + 1;
elements[i] = Proxy.newInstance(. . .);
// obiekt proxy wartoci
}
// Tworzenie losowej liczby cakowitej.
Integer key = new Random().nextInt(elements.length) + 1;
// Wyszukiwanie losowej liczby cakowitej.
int result = Arrays.binarySearch(elements, key);
// Wydruk pasujcej wartoci, jeli istnieje.
if (result >= 0) System.out.println(elements[result]);
Klasa Integer implementuje interfejs Comparable. Obiekty proxy nale do klasy, ktra jest
definiowana w czasie dziaania programu (ma nazw typu $Proxy0). Ta klasa rwnie implementuje interfejs Comparable, jednak jej metoda compareTo wywouje metod invoke handlera obiektu proxy.
Jak widzielimy wczeniej w tym rozdziale, klasa Integer implementuje interfejs
Comparable<Integer>. Jednak w czasie dziaania programu wszystkie typy parametryzowane s czyszczone i tworzony jest obiekt proxy przy uyciu obiektu class surowej klasy Comparable.
Rozdzia 6.
309
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.
Na kocu programu znajduje si nastpujcy wiersz kodu:
System.out.println(elements[result]);
/**
* 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;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
{
// Drukowanie argumentu niejawnego.
System.out.print(target);
// Drukowanie nazwy metody.
System.out.print("." + m.getName() + "(");
// Drukowanie argumentw jawnych.
if (args != null)
{
for (int i = 0; i < args.length; i++)
{
System.out.print(args[i]);
if (i < args.length - 1) System.out.print(", ");
}
}
System.out.println(")");
// Wywoanie rzeczywistej metody.
return m.invoke(target, args);
}
}
Metoda println wywouje metod toString na rzecz obiektu proxy. Wywoanie to rwnie
jest przekierowywane do obiektu obsugujcego wywoanie.
Wynik dziaania programu jest nastpujcy:
500.compareTo(288)
250.compareTo(288)
375.compareTo(288)
312.compareTo(288)
281.compareTo(288)
296.compareTo(288)
288.compareTo(288)
288.toString()
Mona zaobserwowa, jak algorytm wyszukiwania binarnego namierza liczb, dzielc przeszukiwany 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.
311
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 przypadku 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
312
Java. Podstawy
Grafika
W tym rozdziale:
Tworzenie ramek
Pozycjonowanie ramek
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 rozdziale 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 klawiaturze lub kliknicie przyciskiem myszy, oraz dodawanie do aplikacji takich elementw
interfejsu jak menu i przyciski. Po zapoznaniu si z tymi trzema rozdziaami bdziesz dysponowa wiedz, ktra jest niezbdna do pisania aplikacji graficznych. Bardziej zaawansowane techniki zwizane z programowaniem grafiki zostay opisane w drugim tomie.
Osoby zainteresowane wycznie programowaniem po stronie serwera, ktre nie maj
w planach pisania GUI, mog bezpiecznie pomin te rozdziay.
314
Java. Podstawy
Rozdzia 7.
Grafika
315
Biblioteka Swing nie zastpia AWT, tylko zostaa zbudowana w oparciu o architektur 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 interfejsu 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 pniej przez specjalistw od marketingu przechrzczono na Java look and feel. Jednak wikszo 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 niektrych 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 aplikacje 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
Styl Napkin (http://napkinlaf.sourceforge.net) nadaje elementom interfejsu uytkownika wygld przypominajcy odrczne rysowanie. Wysyajc do klienta utworzony
w nim prototyp, dajemy wyrany sygna, e nie jest to jeszcze ukoczony produkt.
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 zasobw. Narzdzia te umoliwiaj zaprojektowanie caej wizualnej strony aplikacji oraz automatycznie 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.
Rozdzia 7.
Grafika
319
Rysunek 7.5.
Najprostsza
widoczna ramka
public SimpleFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
320
Java. Podstawy
Po drugie, okrelamy, co ma si sta, kiedy uytkownik zamknie ramk aplikacji. W tym przypadku chcemy, aby program zosta zamknity. Odpowiedzialna jest za to ponisza instrukcja:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Po rozplanowaniu instrukcji inicjujcych metoda main koczy dziaanie. Zauwamy, e zakoczenie metody main nie oznacza zamknicia programu, a jedynie gwnego wtku. Wtek
dystrybucji zdarze podtrzymuje dziaanie programu a do jego zakoczenia poprzez zamknicie ramki lub wywoanie metody System.exit.
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 przypadku jej zadanie sprowadza si jedynie do wypenienia domylnym kolorem ta.
Od Java SE 1.4 istnieje moliwo wyczenia wszelkich dodatkw za pomoc
wywoania metody frame.setUndecorated(true).
Rozdzia 7.
Grafika
321
systemowym itd.
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 pooenie (ale nie rozmiar). Zazwyczaj nowe okno jest wywietlane z nieznacznym przesuniciem wzgldem poprzedniego.
W przypadku ramki wsprzdne metod setLocation i setBounds s wzgldne do
caego ekranu. W rozdziale 9. dowiemy si, e wsprzdne komponentw znajdujcych si w kontenerze odnosz si do tego kontenera.
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 zamienienia 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 wczeniej ustawiona.
Nie wiemy (i nie obchodzi nas to), jak klasa Frame implementuje t wasno. Prawdopodobnie wykorzystuje swj odpowiednik ramki do przechowywania tytuu. Moliwe, e ma
nastpujce pole:
private String title;
Jeli klasa zawiera pasujce pole, nie wiemy (lub nie obchodzi nas to), jak metody dostpowe 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 zaczynaj si od przedrostka is. Na przykad dwie przedstawione poniej metody definiuj wasno
locationByPlatform:
public boolean isLocationByPlatform()
public void setLocationByPlatform(boolean b)
Rozmiar ramki ustawiamy na poow ekranu i pozwalamy systemowi na ustalenie pooenia ramki:
324
Java. Podstawy
setSize(screenWidth / 2, screenHeight / 2);
setLocationByPlatform(true);
Rozdzia 7.
Grafika
325
Jeli ramka zawiera tylko standardowe komponenty, jak przyciski i pola tekstowe,
jej rozmiar mona ustawi za pomoc metody pack. Rozmiar zostanie ustawiony
na najmniejszy, w ktrym zmieszcz si wszystkie komponenty. Czsto rozmiar
gwnej ramki jest ustawiany na najwiksz warto. Od Java SE 1.4 ramk mona
zmaksymalizowa za pomoc nastpujcego wywoania:
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
java.awt.Component 1.0
boolean isVisible()
void setVisible(boolean b)
326
Java. Podstawy
java.awt.Window 1.0
void toFront()
void toBack()
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)
Image getIconImage()
Pobiera lub ustawia wasno iconImage, ktra okrela ikon ramki. System moe
wywietli ikon jako dodatek w ramce lub w innym miejscu.
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.
Rozdzia 7.
Grafika
327
java.awt.Toolkit 1.0
Dimension getScreenSize()
ImageIcon(String filename)
Image getImage()
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 wywoywanie metody JFrame.add na rzecz warstwy z treci.
Dziki temu od Java SE 5.0 mona stosowa krtki zapis:
frame.add(c);
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. Rysowanie w Javie zawsze odbywa si za porednictwem obiektu Graphics. Udostpnia on metody
rysujce wzory, obrazy i tekst.
Rysunek 7.8.
Wewntrzna
struktura
ramki JFrame
Rozdzia 7.
Grafika
329
{
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.
Jakiego rodzaju czynnoci uruchamiaj t automatyczn reakcj? Rysowanie jest konieczne,
na przykad kiedy uytkownik zwikszy rozmiar okna albo je zminimalizuje, a nastpnie
zmaksymalizuje. Jeli zostanie otwarte nowe okno, ktre czciowo przykryje stare, to po
zamkniciu tego nowego okna przykryta cz starego zostanie zniszczona i trzeba j bdzie
ponownie narysowa (system graficzny nie zapisuje pikseli, ktre znajduj si pod spodem).
Oczywicie przy pierwszym uruchomieniu okna musi ono przetworzy kod okrelajcy sposb i miejsce rysowania pocztkowych elementw.
Aby wymusi ponowne rysowanie ekranu, naley uy metody repaint. Wywoa
ona metody paintComponent wszystkich komponentw, ktre maj prawidowo
skonfigurowany obiekt Graphics.
Z przedstawionego powyej fragmentu kodu wnioskujemy, e metoda paintComponent przyjmuje tylko jeden parametr typu Graphics. Jednostk miary obiektw Graphics wywietlanych na ekranie s piksele. Para wsprzdnych (0, 0) okrela lewy grny rg komponentu,
na ktrego powierzchni odbywa si rysowanie.
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 krawdzi grnej. Mimo e nie potrafimy jeszcze mierzy dugoci acuchw, zaczniemy rysowanie 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;
public void paintComponent(Graphics g)
{
g.drawString("To nie jest program Witaj, wiecie", MESSAGE_X, MESSAGE_Y);
330
Java. Podstawy
}
. . .
Gdy umiecimy w ramce jakie komponenty i bdziemy chcieli uy ich preferowanych rozmiarw, to zamiast setSize wywoamy metod pack:
class NotHelloWorldFrame extends JFrame
{
public NotHelloWorldFrame()
{
add(new NotHelloWorldComponent());
pack();
}
}
procedury rysujce
Rozdzia 7.
Grafika
Container getContentPane()
331
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()
Dimension getPreferredSize()
void paintComponent(Graphics g)
void pack()
7.5. Figury 2D
Od Java 1.0 klasa Graphics udostpnia metody rysujce linie, prostokty, elipsy itd. Ich moliwoci 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 graficznych. W tym rozdziale opisujemy tylko podstawy tej biblioteki wicej bardziej zaawansowanych 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
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);
// 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 zmiennoprzecinkowej:
float f = 1.2F;
// OK
334
Java. Podstawy
Teraz przyjrzyjmy si poniszej instrukcji:
Rectangle2D r = . . .
float f = r.getWidth();
// bd
// OK
Rozdzia 7.
Grafika
335
Oznacza to, e uycie klas wewntrznych jest konieczne tylko przy tworzeniu obiektw figur.
Parametry konstruktora okrelaj lewy grny rg, szeroko i wysoko prostokta.
Klasa Rectangle2D.Float zawiera jedn metod, ktrej nie dziedziczy po klasie
Rectangle2D. Jest to metoda setRect(float x, float y, float h, float w).
Metody tej nie mona uy, jeli referencja do obiektu typu Rectangle2D.Float jest
przechowywana w zmiennej typu Rectangle2D. Nie jest to jednak dua strata klasa
Rectangle2D zawiera metod setRect z parametrami typu double.
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);
Klasy Rectangle2D i Ellipse2D dziedzicz po wsplnej nadklasie RectangularShape. Wprawdzie elipsa nie jest prostoktna, ale mona na niej opisa prostokt (zobacz rysunek 7.10).
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:
wysoko i szeroko.
utworzy elips wpisan w prostokt, ktrego lewy grny rg znajduje si w punkcie o wsprzdnych (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 prostokt 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
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;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// Rysowanie prostokta.
double
double
double
double
leftX = 100;
topY = 100;
width = 200;
height = 150;
Rozdzia 7.
Grafika
double getCenterX()
double getCenterY()
double getMinX()
double getMinY()
double getMaxX()
double getMaxY()
double getWidth()
double getHeight()
double getX()
double getY()
339
340
Java. Podstawy
java.awt.geom.Ellipse2D.Double 1.2
Point2D.Double(double x, double y)
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 narysujemy prostokt new Rectangle2D.Double(0, 0, 10, 20), to rysunek bdzie obejmowa 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 nastpujcych 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 literami. 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)
// niebieskozielony
Poza jednolitymi kolorami mona take stosowa bardziej skomplikowane ustawienia, jak rne odcienie czy obrazy. Wicej informacji na ten temat znajduje si
w drugim tomie w rozdziale o zaawansowanych technikach AWT. Jeli zamiast obiektu
typu Graphics2D zostanie uyty obiekt Graphics, kolory naley ustawia za pomoc metody
setColor.
342
Java. Podstawy
Zastosowanie
desktop
Kolor ta pulpitu
activeCaption
activeCaptionText
activeCaptionBorder
inactiveCaption
inactiveCaptionText
inactiveCaptionBorder
window
To okna
windowBorder
windowText
menu
To menu
menuText
text
Kolor ta tekstu
textText
Kolor tekstu
textInactiveText
textHighlight
textHighlightText
control
controlText
controlLtHighlight
controlHighlight
controlShadow
controlDkShadow
scrollbar
info
infoText
java.awt.Color 1.0
Rozdzia 7.
Grafika
343
java.awt.Graphics 1.0
Color getColor()
void setColor(Color c)
Pobiera lub ustawia kolor. Wszystkie nastpne rysunki bd miay ten kolor.
Parametry:
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)
Color getBackground()
void setBackground(Color c)
Color getForeground()
void setForeground(Color c)
Nowy kolor ta
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
pomoc statycznej metody getLocalGraphicsEnvironment. Poniszy program drukuje nazwy
wszystkich czcionek znajdujcych si w systemie:
import java.awt.*;
public class ListFonts
{
public static void main(String[] args)
{
String[] fontNames = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
for (String fontName : fontNames)
System.out.println(fontName);
}
}
Rozdzia 7.
Grafika
345
Trzeci argument okrela rozmiar w punktach. Jednostka ta jest powszechnie stosowana w typografii 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 konstruktora 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);
Zastosowana zostaa zwyka czcionka o rozmiarze 1 punktu. Do okrelenia danego rozmiaru czcionki naley uy metody deriveFont:
Font f = f1.deriveFont(14.0F);
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 bezszeryfow 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);
346
Java. Podstawy
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.
Szeroko prostokta zwracanego przez metod getStringBounds okrela szeroko tekstu.
Wysoko natomiast jest rwna sumie wyduenia dolnego, leadingu i wyduenia grnego.
Prostokt ma swj pocztek na linii bazowej acucha. Grna wsprzdna y prostokta ma
warto ujemn. W zwizku z tym szeroko, wysoko i wyduenie grne acucha mona
sprawdzi nastpujco:
double stringWidth = bounds.getWidth();
double stringHeight = bounds.getHeight();
double ascent = -bounds.getY();
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
java.awt.*;
java.awt.font.*;
java.awt.geom.*;
javax.swing.*;
/**
* @version 1.33 2007-04-14
348
Java. Podstawy
* @author Cay Horstmann
*/
public class FontTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new FontFrame();
frame.setTitle("FontTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* 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;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
String message = "Witaj, wiecie!";
Font f = new Font("Serif", Font.BOLD, 36);
g2.setFont(f);
// Sprawdzenie rozmiaru tekstu.
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
// set (x, y) = lewy grny rg tekstu
double x = (getWidth() - bounds.getWidth()) / 2;
double y = (getHeight() - bounds.getHeight()) / 2;
Rozdzia 7.
Grafika
349
name
style
size
String getFontName()
String getFamily()
String getName()
Pobiera nazw logiczn (np. SansSerif), jeli czcionka zostaa utworzona z nazwy
logicznej. W przeciwnym przypadku zwraca nazw czcionki.
350
Java. Podstawy
float getAscent()
float getDescent()
float getLeading()
float getHeight()
Font getFont()
font
Czcionka
str
acuch
Rozdzia 7.
Grafika
351
java.awt.Graphics2D 1.2
FontRenderContext getFontRenderContext()
str
acuch
javax.swing.JComponent 1.2
352
Java. Podstawy
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
Rozdzia 7.
Grafika
pack();
}
}
/**
* Komponent wywietlajcy powielony obraz.
*/
class ImageComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private Image image;
public ImageComponent()
{
image = new ImageIcon("blue-ball.gif").getImage();
}
public void paintComponent(Graphics g)
{
if (image == null) return;
int imageWidth = image.getWidth(this);
int imageHeight = image.getHeight(this);
// Rysowanie obrazu w lewym grnym rogu.
g.drawImage(image, 0, 0, null);
// Powielenie obrazu w obrbie komponentu.
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);
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH,
DEFAULT_HEIGHT); }
}
java.awt.Graphics 1.0
img
Obraz do narysowania
observer
353
354
Java. Podstawy
img
Obraz do narysowania
width
Szeroko obrazu
height
Wysoko obrazu
observer
void copyArea(int x, int y, int width, int height, int dx, int dy)
width
height
dx
dy
Obsuga zdarze
W tym rozdziale:
Akcje
Obsuga zdarze ma fundamentalne znaczenie w programach z graficznym interfejsem uytkownika. Kady, kto chce tworzy interfejsy graficzne w Javie, musi opanowa obsug zdarze. Ten rozdzia opisuje model obsugi zdarze biblioteki AWT. Do opisywanych zagadnie
nale przechwytywanie zdarze w komponentach interfejsu uytkownika i urzdzeniach
wejciowych, a take akcje (ang. actions), czyli bardziej strukturalna metoda przetwarzania
zdarze.
356
Java. Podstawy
Natomiast programici czystego jzyka C zajmujcy si zdarzeniami musz pisa procedury
nieprzerwanie monitorujce kolejk zdarze w celu sprawdzenia, jakie powiadomienia przesya system operacyjny (z reguy do tego celu stosuje si ptl zawierajc bardzo rozbudowan instrukcj switch!). Technika ta jest oczywicie bardzo mao elegancka i sprawia wiele
problemw podczas pisania kodu. Jej zalet jest natomiast to, e nie ma adnych ogranicze
dotyczcych zdarze, na ktre mona reagowa, w przeciwiestwie do innych jzykw,
np. Visual Basica, ktre wkadaj bardzo duo wysiku w ukrywanie kolejki zdarze przed
programist.
W rodowisku programistycznym Javy przyjto podejcie porednie pomidzy jzykami Visual
Basic a C, jeli chodzi o oferowane moliwoci, a co za tym idzie take zoono. Poruszajc si w zakresie zdarze, ktre obsuguje biblioteka AWT, programista ma pen kontrol nad sposobem przesyania zdarze ze rde zdarze (ang. event sources), np. przyciskw lub paskw przewijania, do suchaczy zdarze (ang. event listener). Na suchacza
zdarze mona desygnowa kady obiekt w praktyce wybiera si ten obiekt, ktry
z atwoci moe wykona odpowiednie dziaania w odpowiedzi na zdarzenie. Ten delegacyjny model zdarze daje znacznie wiksze moliwoci ni jzyk Visual Basic, w ktrym
suchacz jest ustalony z gry.
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 nale 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.
Podsumujmy, co ju wiemy na temat obsugi zdarze w bibliotece AWT:
Rozdzia 8.
Obsuga zdarze
357
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.
Klasa implementujca interfejs ActionListener musi definiowa metod o nazwie action
Performed, ktra jako parametr przyjmuje obiekt typu ActionEvent:
class MyListener implements ActionListener
{
. . .
public void actionPerformed(ActionEvent event)
{
// Instrukcje wykonywane w odpowiedzi na kliknicie przycisku.
. . .
}
}
Kiedy uytkownik kliknie przycisk, obiekt typu JButton tworzy obiekt typu ActionEvent
i wywouje metod listener.actionPerformed(event), przekazujc do niej ten obiekt zdarzenia. rdo zdarze, takie jak przycisk, moe mie kilka suchaczy. W takim przypadku
kliknicie przycisku przez uytkownika powoduje wywoanie metod actionPerformed wszystkich suchaczy.
Rysunek 8.2 przedstawia relacje pomidzy rdem zdarze, suchaczem zdarze i obiektem zdarze.
358
Java. Podstawy
Rysunek 8.2.
Powiadamianie
o zdarzeniach
Rozdzia 8.
Obsuga zdarze
359
Rysunek 8.3.
Panel
z przyciskami
Nastpnie dla kadego koloru tworzymy osobny obiekt i kady z nich rejestrujemy jako suchacza 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.).
Zastosujemy drug z opisanych metod. Poniej przedstawiamy klas ColorAction wewntrz
klasy ButtonFrame:
class ButtonPanel extends JFrame
{
private JPanel buttonPanel;
. . .
private class ColorAction implements ActionListener
{
private Color backgroundColor;
. . .
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);
}
}
}
Rozdzia 8.
private static final int DEFAULT_HEIGHT = 200;
public ButtonFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Tworzenie przyciskw
JButton yellowButton = new JButton("ty");
JButton blueButton = new JButton("Niebieski");
JButton redButton = new JButton("Czerwony");
buttonPanel = new JPanel();
// Dodanie przyciskw do panelu
buttonPanel.add(yellowButton);
buttonPanel.add(blueButton);
buttonPanel.add(redButton);
// Dodanie panelu do ramki
add(buttonPanel);
// Utworzenie akcji przyciskw
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
// Powizanie akcji z przyciskami
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);
}
/**
* Suchacz akcji ustawiajcy kolor ta panelu.
*/
private class ColorAction implements ActionListener
{
private Color backgroundColor;
public ColorAction(Color c)
{
backgroundColor = c;
}
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);
}
}
}
javax.swing.JButton 1.2
JButton(String label)
JButton(Icon icon)
Obsuga zdarze
361
362
Java. Podstawy
Tworzy przycisk. acuch etykiety moe zawiera sam tekst lub (od Java SE 1.3)
kod HTML, np. "<html><b>Ok</b></html>".
java.awt.Container 1.0
Component add(Component c)
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 zmiennej parametrycznej backgroundColor (podobnie jak w przypadku wszystkich zmiennych lokalnych 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.
Anonimowe klasy wewntrzne potrafi zmyli niejednego programist. Mona jednak
przyzwyczai si do ich rozszyfrowywania, wyrabiajc sobie umiejtno pomijania wzrokiem kodu procedury:
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);
}
});
Akcja przycisku ustawia kolor ta. Dopki procedura obsugi zdarze skada si z tylko
kilku instrukcji, wydaje si, e z odczytem nie powinno by problemw, zwaszcza jeli
w sferze naszych zainteresowa nie le mechanizmy klas wewntrznych.
java.util.EventObject 1.1
Object setSource()
String getActionCommand()
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 nastpujcego 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());
}
}
366
Java. Podstawy
Zauwamy, e styl Metal jest zlokalizowany w pakiecie javax.swing. Pozostae style znajduj 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.
Poniewa w plikach wasnoci linie zaczynajce si od znaku # s ignorowane,
mona w takim pliku umieci kilka stylw i wybiera je wedle upodobania, odpowiednio zmieniajc pooenie znaku #:
#swing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel
swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel
#swing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel
Aby zmieni styl w ten sposb, konieczne jest ponowne uruchomienie programu. Programy
Swing wczytuj plik swing.properties tylko jeden raz przy uruchamianiu.
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 spowodowa 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.
Poniszy przykadowy fragment programu przedstawia sposb przeczenia na styl Motif:
String plaf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
try
{
UIManager.setLookAndFeel(plaf);
SwingUtilities.updateComponentTreeUI(panel);
}
catch(Exception e) { e.printStackTrace(); }
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 poprzedniej sekcji, akcj przycisku, polegajc na zmianie stylu, okrelilimy za pomoc metody
pomocniczej makeButton i anonimowej klasy wewntrznej.
Listing 8.2. plaf/PlafFrame.java
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.
JButton button = new JButton(name);
buttonPanel.add(button);
// Ustawienie akcji przycisku.
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();
}
}
});
}
}
String getName()
String getClassName()
Rozdzia 8.
Obsuga zdarze
369
W Javie klasa, ktra implementuje dany interfejs, musi definiowa wszystkie jego metody.
W tym przypadku oznacza to implementacj siedmiu metod. Przypomnijmy jednak, e interesuje nas tylko jedna z nich, o nazwie windowClosing.
Oczywicie nic nie stoi na przeszkodzie, aby zaimplementowa ten interfejs, wstawi wywoanie 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
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
}
Pisanie szeciu metod, ktre nic nie robi, jest tym rodzajem pracy, ktrej nikt nie lubi. Zadanie to uatwiaj klasy adaptacyjne (ang. adapter class) dostpne z kadym interfejsem nasuchujcym w bibliotece AWT, ktry ma wicej ni jedn metod. Klasy te implementuj
wszystkie metody interfejsw, ktrym odpowiadaj, ale metody te nic nie robi. Na przykad klasa WindowAdapter zawiera definicje siedmiu nicnierobicych metod. Oznacza to, e
klasa adaptacyjna automatycznie spenia wymagania techniczne stawiane przez Jav, a dotyczce 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).
Rozszerzymy klas WindowAdapter. Odziedziczymy po niej sze nicnierobicych metod,
a metod windowClosing przesonimy:
class Terminator extends WindowAdapter
{
public void windowClosing(WindowEvent e)
{
if (uytkownik potwierdza)
System.exit(0);
}
}
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.
Jeli w nazwie metody rozszerzanej klasy adaptacyjnej znajdzie si bd, kompilator go nie wykryje. Jeli na przykad w klasie rozszerzajcej WindowAdapter zostanie
zdefiniowana metoda windowIsClosing, nowa klasa bdzie zawieraa osiem metod, a metoda windowClosing nie bdzie nic robia.
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);
}
});
Powtarzamy jeszcze raz, e do skadni wewntrznych klas anonimowych trzeba si przyzwyczai. Dziki nim mona pisa tak zwizy kod, jak to tylko moliwe.
372
Java. Podstawy
java.awt.event.WindowListener 1.1
void windowOpened(WindowEvent e)
void windowClosing(WindowEvent e)
void windowClosed(WindowEvent e)
void windowIconified(WindowEvent e)
void windowDeiconified(WindowEvent e)
void windowActivated(WindowEvent e)
void WindowDeactivated(WindowEvent e)
Zwraca nowy i stary stan okna w zdarzeniu zmiany stanu okna. Zwracana liczba
cakowita jest jedn z nastpujcych wartoci:
Frame.NORMAL
Frame.ICONIFIED
Frame.MAXIMIZED_HORIZ
Frame.MAXIMIZED_VERT
Frame.MAXIMIZED_BOTH
Rozdzia 8.
Obsuga zdarze
373
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 zdarze:
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 wica je z rnymi rdami zdarze interfejs Action. Akcja to obiekt, ktry opakowuje:
Pierwsza z tych metod jest ju nam znana z interfejsu ActionListener. Naley doda, e interfejs 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, odpowiadajca 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 acuchw: Action.NAME i Action.SMALL_ICON:
action.putValue(Action.NAME, "Niebieski");
action.putValue(Action.SMALL_ICON, new ImageIcon("blue-ball.gif"));
374
Java. Podstawy
Warto
NAME
SMALL_ICON
SHORT_DESCRIPTION
LONG_DESCRIPTION
MNEMONIC_KEY
ACCELERATOR_KEY
ACTION_COMMAND_KEY
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 automatycznie 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, zwaszcza 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 czowiek napisa klas o nazwie AbstractAction, ktra implementuje wszystkie te metody z wyjtkiem actionPerformed. Klasa ta zajmuje si zapisywaniem par nazwa warto i zarzdzaniem 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.
Na koniec przypiszemy obiekty akcji do klawiszy, dziki czemu akcje te bd wykonywane,
kiedy uytkownik wpisze polecenia z klawiatury. Kojarzenie akcji z klawiszami naley zacz
od wygenerowania obiektu klasy KeyStroke. Klasa ta opakowuje opis klawisza. Do utworzenia obiektu typu KeyStroke nie uywa si konstruktora, ale statycznej metody getKey
Stroke klasy KeyStroke.
KeyStroke ctrlBKey = KeyStroke.getKeyStroke("ctrl N");
Do zrozumienia nastpnego etapu potrzebna jest znajomo pojcia aktywnoci komponentu (ang. keyboard focus). Interfejs uytkownika moe si skada z wielu przyciskw,
menu, paskw przewijania i innych komponentw. Kiedy zostanie nacinity klawisz, zdarzenie to zostaje wysane do aktywnego komponentu. Komponent ten jest z reguy (cho
nie zawsze) w jaki sposb wizualnie wyrniony. Na przykad w stylu Javy tekst na aktywnym przycisku ma cienk obwdk. Fokus (aktywno komponentu) mona przenosi na
rne komponenty za pomoc klawisza Tab. Nacinicie klawisza spacji powoduje kliknicie
aktywnego przycisku. Inne klawisze wywouj inne dziaania. Na przykad klawisze strzaek mog sterowa paskiem przewijania.
Jednak my nie chcemy wysya zdarzenia nacinicia klawisza do aktywnego komponentu.
W przeciwnym razie kady przycisk musiaby zna procedur obsugi kombinacji klawiszy
Ctrl+Y, Ctrl+B i Ctrl+R.
376
Java. Podstawy
Jest to bardzo powszechny problem. Jednak projektanci biblioteki Swing znaleli dla niego
proste rozwizanie. Kady JComponent posiada trzy mapy wejcia (ang. input maps), z ktrych kada odwzorowuje obiekty KeyStroke na zwizane z nimi akcje. Mapy te odpowiadaj trzem rnym sytuacjom (zobacz tabela 8.2).
WHEN_FOCUSED
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
WHEN_IN_FOCUSED_WINDOW
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 nastpujcy 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 elementach menu, w zwizku z czym moe si zmienia w zalenoci od kaprysu projektanta 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.
378
Java. Podstawy
public class ActionFrame extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public ActionFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
buttonPanel = new JPanel();
// 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);
// Dodanie przyciskw dla akcji
buttonPanel.add(new JButton(yellowAction));
buttonPanel.add(new JButton(blueAction));
buttonPanel.add(new JButton(redAction));
// Dodanie panelu do ramki
add(buttonPanel);
// Powizanie klawiszy Z, N i C z nazwami
InputMap imap = buttonPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_
FOCUSED_COMPONENT);
imap.put(KeyStroke.getKeyStroke("ctrl Z"), "panel.yellow");
imap.put(KeyStroke.getKeyStroke("ctrl N"), "panel.blue");
imap.put(KeyStroke.getKeyStroke("ctrl C"), "panel.red");
// Powizanie nazw z akcjami
ActionMap amap = buttonPanel.getActionMap();
amap.put("panel.yellow", yellowAction);
amap.put("panel.blue", blueAction);
amap.put("panel.red", redAction);
}
public class ColorAction extends AbstractAction
{
/**
* Tworzy akcj zmiany koloru.
* @param name nazwa, ktra pojawi si na przycisku
* @param icon ikona, ktra pojawi si na przycisku
* @param c kolor ta
*/
public ColorAction(String name, Icon icon, Color c)
{
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);
putValue(Action.SHORT_DESCRIPTION, "Ustaw kolor panelu na " +
name.toLowerCase());
Rozdzia 8.
Obsuga zdarze
putValue("color", c);
}
public void actionPerformed(ActionEvent event)
{
Color c = (Color) getValue("color");
buttonPanel.setBackground(c);
}
}
}
javax.swing.Action 1.2
boolean isEnabled()
void setEnabled(boolean b)
key
value
Zwraca map wic klucze mapy akcji (ktre mog by dowolnymi obiektami)
z obiektami klasy Action.
Pobiera map wejcia, ktra odwzorowuje klawisze w postaci kluczy mapy akcji.
379
380
Java. Podstawy
Parametry:
flag
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 wymienionych 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.
Niektrzy projektanci interfejsw tworz kombinacje klawiszy poczone z klikniciami
myszk, np. Ctrl+Shift+kliknicie. Naszym zdaniem jest to postpowanie niegodne naladowania. Osoby, ktre nie zgadzaj si z nasz opini, moe przekona fakt, e sprawdzanie przyciskw myszy i klawiszy specjalnych jest niezwykle zagmatwanym zadaniem
niebawem si o tym przekonamy.
Aby sprawdzi, ktre modyfikatory zostay ustawione, naley uy maski bitowej. W oryginalnym 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
Metoda getModifiersEx zwraca dokadne informacje o przyciskach myszy i klawiszach specjalnych uytych w zdarzeniu myszy.
Pamitajmy, e maska BUTTON3_DOWN_MASK w systemie Windows sprawdza prawy (nie gwny)
przycisk myszy. Na przykad poniszy fragment programu sprawdza, czy prawy przycisk
myszy jest wcinity:
if ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)
. . . // procedury obsugi zdarzenia kliknicia prawym przyciskiem myszy
Kiedy kursor myszy przesuwa si nad oknem, odbiera ono stay strumie zdarze ruchu
myszy. Zauwamy, e s osobne interfejsy MouseListener i MouseMotionListener. Wyrniono 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 reprezentowane przez nie kursory w systemie Windows.
Poniej znajduje si kod rdowy metody mouseMoved z klasy MouseMotionListener zdefiniowanej w naszym przykadowym programie:
382
Java. Podstawy
Staa
Ikona
Staa
DEFAULT_CURSOR
NE_RESIZE_CURSOR
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
Mona zdefiniowa wasne typy kursorw. Suy do tego metoda createCustomCursor z klasy Toolkit:
Toolkit tk = Toolkit.getDefaultToolkit();
Image img = tk.getImage("dynamite.gif");
Cursor dynamiteCursor = tk.createCustomCursor(img, new Point(10, 10), "dynamite
stick");
Pierwszy argument tej metody okrela plik graficzny przedstawiajcy kursor. Drugi wyznacza przesunicie punktu aktywnego kursora. Trzeci jest acuchem opisujcym kursor.
acuch ten moe suy zwikszeniu dostpnoci. Na przykad program czytajcy z ekranu
uywany przez osob niedowidzc moe przeczyta opis takiego kursora.
Jeli w czasie przesuwania myszy uytkownik kliknie jej przycisk, generowane s wywoania metody mouseDragged zamiast mouseMoved. Nasz przykadowy program zezwala na przeciganie kwadratw pod kursorem. Efekt ten uzyskalimy, aktualizujc pooenie przeciganego kwadratu, tak aby jego rodek znajdowa si w tym samym miejscu co punkt
centralny myszki. Nastpnie ponownie rysujemy obszar roboczy, aby ukaza nowe pooenie 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.
Na zakoczenie wyjanimy sposb nasuchiwania zdarze generowanych przez mysz. Kliknicia przyciskiem myszy s raportowane przez metod mouseClicked nalec do interfejsu
MouseListener. Poniewa wiele aplikacji korzysta wycznie z klikni myszk i wystpuj
one bardzo czsto, zdarzenia ruchu myszy i przecigania zostay zdefiniowane w osobnym
interfejsie o nazwie MouseMotionListener.
W naszym programie interesuj nas oba rodzaje zdarze generowanych przez mysz. Zdefiniowalimy 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.
Listing 8.4. mouse/MouseFrame.java
package mouse;
import javax.swing.*;
/**
* Ramka zawierajca okienko do testowania myszy
*/
public class MouseFrame extends JFrame
{
public MouseFrame()
{
add(new MouseComponent());
pack();
}
}
java.awt.*;
java.awt.event.*;
java.awt.geom.*;
java.util.*;
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());
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// Rysowanie wszystkich kwadratw
for (Rectangle2D r : squares)
g2.draw(r);
}
/**
* 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();
current = new Rectangle2D.Double(x - SIDELENGTH / 2, y - SIDELENGTH /
2, SIDELENGTH,
SIDELENGTH);
squares.add(current);
repaint();
}
Rozdzia 8.
Obsuga zdarze
/**
* 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());
}
public void mouseClicked(MouseEvent event)
{
// Usunicie kwadratu w wyniku jego dwukrotnego kliknicia
current = find(event.getPoint());
if (current != null && event.getClickCount() >= 2) remove(current);
}
}
private class MouseMotionHandler implements MouseMotionListener
{
public void mouseMoved(MouseEvent event)
{
// Ustawienie kursora na krzyyk, jeli znajduje si wewntrz
// kwadratu
if (find(event.getPoint()) == null) setCursor(Cursor.getDefaultCursor());
else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
public void mouseDragged(MouseEvent event)
{
if (current != null)
{
int x = event.getX();
int y = event.getY();
// Przecignicie aktualnego kwadratu w celu wyrodkowania go w punkcie (x, y)
current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH,
SIDELENGTH);
repaint();
}
}
}
}
385
386
Java. Podstawy
java.awt.event.MouseEvent 1.1
int getX()
int getY()
Point getPoint()
int getClickCount()
image
hotSpot
name
java.awt.Component 1.0
Rozdzia 8.
Obsuga zdarze
387
388
Java. Podstawy
Niektre komponenty Swing generuj obiekty zdarzeniowe jeszcze innych typw zdarze.
Rozszerzaj one bezporednio klas EventObject, a nie AWTEvent.
Obiekty zdarzeniowe zawieraj informacje o zdarzeniach przesyanych przez rdo zdarze
do swoich suchaczy. W razie potrzeby mona przeanalizowa obiekty zdarzeniowe, ktre
zostay przekazane do obiektw nasuchujcych, co zrobilimy w przykadzie z przyciskiem za pomoc metod getSource i getActionCommand.
Niektre klasy zdarzeniowe AWT s dla programisty Javy bezuyteczne. Na przykad biblioteka 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 specjalnych typw zdarze.
Rozdzia 8.
Obsuga zdarze
389
ActionListener
AdjustmentListener
FocusListener
ItemListener
KeyListener
MouseListener
MouseMotionListener
MouseWheelListener
WindowListener
WindowFocusListener
WindowStateListener
Tabela 8.4 przedstawia najwaniejsze interfejsy nasuchowe, zdarzenia i rda zdarze biblioteki AWT.
Tabela 8.4. Obsuga zdarze
Interfejs
Metody
Parametry/
metody dostpu
Zdarzenia
generowane przez
ActionListener
actionPerformed
ActionEvent
AbstractButton
JComboBox
JTextField
Timer
getActionCommand
getModifiers
AdjustmentListener
adjustmentValueChanged
AdjustmentEvent
JScrollbar
getAdjustable
getAdjustmentType
getValue
ItemListener
itemStateChanged
ItemEvent
getItem
AbstractButton
JComboBox
getItemSelectable
getStateChange
FocusListener
focusGained
focusLost
FocusEvent
KeyListener
keyPressed
keyReleased
keyTyped
KeyEvent
isTemporary
getKeyChar
getKeyCode
getKeyModifiersText
getKeyText
isActionText
Component
Component
390
Java. Podstawy
Zdarzenia
generowane przez
mousePressed
mouseReleased
mouseEntered
mouseExited
mouseClicked
MouseEvent
Component
mouseDragged
MouseEvent
Component
MouseWheelEvent
Component
Interfejs
Metody
MouseListener
getClickCount
getX
getY
getPoint
translatePoint
MouseMotionListener
mouseMoved
MouseWheelListener
MouseWheelMoved
getWheelRotation
getScrollAmount
WindowListener
WindowFocusListener
WindowStateListener
windowClosing
windowOpened
windowIconified
windowDeiconified
windowClosed
windowActivated
windowDeactivated
WindowEvent
windowGainedFocus
windowLostFocus
WindowEvent
windowStateChanged
WindowEvent
Window
getWindow
Window
getOppositeWindow
Window
getOldState
getNewState
Komponenty Swing
interfejsu uytkownika
W tym rozdziale:
Wprowadzanie tekstu
Komponenty wyboru
Menu
Okna dialogowe
392
Java. Podstawy
Rozdzia 9.
393
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.
Formalny opis wielu wzorcw programistycznych znajduje si w nowatorskiej ksice powiconej tej tematyce pod tytuem Wzorce projektowe (WNT, Warszawa 2005), ktrej
autorem jest Erich Gamma i wsppracownicy (tytu oryginau Design patterns Elements
of Reusable Object-Oriented Software). Gorco polecamy take lektur doskonaej ksiki
pod tytuem A System of Patterns (John Wiley & Sons, 1996), ktrej autorem jest Frank
Buschmann i wsppracownicy. Naszym zdaniem ta pozycja jest mniej nowatorska od poprzedniej i bardziej przystpna.
394
Java. Podstawy
Tre np. stan przycisku (wcinity lub nie) lub tekst w polu tekstowym.
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 przycisku na ekranie, przycisk ten zostanie wcinity.
Oczywicie uywajc przycisku w programie, nikt nie rozwaa dogbnie jego wewntrznych mechanizmw i cech. To jest przecie zadanie programisty, ktry ten przycisk implementowa. 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.
W zwizku z tym projektanci biblioteki Swing postanowili skorzysta z dobrze znanego
wzorca o nazwie Model-View-Controller (MVC). Wzorzec ten, podobnie jak wiele innych
wzorcw projektowych, odwouje si do jednej z zasad projektowania zorientowanego
obiektowo, ktr opisywalimy w rozdziale 5., a ktra brzmi: nie obciaj jednego obiektu
zbyt du liczb dziaa. Nie twrz jednej klasy, ktra robi wszystko. Styl jednego komponentu zwi z jednym obiektem, a tre przechowuj w innym obiekcie. Wzorzec projektowy MVC podpowiada, jak to zrobi. Naley napisa trzy osobne klasy:
Wzorzec precyzyjnie okrela interakcje pomidzy tymi trzema obiektami. Model przechowuje tre i nie posiada interfejsu uytkownika. W przypadku przycisku nie ma tej treci
duo. Stanowi j tylko niewielki zestaw znacznikw okrelajcych, czy przycisk jest wcinity, 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 jednak 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 tekstowy 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.
395
Jedn z zalet wzorca MVC jest to, e model mona przedstawia na rne sposoby, za kadym 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 porednictwem kontrolera jednego z widokw, oba widoki s informowane o tej zmianie. W momencie 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
396
Java. Podstawy
Rysunek 9.4.
Relacje pomidzy
modelem,
widokiem
i kontrolerem
Rozdzia 9.
397
Warto
actionCommand
mnemonic
armed
enabled
pressed
true, jeli przycisk zosta nacinity, a przycisk myszy nie zosta jeszcze zwolniony
rollover
selected
Kady obiekt typu JButton przechowuje obiekt modelu przycisku, ktry mona z niego
wydoby.
JButton button = new JButton("Niebieski");
ButtonModel model = button.getModel();
398
Java. Podstawy
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 przecznikach 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.
Po przeczytaniu tego wprowadzenia do mechanizmw dziaania przyciskw JButton moe
nasun si jedno pytanie: czym w rzeczywistoci jest JButton? Jest to po prostu klasa
osonowa dziedziczca po klasie JComponent, ktra przechowuje obiekt DefaultButtonModel,
dane widoku (takie jak etykieta i ikona przycisku) oraz obiekt BasicButtonUI odpowiedzialny
za widok przycisku.
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.
399
Rysunek 9.6.
Panel
z szecioma
przyciskami
zarzdzanymi
przez zarzdc
rozkadu
Rysunek 9.7.
Zmiana rozmiaru
panelu powoduje
automatyczne
przegrupowanie
przyciskw
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 komponent i dane dotyczce jego umiejscowienia do zarzdcy rozkadu.
java.awt.Container 1.0
void setLayout(LayoutManager m)
Component add(Component c)
400
Java. Podstawy
Parametry:
FlowLayout()
FlowLayout(int align)
Parametry:
align
hgap
vgap
Rozdzia 9.
401
Rysunek 9.9.
Rozkad brzegowy
// nie
402
Java. Podstawy
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);
BorderLayout()
hgap
vgap
Rozdzia 9.
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 popularno w publikacjach na temat Javy. W tym programie po dodaniu komponentu do ramki
nastpuje wywoanie metody pack. Metoda ta oblicza wysoko i szeroko ramki, wykorzystujc preferowane rozmiary wszystkich komponentw.
Listing 9.1. calculator/CalculatorPanel.java
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
display = new JButton("0");
display.setEnabled(false);
add(display, BorderLayout.NORTH);
ActionListener insert = new InsertAction();
ActionListener command = new CommandAction();
404
Java. Podstawy
// Wstawienie przyciskw na siatk 44
panel = new JPanel();
panel.setLayout(new GridLayout(4, 4));
addButton("7",
addButton("8",
addButton("9",
addButton("/",
insert);
insert);
insert);
command);
addButton("4",
addButton("5",
addButton("6",
addButton("*",
insert);
insert);
insert);
command);
addButton("1",
addButton("2",
addButton("3",
addButton("-",
insert);
insert);
insert);
command);
addButton("0",
addButton(".",
addButton("=",
addButton("+",
insert);
insert);
command);
command);
add(panel, BorderLayout.CENTER);
/**
* Dodaje przycisk do panelu centralnego.
* @param label etykieta przycisku
* @param listener suchacz przyciskw
*/
private void addButton(String label, ActionListener listener)
{
JButton button = new JButton(label);
button.addActionListener(listener);
panel.add(button);
}
/**
* Ta akcja wstawia acuch akcji przycisku na kocu tekstu do wywietlenia.
*/
private class InsertAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String input = event.getActionCommand();
if (start)
{
display.setText("");
start = false;
}
display.setText(display.getText() + input);
}
}
Rozdzia 9.
405
/**
* Ta akcja wykonuje polecenia okrelone przez akcj przycisku.
*/
private class CommandAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String command = event.getActionCommand();
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 wykorzystywane s niewielkie siatki (zazwyczaj skadajce si z jednego wiersza lub jednej
kolumny), za pomoc ktrych ustawia si niektre obszary okna. Na przykad panel z rozkadem siatkowym mona wykorzysta do utworzenia szeregu przyciskw o takich samych
rozmiarach.
java.awt.GridLayout 1.0
406
Java. Podstawy
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
columns
hgap
vgap
String getText()
boolean isEditable()
void setEditable(boolean b)
Pobiera lub ustawia wasno editable, ktra okrela, czy uytkownik moe
edytowa zawarto komponentu tekstowego.
Rozdzia 9.
407
Powyszy fragment programu dodaje pole tekstowe i inicjuje je, wstawiajc do niego acuch
acuch testowy. Drugi parametr tego konstruktora ustawia szeroko pola. W tym przypadku 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 dostosowa jego rozmiar. Szeroko kolumny ustawiona w konstruktorze JTextField nie stanowi
grnego limitu znakw, ktre moe wprowadzi uytkownik. Moliwe jest wpisanie duszego acucha, ktry po przekroczeniu szerokoci pola bdzie mona przewija. Uytkownicy 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.
Po zmianie rozmiaru pola tekstowego za pomoc metody setColumns naley wywoa metod revalidate zawierajcego je kontenera.
textField.setColumns(10);
panel.revalidate();
Metoda revalidate ponownie ustala rozmiar i rozkad wszystkich komponentw znajdujcych si w kontenerze. Po uyciu metody revalidate zarzdca rozkadu zmienia
rozmiar kontenera, dziki czemu moe by widoczne pole tekstowe o zmienionym
rozmiarze.
Metoda revalidate naley do klasy JComponent. Nie zmienia ona od razu rozmiaru
komponentu, ale zaznacza go jako kandydata do takiej zmiany. Podejcie to pozwala
unikn powtarzania oblicze, w przypadku gdy konieczna jest zmiana rozmiaru wielu
komponentw. Aby jednak obliczy ponownie rozmiar wszystkich komponentw w ramce
JFrame, naley wywoa metod validate klasa JFrame nie dziedziczy po klasie
JComponent.
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!");
408
Java. Podstawy
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.
int getColumns()
void revalidate()
void setFont(Font f)
void validate()
Font getFont()
Rozdzia 9.
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.
W przyciskach, etykietach i elementach menu poza zwykym tekstem mona uywa
jzyka HTML. Nie zalecamy jednak tego przy przyciskach, poniewa zakcony
zostaje oryginalny styl. Natomiast w etykietach HTML pozwala uzyska ciekawe efekty.
Jedyne, co trzeba zrobi, to otoczy acuch etykiety znacznikami <html></html>:
label = new JLabel("<html><b>Wymagany</b> tekst:</html>");
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)
Tworzy etykiet.
Parametry:
text
Tekst etykiety
icon
Ikona etykiety
align
String getText()
Icon getIcon()
410
Java. Podstawy
Ustawia znak echa dla pola hasa. Jest to warto doradcza okrelony styl
moe wymusi stosowanie wasnego znaku. Warto 0 przywraca domylny znak.
char[] getPassword()
Zwraca tekst zawarty w polu hasa. Aby zwikszy bezpieczestwo, naley nadpisa
zawarto zwrconej tablicy, kiedy nie jest ju potrzebna (haso nie jest zwracane
jako acuch, poniewa acuchy pozostaj w maszynie wirtualnej, a zostan
usunite przez system zbierania nieuytkw).
// 8 wierszy po 40 kolumn.
Parametr columns ma takie samo przeznaczenie jak poprzednio. Nadal trzeba pamita o dodaniu kilku dodatkowych kolumn. Podobnie jak wczeniej, liczba wierszy i kolumn nie ogranicza 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.
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);
Zawijanie wierszy jest wycznie efektem wizualnym. Nie powoduje ono wstawiania znakw \n.
412
Java. Podstawy
public class TextComponentFrame extends JFrame
{
public static final int TEXTAREA_ROWS = 8;
public static final int TEXTAREA_COLUMNS = 20;
public TextComponentFrame()
{
final JTextField textField = new JTextField();
final JPasswordField passwordField = new JPasswordField();
JPanel northPanel = new JPanel();
northPanel.setLayout(new GridLayout(2, 2));
northPanel.add(new JLabel("Nazwa uytkownika: ", SwingConstants.RIGHT));
northPanel.add(textField);
northPanel.add(new JLabel("Haso: ", SwingConstants.RIGHT));
northPanel.add(passwordField);
add(northPanel, BorderLayout.NORTH);
final JTextArea textArea = new JTextArea(TEXTAREA_ROWS, TEXTAREA_COLUMNS);
JScrollPane scrollPane = new JScrollPane(textArea);
add(scrollPane, BorderLayout.CENTER);
// Dodanie przycisku wstawiajcego tekst do obszaru tekstowego
JPanel southPanel = new JPanel();
JButton insertButton = new JButton("Wstaw");
southPanel.add(insertButton);
insertButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
textArea.append("Nazwa uytkownika: " + textField.getText() + "
Haso: "
+ new String(passwordField.getPassword()) + "\n");
}
});
add(southPanel, BorderLayout.SOUTH);
pack();
}
}
JTextArea()
Rozdzia 9.
413
void setTabSize(int c)
JScrollPane(Component c)
414
Java. Podstawy
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 konstruktorze.
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 zawsze wiemy suchacza akcji. W omawianym programie oba pola wspdziel jednego
suchacza.
ActionListener listener = . . .
bold.addActionListener(listener);
italic.addActionListener(listener);
Rozdzia 9.
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)
boolean isSelected ()
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 przycisku w odpowiedzi na wczenie innego.
416
Java. Podstawy
Rysunek 9.15.
Grupa przyciskw
radiowych
(przecznikw)
Rozdzia 9.
417
Listing 9.4 przedstawia kompletny kod programu ustawiajcego rozmiar czcionki za pomoc
przecznikw.
Listing 9.4. radioButton/RadioButtonFrame.java
package radioButton;
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
label = new JLabel("Ko i w grali w koci z pikn m u rda.");
label.setFont(new Font("Serif", Font.PLAIN, DEFAULT_SIZE));
add(label, BorderLayout.CENTER);
// Dodanie przecznikw
buttonPanel = new JPanel();
group = new ButtonGroup();
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
*/
public void addRadioButton(String name, final int size)
{
boolean selected = size == DEFAULT_SIZE;
JRadioButton button = new JRadioButton(name, selected);
group.add(button);
buttonPanel.add(button);
// Ten suchacz ustawia rozmiar czcionki etykiety
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Parametr size odwouje si do ostatniego parametru metody addRadioButton
label.setFont(new Font("Serif", Font.PLAIN, size));
}
};
button.addActionListener(listener);
}
}
javax.swing.JRadioButton 1.2
void add(AbstractButton b)
ButtonModel getSelection()
String getActionCommand()
Rozdzia 9.
419
javax.swing.AbstractButton 1.2
void setActionCommand(String s)
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.
Etched (wgbienie),
Line (linia),
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
java.awt.*;
java.awt.event.*;
javax.swing.*;
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();
addRadioButton("Lowered bevel", BorderFactory.createLoweredBevelBorder());
addRadioButton("Raised bevel", BorderFactory.createRaisedBevelBorder());
addRadioButton("Etched", BorderFactory.createEtchedBorder());
addRadioButton("Line", BorderFactory.createLineBorder(Color.BLUE));
addRadioButton("Matte", BorderFactory.createMatteBorder(10, 10, 10, 10,
Color.BLUE));
addRadioButton("Empty", BorderFactory.createEmptyBorder());
Border etched = BorderFactory.createEtchedBorder();
Border titled = BorderFactory.createTitledBorder(etched, "Typy obramowania");
buttonPanel.setBorder(titled);
setLayout(new GridLayout(2, 1));
add(buttonPanel);
add(demoPanel);
pack();
}
public void addRadioButton(String buttonName, final Border b)
{
JRadioButton button = new JRadioButton(buttonName);
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
demoPanel.setBorder(b);
}
});
group.add(button);
buttonPanel.add(button);
}
}
Rozdzia 9.
421
Rne obramowania maj rne opcje suce do ustawiania szerokoci i koloru. Szczegowe 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
static Border createEmptyBorder(int top, int left, int bottom, int right)
highlight, shadow
type
Warto EtchedBorder.RAISED
lub EtchedBorder.LOWERED
highlight, shadow
type
Warto EtchedBorder.RAISED
lub EtchedBorder.LOWERED
422
Java. Podstawy
title
Tytu
border
Obramowanie, do ktrego ma by
dodany tytu
justification
position
font
color
Kolor tytuu
SoftBevelBorder(int type)
highlight, shadow
type
Warto EtchedBorder.RAISED
lub EtchedBorder.LOWERED
javax.swing.border.LineBorder 1.2
Rozdzia 9.
423
javax.swing.JComponent 1.2
Jeli pole listy rozwijalnej jest edytowalne (editable), aktualnie wybran opcj mona edytowa, 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.
Od Java 7 klasa JComboBox jest generyczna. Przykadowo JComboBox<String> przechowuje
obiekty typu String, a JComboBox<Integer> liczby cakowite.
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())
424
Java. Podstawy
JComboBox<String> faceCombo = new JComboBox<>();
faceCombo.addItem("Serif");
faceCombo.addItem("SansSerif");
. . .
Ta metoda dodaje acuch na kocu listy. Elementy mona take dodawa w dowolnym
miejscu listy za pomoc metody InsertItemAt:
faceCombo.insertItemAt("Monospaced", 0);
Dodawane elementy mog by dowolnego typu lista rozwijalna wywouje metod toString
przed wywietleniem kadego elementu.
Do usuwania elementw z listy w czasie dziaania programu su metody removeItem
i removeItemAt. Pierwszej naley poda tre elementu, ktry ma by usunity, a drugiej numer
pozycji elementu do usunicia.
faceCombo.removeItem("Monospaced");
faceCombo.removeItemAt(0);
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. Zwrcon 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));
}
Rozdzia 9.
425
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)
426
Java. Podstawy
void removeAllItems()
Object getSelectedItem()
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.
Najczciej stosowana metoda tworzenia suwakw jest nastpujca:
JSlider slider = new JSlider(min, max, initialValue);
Jeli parametry okrelajce warto minimaln, maksymaln i pocztkow nie zostan podane,
bd miay wartoci odpowiednio 0, 100 i 50.
Aby utworzy pionowy suwak, naley zastosowa nastpujc metod:
JSlider slider = new JSlider(SwingConstants.VERTICAL, min, max, initialValue);
Przedstawione konstruktory tworz zwyke suwaki, jak pierwszy na rysunku 9.18. Niebawem nauczymy si ozdabia suwaki rozmaitymi dodatkami.
W miar przesuwania przez uytkownika gaki suwaka przyjmuje on kolejne wartoci od
minimalnej do maksymalnej. Kiedy zmienia si warto, do wszystkich suchaczy zmian
wysyane jest zdarzenie typu ChangeEvent. Aby mc odbiera powiadomienia o zmianach,
naley wywoa metod addChangeListener i zainstalowa obiekt implementujcy interfejs
ChangeListener. Interfejs ten zawiera jedn metod o nazwie stateChanged. W metodzie tej
naley sprawdzi warto suwaka:
public void stateChanged(ChangeEvent event)
{
JSlider slider = (JSlider) event.getSource();
int value = slider.getValue();
. . .
}
Rozdzia 9.
427
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);
Duga kreska pojawia si co 20 jednostek, a krtsza co pi. Jednostki odnosz si do wartoci suwaka, nie do pikseli.
Te instrukcje su tylko do ustawienia liczby jednostek, co ile maj si pojawia znaczniki
w postaci kresek. Aby kreski te zostay uwidocznione, potrzebne jest nastpujce wywoanie:
slider.setPaintTicks(true);
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.
Na przykad w przypadku suwaka o zakresie 0 100 i odstpie dugich kresek co 20 jednostek etykiety bd nastpujce: 0, 20, 40, 60, 80, 100.
428
Java. Podstawy
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. Nastpnie 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);
Czwarty suwak na rysunku 9.18 jest pozbawiony prowadnicy. Za jej usunicie odpowiedzialna jest ponisza procedura:
slider.setPaintTrack(false);
java.awt.*;
java.util.*;
javax.swing.*;
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.
429
430
Java. Podstawy
slider.setPaintLabels(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Etykiety");
// Suwak z etykietami literowymi
slider = new JSlider();
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
Dictionary<Integer, Component> labelTable = new Hashtable<>();
labelTable.put(0, new JLabel("A"));
labelTable.put(20, new JLabel("B"));
labelTable.put(40, new JLabel("C"));
labelTable.put(60, new JLabel("D"));
labelTable.put(80, new JLabel("E"));
labelTable.put(100, new JLabel("F"));
slider.setLabelTable(labelTable);
addSlider(slider, "Niestandardowe etykiety");
// Suwak z etykietami ikonowymi
slider = new JSlider();
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(20);
labelTable = new Hashtable<Integer, Component>();
// Dodawanie obrazw kart
labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
labelTable.put(20, new JLabel(new ImageIcon("ten.gif")));
labelTable.put(40, new JLabel(new ImageIcon("jack.gif")));
labelTable.put(60, new JLabel(new ImageIcon("queen.gif")));
labelTable.put(80, new JLabel(new ImageIcon("king.gif")));
labelTable.put(100, new JLabel(new ImageIcon("ace.gif")));
slider.setLabelTable(labelTable);
addSlider(slider, "Ikony");
// Dodawanie pola tekstowego, ktre wywietla warto ustawion na suwaku
textField = new JTextField();
add(sliderPanel, BorderLayout.CENTER);
add(textField, BorderLayout.SOUTH);
pack();
}
/**
* Dodaje suwak do panelu suwakw i wie suchacza.
* @param s suwak
Rozdzia 9.
JSlider()
JSlider(int direction)
direction
SwingConstants.HORIZONTAL
min, max
initialValue
void setPaintTicks(boolean b)
void setPaintLabels(boolean b)
431
432
Java. Podstawy
void setSnapToTicks(boolean b)
void setPaintTrack(boolean b)
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 interfejsu 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 zamknite, 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. Zazwyczaj jest on umieszczany na samej grze ramki za pomoc metody setJMenuBar:
frame.setJMenuBar(menuBar);
Rozdzia 9.
433
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);
Polecenia wykonywane w odpowiedzi na kliknicie elementu menu czsto mog by aktywowane take przez inne elementy interfejsu, jak przyciski na pasku narzdzi. W rozdziale 8.
nauczylimy si okrela polecenia za porednictwem obiektw Action. Polega to na zdefiniowaniu klasy implementujcej interfejs Action, zazwyczaj dla wygody rozszerzajcej klas
AbstractAction. Etykiet elementu menu okrela si w konstruktorze obiektu typu Abstract
Action. Ponadto naley przedefiniowa metod actionPerformed na procedur obsugi akcji.
Na przykad:
Action exitAction = new AbstractAction("Zakocz")
{
public void actionPerformed(ActionEvent event)
{
procedury obsugi akcji
System.exit(0);
}
};
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)
434
Java. Podstawy
JMenuItem add(Action a)
void addSeparator()
index
JMenuItem(String label)
JMenuItem(Action a) 1.3
Rozdzia 9.
435
Na rysunku 9.19 ikony znajduj si obok kilku elementw menu. Przy standardowych ustawieniach 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.
Inna metoda ustawiania ikony polega na uyciu konstruktora AbstractAction:
cutAction = new
AbstractAction("Wytnij", new ImageIcon("cut.gif"))
{
public void actionPerformed(ActionEvent event)
{
kod akcji
}
};
javax.swing.JMenuItem 1.2
pos
javax.swing.AbstractAction 1.2
436
Java. Podstawy
Przeczniki w elementach menu dziaaj tak samo jak zwyke przeczniki. Musz nalee
do grupy przyciskw, w ktrej zaznaczenie jednego elementu powoduje usunicie zaznaczenia 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)
JRadioButtonMenuItem(String label)
boolean isSelected()
Rozdzia 9.
437
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 komponent 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 komponent, ktry rwnie posiada takie menu. Komponent podrzdny moe odziedziczy menu
kontekstowe elementu nadrzdnego dziki poniszej instrukcji:
child.setInheritsPopupMenu(true);
javax.swing.JPopupMenu 1.2
438
Java. Podstawy
Parametry:
x, y
boolean isPopupTrigger()
Rozdzia 9.
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 podmenu. 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"));
440
Java. Podstawy
label
Etykieta
mnemonic
void setAccelerator(KeyStroke k)
Rozdzia 9.
441
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 wyczaniu 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 uywa 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());
}
Dezaktywacja elementw menu bezporednio przed wywietleniem menu jest sprytnym rozwizaniem, ale nie sprawdza si w przypadku elementw posiadajcych
skrty klawiszowe. Poniewa wcinicie kombinacji klawiszy skrtu nie powoduje otwarcia
menu, akcja nie jest dezaktywowana, a wic mona j wyzwoli za pomoc akceleratora.
javax.swing.JMenuItem 1.2
void setEnabled(boolean b)
void menuSelected(MenuEvent e)
void menuDeselected(MenuEvent e)
void menuCanceled(MenuEvent e)
442
Java. Podstawy
Rozdzia 9.
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
// Menu z polem wyboru i przecznikami
readonlyItem = new JCheckBoxMenuItem("Tylko do odczytu");
readonlyItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
boolean saveOk = !readonlyItem.isSelected();
saveAction.setEnabled(saveOk);
saveAsAction.setEnabled(saveOk);
}
});
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem = new JRadioButtonMenuItem("Wstawianie");
insertItem.setSelected(true);
JRadioButtonMenuItem overtypeItem = new JRadioButtonMenuItem("Nadpisywanie");
group.add(insertItem);
group.add(overtypeItem);
// Ikony
Action cutAction = new TestAction("Wytnij");
cutAction.putValue(Action.SMALL_ICON, new ImageIcon("cut.gif"));
Action copyAction = new TestAction("Kopiuj");
copyAction.putValue(Action.SMALL_ICON, new ImageIcon("copy.gif"));
Action pasteAction = new TestAction("Wklej");
pasteAction.putValue(Action.SMALL_ICON, new ImageIcon("paste.gif"));
JMenu editMenu = new JMenu("Edycja");
editMenu.add(cutAction);
editMenu.add(copyAction);
editMenu.add(pasteAction);
// Zagniedone menu
JMenu optionMenu = new JMenu("Opcje");
optionMenu.add(readonlyItem);
optionMenu.addSeparator();
optionMenu.add(insertItem);
optionMenu.add(overtypeItem);
editMenu.addSeparator();
editMenu.add(optionMenu);
// Mnemoniki
JMenu helpMenu = new JMenu("Pomoc");
443
444
Java. Podstawy
helpMenu.setMnemonic('P');
JMenuItem indexItem = new JMenuItem("Indeks");
indexItem.setMnemonic('I');
helpMenu.add(indexItem);
// Mnemoniki mona take dodawa do akcji
Action aboutAction = new TestAction("O programie");
aboutAction.putValue(Action.MNEMONIC_KEY, new Integer('O'));
helpMenu.add(aboutAction);
// Dodanie wszystkich menu najwyszego rzdu do paska menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(helpMenu);
// Menu kontekstowe
popup = new JPopupMenu();
popup.add(cutAction);
popup.add(copyAction);
popup.add(pasteAction);
JPanel panel = new JPanel();
panel.setComponentPopupMenu(popup);
add(panel);
// Poniszy wiersz stanowi obejcie bdu 4966109
panel.addMouseListener(new MouseAdapter() {});
}
}
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 (rysunek 9.25). Po zwolnieniu przycisku myszy pasek narzdzi pozostaje w nowej lokalizacji
(rysunek 9.26).
Rozdzia 9.
445
Rysunek 9.25.
Przeciganie
paska narzdzi
Rysunek 9.26.
Pasek narzdzi
w nowej lokalizacji
Przeciganie paska narzdzi jest moliwe w kontenerach z ukadem krawdziowym lub dowolnym zarzdc rozkadu, ktry obsuguje ograniczenia North, East,
South i West.
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);
446
Java. Podstawy
Nastpnie pasek narzdzi trzeba wstawi do ramki.
add(bar, BorderLayout.NORTH);
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 pooenie 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 wszystkie 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.
Listing 9.9. toolBar/ToolBarTest.java
package toolBar;
import java.awt.*;
Rozdzia 9.
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)
JButton add(Action a)
void addSeparator()
447
448
Java. Podstawy
Rozdzia 9.
449
450
Java. Podstawy
Przyjrzyjmy si opcjom dotyczcym wyboru wasnoci czcionki na rysunku 9.29. Opieraj
si one na nastpujcych komponentach:
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
Utwrz obiekt typu GridBagLayout. Nie trzeba podawa liczby wierszy i kolumn,
z ktrych ma si skada siatka. Zarzdca sam sprbuje te informacje zdoby
na podstawie danych dostarczonych pniej.
poniszego wywoania:
add(component, constraints);
Rozdzia 9.
451
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 przynosi to dobrego rezultatu.
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. external padding).
Wartoci ipadx i ipady okrelaj dopenienie wewntrzne (ang. internal padding). Wartoci
te s dodawane do minimalnej szerokoci i wysokoci komponentu. Stanowi to zabezpieczenie przed skurczeniem si komponentu do minimalnych rozmiarw.
Rozdzia 9.
1.
453
Niektre rodowiska do budowy GUI udostpniaj nawet wizualne narzdzia suce do okrelania ogranicze. Rysunek 9.31 przedstawia okno dialogowe konfiguracji w NetBeans.
Rysunek 9.31.
Okrelanie
ogranicze
rozkadu
GridBagLayout
w rodowisku
NetBeans
454
Java. Podstawy
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 debugowania.
W kursie na stronie http://docs.oracle.com/javase/tutorial/uiswing/layout/
gridbag.html znajduje si zalecenie, aby uywa tego samego obiektu GridBagConstraints dla wszystkich komponentw. W naszym odczuciu powstay w ten sposb
kod jest trudny do odczytania i podatny na bdy. Spjrzmy na przykad na demonstracyjny
program dostpny na stronie http://docs.oracle.com/javase/tutorial/uiswing/events/
containerlistener.html. Czy przyciski z zaoenia miay si rozciga, czy moe programista zapomnia wyczy ograniczenie fill?
Listing 9.10. gridbag/FontFrame.java
package gridbag;
import
import
import
import
java.awt.*;
java.awt.event.*;
java.beans.*;
javax.swing.*;
Rozdzia 9.
/**
* 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;
private
private
private
private
private
JComboBox<String> face;
JComboBox<Integer> size;
JCheckBox bold;
JCheckBox italic;
JTextArea sample;
public FontFrame()
{
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
ActionListener listener = EventHandler.create(ActionListener.class, this,
"updateSample");
// Tworzenie komponentw
JLabel faceLabel = new JLabel("Krj: ");
face = new JComboBox<>(new String[] { "Serif", "SansSerif", "Monospaced",
"Dialog",
"DialogInput" });
face.addActionListener(listener);
JLabel sizeLabel = new JLabel("Rozmiar: ");
size = new JComboBox<>(new Integer[] { 8, 10, 12, 15, 18, 24, 36, 48 });
size.addActionListener(listener);
bold = new JCheckBox("Bold");
bold.addActionListener(listener);
italic = new JCheckBox("Italic");
italic.addActionListener(listener);
sample = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
sample.setText("Ko i pies grali w koci z pikn m u rda.");
sample.setEditable(false);
sample.setLineWrap(true);
sample.setBorder(BorderFactory.createEtchedBorder());
// Dodawanie komponentw do siatki przy uyciu klasy pomocniczej GBC
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));
455
456
Java. Podstawy
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));
pack();
updateSample();
}
public void updateSample()
{
String fontFace = (String) face.getSelectedItem();
int fontStyle = (bold.isSelected() ? Font.BOLD : 0)
+ (italic.isSelected() ? Font.ITALIC : 0);
int fontSize = size.getItemAt(size.getSelectedIndex());
Font font = new Font(fontFace, fontStyle, fontSize);
sample.setFont(font);
sample.repaint();
}
}
Rozdzia 9.
}
/**
* 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)
{
457
458
Java. Podstawy
this.insets = new Insets(top, left, bottom, right);
return this;
}
/**
* 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
int anchor
NORTH
NORTHEAST
WEST
CENTER
EAST
SOUTHWEST
SOUTH
SOUTHEAST
LINE_START
FIRST_LINE_END
PAGE_START
CENTER
PAGE_END
LAST_LINE_START
LINE_END
LAST_LINE_END
int fill
Rozdzia 9.
459
Insets insets
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 znajdujcym si na grze.
Wyglda to do strasznie, ale na szczcie nie trzeba pisa tego kodu wasnorcznie. Znajomo podstaw dotyczcych akcji rozkadu jest jednak przydatna, poniewa umoliwia znajdywanie 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.
461
Jak wida w przykadowym kodzie, rozkad grupowy oddziela obliczenia zwizane z uoeniem 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 tekstowe 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).
Czynnoci te powoduj due zmiany w kodzie:
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jLabel1, GroupLayout.Alignment.TRAILING)
.addComponent(jLabel2, GroupLayout.Alignment.TRAILING))
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jTextField1)
.addComponent(jPasswordField1))
462
Java. Podstawy
Rysunek 9.32.
Wyrwnywanie
etykiet i pl
tekstowych
w Matisse
Rozdzia 9.
463
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).
Matisse doda nastpujc instrukcj do kodu rozkadu:
layout.linkSize(SwingConstants.HORIZONTAL, new Component[] {jPasswordField1, jTextField1});
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.
Listing 9.12. groupLayout/FontFrame.java
package groupLayout;
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;
private
private
private
private
private
private
JComboBox<String> face;
JComboBox<Integer> size;
JCheckBox bold;
JCheckBox italic;
JScrollPane pane;
JTextArea sample;
public FontFrame()
{
ActionListener listener = EventHandler.create(ActionListener.class, this,
"updateSample");
// Tworzenie komponentw
JLabel faceLabel = new JLabel("Krj: ");
face = new JComboBox<>(new String[] { "Serif", "SansSerif", "Monospaced",
"Dialog",
"DialogInput" });
face.addActionListener(listener);
JLabel sizeLabel = new JLabel("Rozmiar: ");
size = new JComboBox<>(new Integer[] { 8, 10, 12, 15, 18, 24, 36, 48 });
size.addActionListener(listener);
bold = new JCheckBox("Bold");
bold.addActionListener(listener);
Rozdzia 9.
465
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();
}
public void updateSample()
{
String fontFace = (String) face.getSelectedItem();
int fontStyle = (bold.isSelected() ? Font.BOLD : 0)
+ (italic.isSelected() ? Font.ITALIC : 0);
int fontSize = size.getItemAt(size.getSelectedIndex());
Font font = new Font(fontFace, fontStyle, fontSize);
sample.setFont(font);
sample.repaint();
}
}
javax.swing.GroupLayout 6
GroupLayout(Container host)
void setHorizontalGroup(GroupLayout.Group g)
void setVerticalGroup(GroupLayout.Group g)
Wymusza taki sam rozmiar komponentw lub taki sam rozmiar wzgldem tylko
jednej z osi (SwingConstants.HORIZONTAL lub SwingConstants.VERTICAL).
GroupLayout.SequentialGroup createSequentialGroup()
GroupLayout.ParallelGroup createParallelGroup()
Rozdzia 9.
align
resizable
boolean getHonorsVisibility()
void setHonorsVisibility(boolean b)
boolean getAutoCreateGaps()
void setAutoCreateGaps(boolean b)
boolean getAutoCreateContainerGaps()
void setAutoCreateContainerGaps(boolean b)
GroupLayout.Group addComponent(Component c)
GroupLayout.Group addGroup(GroupLayout.Group g)
467
468
Java. Podstawy
javax.swing.GroupLayout.ParallelGroup
GroupLayout.SequentialGroup addContainerGap()
GroupLayout.SequentialGroup addPreferredGap(LayoutStyle.
ComponentPlacement type)
Rozdzia 9.
Parametry:
x, y
width, height
469
Listing 9.13 przedstawia kod bezuytecznego zarzdcy CircleLayout, ktry ukada komponenty na krawdzi koa. Klasa ramowa tego programu jest przedstawiona na listingu 9.14.
Listing 9.13. circleLayout/CircleLayout.java
package circleLayout;
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;
public void addLayoutComponent(String name, Component comp)
{
}
public void removeLayoutComponent(Component comp)
{
}
public void setSizes(Container parent)
{
if (sizesSet) return;
int n = parent.getComponentCount();
preferredWidth = 0;
preferredHeight = 0;
minWidth = 0;
minHeight = 0;
maxComponentWidth = 0;
maxComponentHeight = 0;
// Obliczanie maksymalnych szerokoci i wysokoci komponentw
// oraz ustawianie preferowanego rozmiaru na sum rozmiarw komponentw
for (int i = 0; i < n; i++)
{
Component c = parent.getComponent(i);
if (c.isVisible())
{
Dimension d = c.getPreferredSize();
maxComponentWidth = Math.max(maxComponentWidth, d.width);
maxComponentHeight = Math.max(maxComponentHeight, d.height);
preferredWidth += d.width;
preferredHeight += d.height;
}
}
minWidth = preferredWidth / 2;
minHeight = preferredHeight / 2;
sizesSet = true;
}
public Dimension preferredLayoutSize(Container parent)
{
setSizes(parent);
Insets insets = parent.getInsets();
int width = preferredWidth + insets.left + insets.right;
Rozdzia 9.
471
472
Java. Podstawy
name
comp
Usuwa komponent.
Rozdzia 9.
473
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 komponent znajdujcy si za wspomnianym kontenerem.
Cech t mona obrci na swoj korzy, grupujc powizane elementy w dodatkowym kontenerze, np. panelu.
Elementy z kolejki dostpu usuwa si za pomoc instrukcji podobnej do poniszej:
component.setFocusable(false);
474
Java. Podstawy
przycisk OK.
(typu OK/Cancel).
Rozdzia 9.
475
od uytkownika.
Rysunek 9.36 przedstawia typowe okno dialogowe. Skada si ono z nastpujcych komponentw:
ikona,
komunikat,
Rysunek 9.36.
Okno dialogowe
opcji
Okno dialogowe przyjmujce dane wejciowe (ang. input dialog) zawiera dodatkowy komponent 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.
Ikona po lewej stronie zaley od typu komunikatu, ktrych jest pi:
ERROR_MESSAGE
INFORMATION_MESSAGE
WARNING_MESSAGE
QUESTION_MESSAGE
PLAIN_MESSAGE
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[]
476
Java. Podstawy
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
Icon
Component
Wywietla komponent.
Brak
showConfirmDialog
showOptionDialog
showInputDialog
Metody showConfirmDialog i showOptionDialog zwracaj liczby cakowite reprezentujce kliknity przez uytkownika przycisk. W przypadku okna dialogowego jest to zwyky indeks
wybranej opcji lub warto CLOSED_OPTION, jeli uytkownik zamkn okno, nie wybierajc
adnej opcji. W oknie dialogowym potwierdzenia (ang. confirmation dialog) dostpne s
nastpujce wartoci zwrotne:
OK_OPTION
CANCEL_OPTION
YES_OPTION
NO_OPTION
CLOSED_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.
komponentw).
4. W przypadku okna potwierdzenia wybierz typ opcji (Yes/No, Yes/No/Cancel
lub OK/Cancel).
Rozdzia 9.
477
5. W przypadku okna dialogowego opcji wybierz opcje (acuchy, ikony lub wasne
Wyobramy sobie na przykad, e chcemy utworzy okno dialogowe widoczne na rysunku 9.36. Okno to wywietla komunikat i prosi uytkownika o zatwierdzenie lub anulowanie. Jest to wic okno potwierdzenia. Jako ikona wywietli si znak zapytania. Typ opcji
to OK_CANCEL_OPTION. Oto przykadowy kod tworzcy takie okno:
int selection = JOptionPane.showConfirmDialog(parent,
"Message", "Tytu",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (selection == JOptionPane.OK_OPTION) . . .
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
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));
typePanel = new ButtonPanel("Typ", "Komunikat", "Potwierdzenie", "Opcja",
"Dane wejciowe");
messageTypePanel = new ButtonPanel("Typ komunikatu", "ERROR_MESSAGE",
"INFORMATION_MESSAGE",
"WARNING_MESSAGE", "QUESTION_MESSAGE", "PLAIN_MESSAGE");
messagePanel = new ButtonPanel("Komunikat", "acuch", "Ikona", "Komponent",
"Inny", "Object[]");
optionTypePanel = new ButtonPanel("Potwierdzenie", "DEFAULT_OPTION",
"YES_NO_OPTION",
"YES_NO_CANCEL_OPTION", "OK_CANCEL_OPTION");
optionsPanel = new ButtonPanel("Opcja", "String[]", "Icon[]", "Object[]");
inputPanel = new ButtonPanel("Dane wejciowe", "Pole tekstowe", "Pole kombi");
gridPanel.add(typePanel);
gridPanel.add(messageTypePanel);
gridPanel.add(messagePanel);
gridPanel.add(optionTypePanel);
gridPanel.add(optionsPanel);
gridPanel.add(inputPanel);
// Dodanie panelu z przyciskiem Poka
JPanel showPanel = new JPanel();
JButton showButton = new JButton("Poka");
showButton.addActionListener(new ShowAction());
showPanel.add(showButton);
add(gridPanel, BorderLayout.CENTER);
add(showPanel, BorderLayout.SOUTH);
pack();
}
/**
* Pobiera aktualnie wybrany komunikat.
Rozdzia 9.
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("yellowball.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
public void actionPerformed(ActionEvent event)
{
if (typePanel.getSelection().equals("Potwierdzenie"))
JOptionPane.showConfirmDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(optionTypePanel),
getType(messageTypePanel));
else if (typePanel.getSelection().equals("Dane wejciowe"))
{
if (inputPanel.getSelection().equals("Pole tekstowe"))
JOptionPane.showInputDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(messageTypePanel));
else JOptionPane.showInputDialog(OptionDialogFrame.this, getMessage(),
"Tytu",
getType(messageTypePanel), null, new String[] { "ty",
"Niebieski", "Czerwony" },
"Niebieski");
}
else if (typePanel.getSelection().equals("Komunikat"))
JOptionPane.showMessageDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(messageTypePanel));
else if (typePanel.getSelection().equals("Opcja"))
JOptionPane.showOptionDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(optionTypePanel),
getType(messageTypePanel), null, getOptions(), getOptions()[0]);
}
}
}
/**
* Komponent z pomalowan powierzchni
*/
class SampleComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Rectangle2D rect = new Rectangle2D.Double(0, 0, getWidth() - 1,
getHeight() - 1);
g2.setPaint(Color.YELLOW);
g2.fill(rect);
g2.setPaint(Color.BLUE);
g2.draw(rect);
}
public Dimension getPreferredSize()
{
return new Dimension(10, 10);
}
}
Rozdzia 9.
481
482
Java. Podstawy
parent
message
title
messageType
icon
parent
message
Rozdzia 9.
title
messageType
optionType
icon
483
Wywietla okno dialogowe opcji lub wewntrzne okno dialogowe opcji (wewntrzne
okno dialogowe w caoci zawiera si w swoim oknie nadrzdnym). Zwraca indeks
wybranej przez uytkownika opcji lub warto CLOSED_OPTION, jeli uytkownik
anulowa okno.
Parametry:
parent
message
title
messageType
optionType
icon
options
default
484
Java. Podstawy
parent
message
title
messageType
icon
values
default
Rozdzia 9.
485
Rysunek 9.38.
Okno dialogowe
typu O programie
1.
W konstruktorze nadklasy trzeba poda ramk nadrzdn, tytu okna dialogowego oraz
okreli modalno.
Ramka nadrzdna odpowiada za miejsce wywietlenia okna dialogowego. Warto null
powoduje, e okno naley do ukrytej ramki.
Modalno okrela, ktre z pozostaych okien aplikacji bd zablokowane, kiedy wywietli
si to okno. Okno niemodalne nie blokuje innych okien, natomiast okno modalne blokuje
wszystkie pozostae okna (poza swoimi potomkami). Niemodalne okna dialogowe znajduj
zastosowanie jako zawsze dostpne zestawy narzdzi. Modalne okno dialogowe mona zastosowa, w przypadku gdy do kontynuacji dziaania programu konieczne jest dostarczenie informacji przez uytkownika.
W Java SE 6 dostpne s dwa rodzaje modalnoci. Okno dialogowe z modalnoci
dokumentu (ang. document-modal dialog) blokuje wszystkie okna nalece do tego
samego dokumentu. Jako dokument w tym przypadku rozumie si hierarchi okien
majcych wsplnego przodka, ktry nie ma waciciela w postaci okna dialogowego.
W ten sposb rozwizano problem z systemami pomocy. Wczeniej uytkownik nie mg
korzysta z pomocy, jeli byo wywietlone okno modalne. Okno dialogowe z modalnoci zestawu narzdzi (ang. toolkit-modal dialog) blokuje wszystkie okna nalece do
tego samego zestawu narzdzi. Zestaw narzdzi to program w Javie, ktry uruchamia
kilka aplikacji, np. silnik apletw w przegldarce. Wicej informacji na ten temat mona
znale na stronie www.oracle.com/technetwork/articles/javase/modality-137604.html.
486
Java. Podstawy
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);
Kliknicie przycisku OK powinno zamyka okno. Dziaanie to zostao zdefiniowane w procedurze obsugi przycisku OK:
ok.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});
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.
Listing 9.17. dialog/DialogFrame.java
package dialog;
import java.awt.event.*;
import javax.swing.*;
Rozdzia 9.
/**
* 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);
// Tworzenie menu Plik
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu fileMenu = new JMenu("Plik");
menuBar.add(fileMenu);
// Tworzenie elementw O programie i Zamknij
// Element O programie wywietla okno dialogowe O programie
JMenuItem aboutItem = new JMenuItem("O programie");
aboutItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (dialog == null) // pierwszy raz
dialog = new AboutDialog(DialogFrame.this);
dialog.setVisible(true); // wyskakujce okno dialogowe
}
});
fileMenu.add(aboutItem);
// Element Zamknij powoduje zamknicie programu
JMenuItem exitItem = new JMenuItem("Zamknij");
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
fileMenu.add(exitItem);
}
}
487
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);
// Dodanie etykiety HTML
add(
new JLabel(
"<html><h1><i>Core Java</i></h1><hr> By Cay Horstmann and Gary
Cornell </html>"),
BorderLayout.CENTER);
// Przycisk Ok zamyka okno
JButton ok = new JButton("Ok");
ok.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});
// Dodanie przycisku Ok przy krawdzi poudniowej
JPanel panel = new JPanel();
panel.add(ok);
add(panel, BorderLayout.SOUTH);
pack();
}
}
javax.swing.JDialog 1.2
Tworzy okno dialogowe. Okno nie jest widoczne, dopki nie zostanie celowo
uwidocznione.
Parametry:
parent
title
Tytu okna
modal
Rozdzia 9.
489
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 zamknicia 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.
Prezentowany przykadowy program posiada dodatkowe usprawnienie. Konstruujc obiekt typu
JDialog, trzeba okreli ramk nadrzdn. Czsto jednak zdarza si, e jedno okno dialogowe
490
Java. Podstawy
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;
if (dialog == null || dialog.getOwner() != owner)
{
dialog = new JDialog(owner, true);
dialog.add(this);
dialog.pack();
}
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);
Usprawnienie to zastosowalimy w naszym przykadowym programie. Mechanizm ten wykorzystuje take klasa JOptionPane.
Wiele okien dialogowych posiada przycisk domylny (ang. default button), ktry jest automatycznie naciskany, kiedy uytkownik nacinie klawisz wyzwolenia (ang. trigger key)
w wikszoci stylw jest to klawisz Enter. Przycisk domylny jest w jaki sposb wyrniony, zazwyczaj grubym obramowaniem.
Przycisk domylny ustawia si w panelu gwnym okna dialogowego (ang. root pane):
dialog.getRootPane().setDefaultButton(okButton);
Rozdzia 9.
491
492
Java. Podstawy
/**
* Akcja Connect wywietla okno dialogowe z polem hasa
*/
private class ConnectAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
// Jeli jest to pierwszy raz, tworzy okno dialogowe
if (dialog == null) dialog = new PasswordChooser();
// Ustawianie wartoci domylnych
dialog.setUser(new User("Twoja nazwa", null));
// Wywietlenie okna dialogowego
if (dialog.showDialog(DataExchangeFrame.this, "Pocz"))
{
// Pobranie danych uytkownika w przypadku zatwierdzenia
User u = dialog.getUser();
textArea.append("nazwa uytkownika = " + u.getName() + ", haso = "
+ (new String(u.getPassword())) + "\n");
}
}
}
}
Rozdzia 9.
493
494
Java. Podstawy
// Lokalizacja ramki nadrzdnej
Frame owner = null;
if (parent instanceof Frame) owner = (Frame) parent;
else owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);
// Jeli jest to pierwszy raz lub zmieni si uytkownik, utworzenie nowego okna dialogowego
if (dialog == null || dialog.getOwner() != owner)
{
dialog = new JDialog(owner, true);
dialog.add(this);
dialog.getRootPane().setDefaultButton(okButton);
dialog.pack();
}
// Ustawienie tytuu i wywietlenie okna dialogowego
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}
}
javax.swing.SwingUtilities 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
Ustawia domylny przycisk dla panelu gwnego. Aby dezaktywowa ten przycisk,
naley wywoa t metod z wartoci null.
javax.swing.JButton 1.2
boolean isDefaultButton()
Zwraca warto true, jeli dany przycisk jest domylny w swoim panelu gwnym.
Rozdzia 9.
495
Ponisze punkty opisuj procedur tworzenia okna dialogowego wyboru pliku i odzyskiwania
tego, co uytkownik wybra w polu wyboru.
1.
496
Java. Podstawy
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));
(na przykad z rozszerzeniem .gif), naley utworzy filtr plikw. Filtry plikw
zostay opisane w dalszej czci tego rozdziau.
6. Przy standardowych ustawieniach uytkownik moe wybiera tylko pliki.
Aby umoliwi wybr katalogw, naley uy metody setFileSelectionMode.
Jej parametrem powinna by jedna z nastpujcych wartoci JFileChooser.FILES_ONLY
(domylna), JFileChooser.DIRECTORIES_ONLY lub JFileChooser.FILES_AND_
DIRECTORIES.
7. Do uwidocznienia okna su metody showOpenDialog i showSaveDialog.
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
Rozdzia 9.
497
Pierwsza z tych metod sprawdza, czy plik powinien zosta zaakceptowany. Druga zwraca
opis typu pliku, ktry moe by wywietlony w oknie wyboru plikw.
W pakiecie java.io znajduje si niezwizany z omawian klas interfejs FileFilter, ktry udostpnia jedn metod boolean accept(File f). Jest ona
uywana w metodzie listFiles klasy File do tworzenia listy plikw znajdujcych si
w katalogu. Nie wiadomo, dlaczego projektanci biblioteki Swing nie rozszerzyli tego interfejsu moliwe, e biblioteka klas Javy staa si tak obszerna, e nawet sami
programici z Sun nie potrafi ogarn wszystkich standardowych klas i interfejsw.
W przypadku importu pakietw java.io i javax.swing.filechooser jednoczenie konieczne jest rozwizanie konfliktu nazw. Najprostsze rozwizanie polega na zaimportowaniu samej klasy javax.swing.filechooser.FileFilter zamiast wszystkich klas
tego pakietu javax.swing.filechooser.*.
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 uytkownik 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
Jest to z pewnoci zaawansowana technika. W typowych sytuacjach nie ma potrzeby tworzenia widoku plikw, poniewa odpowiada za niego styl. Aby jednak specjalne typy plikw
miay rne ikony, mona utworzy wasny widok plikw. Wymaga to rozszerzenia klasy
FileView i implementacji piciu metod:
Icon getIcon(File f);
String getName(File f);
String getDescription(File f);
String getTypeDescription(File f);
Boolean isTraversable(File f);
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 okrelon 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;
}
public Icon getIcon(File f)
{
if (!f.isDirectory() && filter.accept(f))
return icon;
else return null;
}
private FileFilter filter;
private Icon icon;
}
Rozdzia 9.
499
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.
Bardziej uyteczna przykadowa klasa o nazwie ExampleFileView znajduje si
w katalogu JDK demo/jfc/FileChooserDemo. Pozwala ona na wizanie ikon i opisw
z dowolnymi rozszerzeniami.
W kocu okno dialogowe mona wyposay w dodatkowe akcesorium (ang. accessory component). 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
500
Java. Podstawy
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 szczegowo 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
. . .
}
}
});
java.awt.event.*;
java.io.*;
javax.swing.*;
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);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
Rozdzia 9.
501
502
Java. Podstawy
java.awt.*;
java.beans.*;
java.io.*;
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;
}
// Wczytanie obrazu jako ikony
ImageIcon icon = new ImageIcon(f.getPath());
// Skalowanie obrazu, jeli jest zbyt duy na ikon
if (icon.getIconWidth() > getWidth()) icon = new
ImageIcon(icon.getImage()
.getScaledInstance(getWidth(), -1, Image.SCALE_DEFAULT));
setIcon(icon);
}
}
});
}
}
Rozdzia 9.
java.io.*;
javax.swing.*;
javax.swing.filechooser.*;
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;
}
public Icon getIcon(File f)
{
if (!f.isDirectory() && filter.accept(f)) return icon;
else return null;
}
}
javax.swing.JFileChooser 1.2
JFileChooser()
Tworzy okno dialogowe wyboru plikw, ktrego mona uywa w wielu ramkach.
void setMultiSelectionEnabled(boolean b)
Pozwala na wybr tylko plikw (domylnie), tylko katalogw lub jednych i drugich.
Parametr mode moe mie jedn z nastpujcych wartoci
503
504
Java. Podstawy
JFileChooser.FILES_ONLY, JFileChooser.DIRECTORIES_ONLY
lub FileChooser.FILES_AND_DIRECTORIES.
File getSelectedFile()
File[] getSelectedFiles()
Pobiera plik lub pliki wybrane przez uytkownika (lub zwraca warto null,
jeli uytkownik nie wybra adnego pliku).
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 setAcceptAllFileFilterUsed(boolean b)
void resetChoosableFileFilters()
Czyci list dostpnych filtrw plikw. Pozostaje tylko filtr All files, jeli nie zostanie
jawnie wyczony.
boolean accept(File f)
String getDescription()
Rozdzia 9.
505
javax.swing.filechooser.FileNameExtensionFilter 6
String getName(File f)
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)
506
Java. Podstawy
Rysunek 9.42.
Karta Swatches
(prbki)
Rysunek 9.43.
Karta HSB
Rysunek 9.44.
Karta RGB
Rozdzia 9.
komponent nadrzdny,
507
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
java.awt.*;
java.awt.event.*;
javax.swing.*;
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);
JButton modelessButton = new JButton("Niemodalne");
modelessButton.addActionListener(new ModelessListener());
add(modelessButton);
JButton immediateButton = new JButton("Bezporednie");
immediateButton.addActionListener(new ImmediateListener());
add(immediateButton);
}
/**
* 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.
// Suchacz
// przycisku OK
{
public void actionPerformed(ActionEvent event)
{
setBackground(chooser.getColor());
}
}, null /* Brak suchacza dla przycisku Cancel. */);
}
public void actionPerformed(ActionEvent event)
{
chooser.setColor(getBackground());
dialog.setVisible(true);
}
}
/**
* 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());
}
});
dialog = new JDialog((Frame) null, false /* niemodalne */);
dialog.add(chooser);
dialog.pack();
}
public void actionPerformed(ActionEvent event)
{
chooser.setColor(getBackground());
dialog.setVisible(true);
}
}
}
javax.swing.JColorChooser 1.2
JColorChooser()
509
510
Java. Podstawy
Color getColor()
void setColor(Color c)
parent
title
initialColor
parent
title
modal
chooser
okListener,
cancelListener
Na tym zakoczymy opis elementw interfejsu uytkownika. Informacje zawarte w rozdziaach 7., 8. i 9. pozwalaj na tworzenie prostych GUI w Swingu. Bardziej zaawansowane komponenty Swing i techniki graficzne zostay opisane w drugim tomie.
10
Przygotowywanie apletw
i aplikacji do uytku
W tym rozdziale:
Pliki JAR
Aplety
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 programu 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 naprawia niektre z wad apletw. Na kocu opisujemy aplety, pokazujc sytuacje, w ktrych mog
znale zastosowanie.
Piszemy take o sposobach zapisywania danych konfiguracyjnych i preferencji uytkownika
w programach.
512
Java. Podstawy
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 ktrych mowa w rozdziale 8. drugiego tomu) i biblioteki kodu. Na przykad biblioteka wykonawcza 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.
Wspomniany plik manifestu ma nazw MANIFEST.MF, a jego lokalizacja to specjalny podkatalog META-INF w pliku JAR. Minimalna zawarto takiego pliku nie jest zbyt interesujca:
Manifest-Version: 1.0
Zoone pliki tego typu mog zawiera znacznie wicej wpisw pogrupowanych w sekcjach. Pierwsza sekcja nosi nazw sekcji gwnej (ang. main section) i ma zastosowanie do
Rozdzia 10.
513
Opis
Tworzy punkt startowy w manifecie (zobacz podrozdzia 10.1.2, Wykonywalne pliki JAR).
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).
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.
Wypakowuje pliki. Jeli podanych zostanie kilka nazw plikw, zostan wypakowane tylko one.
W przeciwnym przypadku program wypakuje wszystkie pliki.
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 ponisze polecenie:
jar cfm NazwaPlikuJAR NazwaPlikuManifest . . .
Aby zaktualizowa plik manifestu istniejcego pliku JAR, naley umieci w pliku tekstowym 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
W zalenoci od konfiguracji systemu operacyjnego moe by moliwe uruchomienie aplikacji za pomoc dwukrotnego kliknicia pliku JAR. Poniej znajduje si opis zachowania
rnych systemw w takiej sytuacji:
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 uruchamiajcy. Wicej informacji na ten temat mona znale pod adresem http://www.javalobby.
org/articles/java2exe.
Rozdzia 10.
515
10.1.3. Zasoby
Klasy uywane zarwno w apletach, jak i aplikacjach czsto wykorzystuj pliki danych tego
samego typu:
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
1.
2. Jeli zasobem jest obraz lub plik audio, wywoaj metod getResource(filename)
Chodzi o to, aby program adujcy klasy potrafi znale klas i odszuka zwizane z ni
zasoby w tej samej lokalizacji.
Na przykad poniszy fragment kodu tworzy ikon z pliku about.gif:
URL url = ResourceTest.class.getResource("about.gif");
Image img = new ImageIcon(url).getImage();
Powyszy kod mona odczyta nastpujco: znajd plik about.gif w tej samej lokalizacji,
w ktrej znajduje si klasa ResourceTest.
Ponisze instrukcje wczytuj plik about.txt:
InputStream stream = ResourceTest.class.getResourceAsStream("about.txt");
Scanner in = new Scanner(stream);
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 separator katalogw stosowany w systemie, w ktrym s przechowywane pliki zasobw. Na przykad w systemie plikw systemu Windows separatory / s automatycznie zamieniane na \.
Nazwa zasobu zaczynajca si od znaku / jest bezwzgldn nazw zasobu. Jest ona lokalizowana w taki sam sposb jak klasa wewntrz pakietu. Na przykad zasb:
/corejava/title.txt
Rozdzia 10.
517
Listing 10.1 przedstawia kod programu demonstrujcego adowanie zasobw. Ponisze polecenia 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.
Listing 10.1. resource/ResourceTest.java
package resource;
import
import
import
import
import
java.awt.*;
java.io.*;
java.net.*;
java.util.*;
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
JTextArea textArea = new JTextArea();
InputStream stream = getClass().getResourceAsStream("about.txt");
Scanner in = new Scanner(stream);
while (in.hasNext())
textArea.append(in.nextLine() + "\n");
add(textArea);
}
}
java.lang.Class 1.0
Znajduje zasb w tym samym katalogu, w ktrym jest umieszczona klasa, i zwraca
adres URL lub strumie wejciowy, za pomoc ktrego mona ten zasb zaadowa.
Zwraca warto null, jeli zasb nie istnieje, dziki czemu nie powoduje wyjtku
dla bdu wejcia-wyjcia.
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, wstawiajc 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. Nastpnie naley uruchomi narzdzie jar w zwyky sposb:
jar cvfm MyArchive.jar manifest.mf pliki do dodania
Rozdzia 10.
519
Wyprbujemy mechanizm Java Web Start na kalkulatorze z rozdziau 9. Wykonaj nastpujce czynnoci:
1.
Skompiluj program.
javac -classpath .:/path/to/javaws.jar webstart/*.java
520
Java. Podstawy
<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
Rozdzia 10.
521
Rysunek 10.3.
Kalkulator otwarty
za porednictwem
Java Web Start
11. Kiedy nastpnym razem sprbujemy uzyska dostp do pliku JNLP, program bdzie
Rysunek 10.4.
Aplikacje
w pamici
podrcznej
522
Java. Podstawy
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 aplikacji, 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 pierwszorzdn 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 uytkownikw w celu rozsyania spamu.
Technologia Java dysponuje zaawansowanym modelem ochrony, ktry zapobiega wykorzystywaniu jej do nikczemnych postpkw. Model ten zosta szczegowo opisany w tomie
drugim. Meneder zabezpiecze (ang. security manager) kontroluje dostp do wszystkich
zasobw systemowych. Przy standardowych ustawieniach zezwala tylko na nieszkodliwe
operacje. Aby zezwoli na dodatkowe operacje, kod musi by podpisany cyfrowo, a uytkownik musi zatwierdzi podpis certyfikatu.
Co pobierany zdalnie kod moe robi na wszystkich platformach? Zawsze mona wywietla
obrazy, odtwarza dwiki, odpowiada na nacinicia przez uytkownika klawiszy i przyciskw 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 wykonawcze jest czsto nazywane piaskownic (ang. sandbox). Kod dziaajcy w piaskownicy
nie moe nic zmienia w systemie uytkownika ani go szpiegowa.
Rozdzia 10.
523
Aby mc dziaa poza piaskownic, pliki JAR aplikacji Java Web Start musz by podpisane 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). Odbierajc aplikacj, mamy pewno, e:
1.
Kod aplikacji nie zosta zmieniony w aden sposb od chwili jej podpisania.
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 rzeczywistoci, jeli klikniemy odnonik More Information, dowiemy si, e aplikacja zostanie uruchomiona bez zwyczajowych ogranicze. To, czy zainstalowa t aplikacj, czy nie, w duej mierze
zaley od tego, czy ufamy firmie yWorks.
Koszty uzyskania certyfikatu od jednego z obsugiwanych dostawcw wynosz kilkaset
dolarw rocznie. Wielu deweloperw generuje wasne certyfikaty i nimi podpisuje kod. Oczywicie Java Web Start nie ma moliwoci sprawdzenia jakoci tych certyfikatw. Odbierajc
tak aplikacj, wiadomo, e:
1.
Kod wyglda dokadnie tak samo jak wtedy, kiedy zosta podpisany.
Nikt niepowoany przy nim nie majstrowa.
2. Kto podpisa kod, ale Java Web Start nie moe sprawdzi kto.
W zwizku z tym mechanizm ten jest kompletnie bezuyteczny. Kady mg zmodyfikowa kod, a nastpnie go podpisa, twierdzc, e jest jego autorem. Niemniej Java Web Start
z najwiksz przyjemnoci wywietli certyfikat do zatwierdzenia (zobacz rysunek 10.7).
Teoretycznie certyfikat mona zweryfikowa w jeszcze inny sposb, ale niewielu uytkownikw ma wystarczajce umiejtnoci.
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.
525
Rysunek 10.7.
Niebezpieczny
certyfikat
W szczeglnoci aplikacja nie moe sprawdzi lokalizacji pliku. W ten sposb programista
zyskuje narzdzia do implementacji akcji otwierania i zapisywania plikw, ale tak duo informacji systemowych, jak to tylko moliwe, jest ukrytych przed niezaufanymi aplikacjami.
526
Java. Podstawy
To API udostpnia nastpujce usugi:
dostp do schowka,
drukowanie,
pobieranie plikw,
pilnowanie, aby by uruchomiony tylko jeden egzemplarz programu (od Java SE 5.0).
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 programie z listingu 10.2 przyjto nastpujc strategi dziaania:
1.
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.
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();
. . .
}
528
Java. Podstawy
Aplikacja moe sprawdzi podstaw swojego klucza URL za pomoc metody getCodeBase
z klasy BasicService.
Do tworzenia kluczy suy metoda create z interfejsu PersistenceService.
URL url = new URL(codeBase, "mykey");
service.create(url, maxSize);
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 drukowania. Pojawia si tylko okienko, w ktrym uytkownik jest proszony o zezwolenie na dostp do drukarki. Wicej informacji na temat API drukowania znajduje si w rozdziale 7. drugiego tomu.
java.awt.event.*;
java.beans.*;
java.io.*;
java.net.*;
Rozdzia 10.
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);
JMenu fileMenu = new JMenu("Plik");
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
setJMenuBar(menuBar);
JMenuItem openItem = fileMenu.add("Otwrz");
openItem.addActionListener(EventHandler.create(ActionListener.class, this,
"open"));
JMenuItem saveItem = fileMenu.add("Zapisz");
saveItem.addActionListener(EventHandler.create(ActionListener.class, this,
"save"));
}
pack();
/**
* Pobiera tytu z magazynu trwaego lub prosi uytkownika o podanie tytuu, jeli
* nie ma wczeniejszego wpisu.
*/
public void setTitle()
{
try
{
String title = null;
BasicService basic = (BasicService)
ServiceManager.lookup("javax.jnlp.BasicService");
URL codeBase = basic.getCodeBase();
PersistenceService service = (PersistenceService) ServiceManager
.lookup("javax.jnlp.PersistenceService");
URL key = new URL(codeBase, "title");
try
{
}
catch (FileNotFoundException e)
{
529
530
Java. Podstawy
title = JOptionPane.showInputDialog("Podaj tytu ramki:");
if (title == null) return;
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.
/**
* 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
URL getCodeBase()
boolean isWebBrowserSupported()
Zwraca warto true, jeli rodowisko Web Start moe uruchomi przegldark.
InputStream getInputStream()
531
532
Java. Podstawy
String getName()
boolean canRead()
boolean canWrite()
Zwraca warto true, jeli dany plik nadaje si do odczytu lub zapisu.
javax.jnlp.FileOpenService
Rozdzia 10.
533
10.3. Aplety
Aplety to programy w jzyku Java doczane do stron HTML. Strona HTML musi poinformowa 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.). Przegldarka 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 przegldarki dla jednej dodatkowej funkcji. Aplety zyskay prawdziw popularno z chwil doczenia 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 rozszerzenie, co umoliwia uruchamianie apletw przy wykorzystaniu zewntrznego rodowiska
uruchomieniowego Javy.
Aby mc uruchamia aplety opisywane w tym rozdziale, naley zainstalowa najnowsz wersj narzdzia Java Plug-in i upewni si, e przegldarka jest poczona
z wtyczk. Informacje na temat konfiguracji i pliki do pobrania mona znale na stronie http://java.com.
534
Java. Podstawy
Rysunek 10.11.
Diagram
dziedziczenia
klasy Applet
Rozdzia 10.
535
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JLabel label = new JLabel("To nie jest aplet Witaj, wiecie",
SwingConstants.CENTER);
add(label);
}
});
rozmiarze apletu.
Poniej znajduje si jego zawarto:
<applet code="applet/NotHelloWorld.class" width="300" height="300">
</applet>
Dobrym pomysem jest przetestowanie apletu we wchodzcej w skad pakietu JDK przegldarce 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 uytkownikowi. 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 poddany 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 przegldarki 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.
Rozdzia 10.
537
nie maj paskw tytuu (mona oczywicie nada tytu samej stronie HTML
za pomoc znacznika title).
8. Nie wywouj metody setVisible(true). Aplet jest wywietlany automatycznie.
java.applet.Applet 1.0
void init()
void start()
void stop()
void destroy()
Wartoci atrybutu code jest nazwa pliku klasy, koniecznie z rozszerzeniem .class. Atrybuty
width i height okrelaj rozmiar okna apletu w pikselach. Koniec znacznika wyznacza znacznik 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 wywietli apletu.
Wszystkie te informacje powinny si znajdowa w kodzie strony internetowej, ktrej minimalna tre moe wyglda nastpujco:
<html>
<head>
<title>NotHelloWorldApplet</title>
538
Java. Podstawy
</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>
width, height
align
Okrela wyrwnanie apletu. Wartoci tego atrybutu s takie same jak atrybutu
align znacznika img.
vspace, hspace
Okrelaj liczb pikseli nad i pod apletem (vspace) oraz po jego obu stronach
(hspace).
code
Okrela nazw pliku klasy apletu. Nazwa ta jest traktowana wzgldem katalogu
podstawowego (patrz niej) lub aktualnej strony, jeli katalog podstawowy nie
jest okrelony.
cieka musi si zgadza z pakietem, do ktrego naley klasa. Jeli na przykad
klasa apletu naley do pakietu com.mycompany, atrybut wyglda nastpujco
code="com/mycompany/MyApplet.class". Mona te stosowa alternatywny zapis
w postaci code="com.mycompany.MyApplet.class", ale w takim przypadku nie mona
stosowa cieek bezwzgldnych. Jeli plik klasy znajduje si gdzie indziej, naley
uy atrybutu codebase.
Atrybut code okrela tylko nazw klasy apletu. Oczywicie sam aplet moe zawiera
take inne pliki klas. Kiedy przegldarka zaaduje klas zawierajc aplet, zorientuje
si, e potrzebne s dodatkowe klasy, i je rwnie zaaduje.
Wymagany jest atrybut code lub object (zobacz poniej).
codebase
Okrela adres URL, pod ktrym naley szuka plikw klas. Adres ten moe
by bezwzgldny i prowadzi nawet do innego serwera. Najczciej jednak stosuje
si adresy wzgldne do podkatalogw na serwerze. Jeli na przykad struktura plikw
i katalogw jest nastpujca:
Rozdzia 10.
539
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
name
540
Java. Podstawy
Dziki integracji Javy i JavaScriptu w przegldarkach Netscape i Internet Explorer mona
wywoywa metody apletu:
myApplet.init();
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 kademu 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.
Na stronie http://www.javaworld.com/javatips/jw-javatip80.html Francis Lu wykorzystuje mechanizm komunikacji Javy z JavaScriptem do rozwizania odwiecznego
problemu zmiany rozmiaru apletu, na stae ustawionego przez atrybuty width i height.
Jest to dobry przykad integracji tych dwch jzykw.
alt
Rozdzia 10.
541
Metod getParameter mona wywoywa tylko w metodzie init apletu, nie w konstruktorze, 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>
542
Java. Podstawy
. . .
}
}
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 wykorzystuje parametry.
Rysunek 10.14.
Aplet
wywietlajcy
wykres
Aplet ten pobiera etykiety i wysokoci supkw z wartoci atrybutw znacznika param. Poniej 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.
<param name="value.4"
<param name="value.5"
<param name="value.6"
<param name="value.7"
<param name="value.8"
<param name="value.9"
</applet>
543
value="4200"/>
value="88000"/>
value="71000"/>
value="32000"/>
value="30600"/>
value="1430"/>
Mona byo utworzy w aplecie tablic acuchw i tablic liczb, ale wykorzystanie mechanizmu 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 zauway, e metoda init pobiera parametry, a metoda paintComponent rysuje wykres.
Listing 10.4. chart/Chart.java
package chart;
import
import
import
import
java.awt.*;
java.awt.font.*;
java.awt.geom.*;
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;
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// Obliczanie wartoci minimalnej i maksymalnej.
if (values == null) return;
double minValue = 0;
double maxValue = 0;
for (double v : values)
{
if (minValue > v) minValue = v;
if (maxValue < v) maxValue = v;
}
if (maxValue == minValue) return;
int panelWidth = getWidth();
int panelHeight = getHeight();
Font titleFont = new Font("SansSerif", Font.BOLD, 20);
Font labelFont = new Font("SansSerif", Font.PLAIN, 10);
// Obliczanie szerokoci tytuu.
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D titleBounds = titleFont.getStringBounds(title, context);
double titleWidth = titleBounds.getWidth();
double top = titleBounds.getHeight();
// Rysowanie tytuu.
double y = -titleBounds.getY();
// wysoko
double x = (panelWidth - titleWidth) / 2;
g2.setFont(titleFont);
Rozdzia 10.
java.applet.Applet 1.0
545
546
Java. Podstawy
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");
URL getDocumentBase()
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.
Rozdzia 10.
547
Zwraca obiekt typu Image zawierajcy obraz wskazywany przez adres URL. Jeli
obraz nie istnieje, zwracana jest natychmiast warto null. W przeciwnym przypadku
zostaje uruchomiony osobny wtek adujcy obraz.
Do czego mona wykorzysta takie odwoanie? Jeli klasa Chart zawiera metod przyjmujc nowe dane i ponownie rysujc wykres, metod t mona wywoa, wykonujc odpowiednie 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());
}
Z naszego dowiadczenia wynika, e zastosowanie metody showStatus jest ograniczone. Przegldarka rwnie uywa paska stanu i w wikszoci przypadkw zastpuje dostarczony przez programist tekst informacj typu Applet running. W zwizku
z tym w pasku stanu mona wywietla komunikaty typu adowanie danych, ale nie
takie, ktre s dla uytkownika wane.
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 aktualn, 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 dodatkowy 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 unikatowych nazwach, w ktrych bd wywietlane dokumenty dane przez aplet rwnie znajdujcy si w jednej z ramek. Przykadowy kod prezentujemy w kolejnym podrozdziale.
Przegldarka apletw nie wywietla stron internetowych. Metoda showDocument
jest przez ni ignorowana.
Rozdzia 10.
549
Lokalizacja
Bieca ramka
_parent
Ramka nadrzdna
_top
_blank
java.applet.Applet 1.2
Enumeration<Applet> getApplets()
550
Java. Podstawy
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");
Rozdzia 10.
551
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.
Po drugie, jeli wpisywanie wartoci domylnej w kadym wywoaniu metody getProperty
okae si zbyt mudne, wszystkie ustawienia domylne mona umieci w drugorzdnej
mapie wasnoci dostarczanej nastpnie w konstruktorze mapy gwnej.
Properties defaultSettings = new Properties();
defaultSettings.put("width", "300");
defaultSettings.put("height", "200");
defaultSettings.put("title", "Domylny tytu");
. . .
Properties settings = new Properties(defaultSettings);
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.
Listing 10.5 przedstawia program zapisujcy i adujcy ustawienia programu. Zapamitywane
s pooenie, rozmiar i tytu ramki. Wygld programu mona dostosowa wedug wasnego
uznania, edytujc plik o nazwie .corejava/program.properties znajdujcy si w katalogu
gwnym.
Listing 10.5. properties/PropertiesTest.java
package properties;
import
import
import
import
java.awt.EventQueue;
java.awt.event.*;
java.io.*;
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;
private File propertiesFile;
private Properties settings;
public PropertiesFrame()
{
// Pobranie informacji o pooeniu, rozmiarze i tytule z pliku wasnoci
String userDir = System.getProperty("user.home");
File propertiesDir = new File(userDir, ".corejava");
if (!propertiesDir.exists()) propertiesDir.mkdir();
propertiesFile = new File(propertiesDir, "program.properties");
Properties defaultSettings = new Properties();
defaultSettings.put("left", "0");
defaultSettings.put("top", "0");
defaultSettings.put("width", "" + DEFAULT_WIDTH);
defaultSettings.put("height", "" + DEFAULT_HEIGHT);
defaultSettings.put("title", "");
settings = new Properties(defaultSettings);
if (propertiesFile.exists()) try
{
FileInputStream in = new FileInputStream(propertiesFile);
settings.load(in);
}
catch (IOException ex)
{
ex.printStackTrace();
}
int left = Integer.parseInt(settings.getProperty("left"));
int top = Integer.parseInt(settings.getProperty("top"));
int width = Integer.parseInt(settings.getProperty("width"));
int height = Integer.parseInt(settings.getProperty("height"));
setBounds(left, top, width, height);
// Jeli nie ma tytuu, uytkownik zostanie poproszony o jego podanie
String title = settings.getProperty("title");
if (title.equals("")) title = JOptionPane.showInputDialog("Wpisz tytu
ramki:");
Rozdzia 10.
553
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);
}
});
Properties()
Properties(Properties defaults)
defaults
Wartoci domylne
Pobiera map wasnoci. Zwraca acuch skojarzony z kluczem key lub acuch
skojarzony z kluczem key w mapie domylnej, jeli nie ma go w aktualnej mapie,
albo warto null, jeli nie zostanie on znaleziony take w tej drugiej mapie.
Parametr:
key
554
Java. Podstawy
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
defaultValue
in
Strumie wejciowy
out
Strumie wyjciowy
header
java.lang.System 1.0
Properties getProperties()
Rozdzia 10.
555
Niektre systemy operacyjne maj centralne repozytorium, w ktrym przechowuj dane konfiguracyjne. 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 zapisywane w lokalnym systemie plikw. Oczywicie implementacja repozytorium nie ma tajemnic dla programisty uywajcego klasy Preferences.
Repozytorium Preferences ma struktur drzewiast z nazwami cieek do wzw typu
/com/mycompany/myapp. Podobnie jak w przypadku pakietw, konfliktw nazw cieek unika
si, stosujc odwrcone nazwy domen. Projektanci tego API zalecaj nawet, aby ciekom do
wzw konfiguracyjnych nadawa takie same nazwy jak pakietom uywanym w programie.
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 dugoterminowego przechowywania. Oczywicie ci, ktrzy si z tym nie zgadzaj, mog zapisywa serializowane obiekty w tablicach bajtw.
Wiksz elastyczno zapewniaj dodatkowe rwnolege drzewa. Kady uytkownik programu ma wasne drzewo i dodatkowe drzewo systemowe, ktre przechowuje ustawienia wsplne
wszystkich uytkownikw. Odpowiednie drzewo ustawie jest pobierane przez klas Prefe
rences przy uyciu pojcia biecego uytkownika, rozumianego zgodnie z systemem operacyjnym.
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
Preferences node = Preferences.userNodeForPackage(obj.getClass());
lub
Preferences node = Preferences.systemNodeForPackage(obj.getClass());
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:
Rozdzia 10.
557
Jeli program wykorzystuje klas Preferences, naley umoliwi uytkownikowi import i eksport 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.
Listing 10.6. preferences/PreferencesTest.java
package preferences;
import
import
import
import
import
java.awt.EventQueue;
java.awt.event.*;
java.io.*;
java.util.prefs.*;
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
Preferences root = Preferences.userRoot();
final Preferences node = root.node("/com/horstmann/corejava");
int left = node.getInt("left", 0);
int top = node.getInt("top", 0);
int width = node.getInt("width", DEFAULT_WIDTH);
int height = node.getInt("height", DEFAULT_HEIGHT);
setBounds(left, top, width, height);
// Jeli nie ma tytuu, uytkownik zostanie poproszony o jego podanie
String title = node.get("title", "");
if (title.equals("")) title = JOptionPane.showInputDialog("Wpisz tytu
ramki:");
if (title == null) title = "";
setTitle(title);
// Utworzenie okna wyboru plikw wywietlajcego pliki XML
final JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// Akceptacja wszystkich plikw z rozszerzeniem .xml
chooser.setFileFilter(new javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".xml") || f.isDirectory();
}
public String getDescription()
{
return "XML files";
}
});
// menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
JMenuItem exportItem = new JMenuItem("Eksport ustawie");
Rozdzia 10.
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();
}
}
}
});
JMenuItem importItem = new JMenuItem("Import ustawie");
menu.add(importItem);
importItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (chooser.showOpenDialog(PreferencesFrame.this) ==
JFileChooser.APPROVE_OPTION)
{
try
{
InputStream in = new
FileInputStream(chooser.getSelectedFile());
Preferences.importPreferences(in);
in.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
});
JMenuItem exitItem = new JMenuItem("Zamknij");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
node.putInt("left", getX());
node.putInt("top", getY());
node.putInt("width", getWidth());
node.putInt("height", getHeight());
559
560
Java. Podstawy
node.put("title", getTitle());
System.exit(0);
}
});
}
}
java.util.prefs.Preferences 1.4
Preferences userRoot()
Preferences systemRoot()
String[] keys()
Rozdzia 10.
561
562
Java. Podstawy
11
Wyjtki, dzienniki,
asercje i debugowanie
W tym rozdziale:
Obsuga bdw
Obsuga wyjtkw
Asercje
Dzienniki
Praca z debugerem
564
Java. Podstawy
Sytuacje wyjtkowe, ktre mog wywoa awari programu (jak na przykad podanie nieprawidowych 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.
Testowanie polega na przeprowadzaniu wielu rnych testw, majcych na celu sprawdzenie,
czy program dziaa zgodnie z przewidywaniami. Testy te mog jednak zabiera duo czasu,
a po zakoczeniu testowania zazwyczaj s niepotrzebne. Mona je wtedy usun i w razie
potrzeby wklei z powrotem, kiedy znw bd potrzebne. Jest to jednak mudne zajcie. Druga
cz rozdziau zostaa powicona selektywnemu uruchamianiu testw za pomoc asercji.
Komunikacja z uytkownikiem w wyjtkowej sytuacji nie zawsze jest moliwa. Czsto te
nie mona normalnie zamkn programu. Wtedy dobrym pomysem jest zapisanie danych
zdarzenia do pniejszej analizy. Trzecia cz tego rozdziau zostaa powicona technikom
zapisu danych do dziennika.
Na zakoczenie dajemy kilka wskazwek na temat wydobywania poytecznych informacji
z dziaajcego programu oraz uywania debugera w zintegrowanym rodowisku programistycznym.
Bdy urzdze. Urzdzenia nie zawsze dziaaj tak, jak powinny. Drukarka moe
by wyczona, a strona internetowa, z ktrej drukujemy, moe by chwilowo
niedostpna. Urzdzenia czsto zawodz w trakcie pracy. Na przykad w drukarce
w czasie drukowania moe si skoczy papier.
Rozdzia 11.
Bdy w kodzie. Jedna z metod moe nie dziaa prawidowo. Moe na przykad
zwraca niepoprawne wyniki albo nieprawidowo uywa innych metod. Inne
przykady bdw spowodowanych przez kod to nieprawidowe obliczenie indeksu
w tablicy i prba dostpu do nieistniejcego elementu tablicy mieszajcej czy prba
pobrania elementu z pustego stosu.
565
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 doskonay 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.
566
Java. Podstawy
niepoprawne rzutowanie,
prba znalezienia obiektu typu Class dla acucha, ktry nie zgadza si z adn
z istniejcych klas.
Rozdzia 11.
567
W specyfikacji Javy wyjtki typu Error lub RuntimeException nazywane s wyjtkami niekontrolowanymi (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.
Nazwa wyjtek wykonawczy (RuntimeException) jest nieco nieprecyzyjna, poniewa wszystkie bdy, ktre opisujemy, wystpuj w czasie dziaania programu.
568
Java. Podstawy
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 dziedzicz po klasie Error. Ich rdem moe by kady fragment kodu, przez co nie ma moliwoci sprawowania nad nimi kontroli.
Podobnie nie naley informowa o wyjtkach dziedziczcych po klasie RuntimeException:
class MyAnimation
{
. . .
void drawImage(int i) throws ArrayIndexOutOfBoundsException
{
. . .
}
}
// zy styl
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.
Podsumowujc, kada metoda musi deklarowa wszystkie wyjtki kontrolowane (ang.
checked), ktre moe zgosi. Wyjtki niekontrolowane s albo poza zasigiem programisty
Rozdzia 11.
569
(Error), albo powstaj w takich sytuacjach, do ktrych programista nie powinien dopuci
(RuntimeException). Jeli metoda nie deklaruje wszystkich wyjtkw kontrolowanych, kompilator wywietli komunikat o bdzie.
Oczywicie, jak przekonalimy si wczeniej, zamiast deklarowa wyjtek, mona go przechwyci. Wtedy nie zostanie on wyrzucony z metody, a wic nie jest potrzebna specyfikacja
throws. Dalej nauczymy si podejmowa decyzj, czy przechwyci wyjtek, czy pozwoli
zrobi to komu innemu.
Przesaniajc metod, naley pamita, e wyjtki kontrolowane deklarowane przez
metod w podklasie nie mog by bardziej oglne ni w metodzie nadklasy (metoda podklasy moe zgasza mniej oglne wyjtki lub nie zgasza adnych). Jeli metoda w nadklasie nie zgasza adnych wyjtkw kontrolowanych, metoda w podklasie
rwnie nie moe tego robi. Jeli na przykad przesonimy metod JComponent.paint
Component, metoda paintComponent w podklasie nie moe zgasza adnych wyjtkw kontrolowanych, poniewa nie robi tego metoda z nadklasy.
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.
Specyfikator throws w Javie ma prawie identyczne zastosowanie jak throw w C++.
Jedyna rnica polega na tym, e w C++ specyfikatory throw s stosowane w czasie
dziaania programu, a nie kompilacji. Oznacza to, e kompilator C++ nie zwraca uwagi
na specyfikacje wyjtkw. Jeli jednak wyjtek wystpi w funkcji nieznajdujcej si na
licie throw, wywoywana jest funkcja unexpected i program zostaje zamknity.
Ponadto w C++, jeli funkcja nie posiada adnej specyfikacji throw, moe zgosi kady
rodzaj wyjtku. W Javie metoda bez specyfikatora throws nie moe w ogle zgasza
wyjtkw kontrolowanych.
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.
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 polega na tym, e w Javie mona generowa wycznie obiekty nalece do podklas
klasy Throwable. W C++ mona zgasza wartoci dowolnego typu.
Rozdzia 11.
571
Throwable()
Throwable(String message)
String getMessage()
572
Java. Podstawy
ledzenia stosu wywoa i wracaj do ptli przetwarzajcej interfejs (dobrym rozwizaniem
podczas usuwania bdw z programu z graficznym interfejsem uytkownika jest ustawienie
konsoli w widocznym miejscu).
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 klauzuli catch, to:
1.
Jeli aden z fragmentw kodu w bloku try nie zgosi wyjtku, klauzula catch zostaje
pominita.
Jeli w ktrejkolwiek z metod zostanie wygenerowany wyjtek innego typu ni okrelony
w klauzuli catch, program natychmiast z tej metody wychodzi (moe gdzie wyej w stosie
wywoa znajduje si klauzula catch przeznaczona dla tego typu wyjtku).
Powysze informacje zilustrujemy typowym przykadem procedury odczytujcej dane:
public void read(String filename)
{
try
{
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read()) != -1)
{
przetwarzanie danych wejciowych
}
}
catch (IOException exception)
{
exception.printStackTrace();
}
}
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.
573
// Java
// 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. Przypumy 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.
575
Gdy przechwytywanych jest kilka wyjtkw, zmienn wyjtku jest final. Przykadowo 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.
576
Java. Podstawy
Czasami trzeba tylko zarejestrowa informacj o wyjtku i zgosi go ponownie bez zmieniania:
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.
Rozdzia 11.
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.
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
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.
Zdecydowanie zalecamy oddzielenie od siebie blokw try-catch i try-finally.
Dziki temu kod jest znacznie bardziej przejrzysty. Na przykad:
InputStream in = ...;
try
{
try
{
potencjalne rdo wyjtkw
}
finally
{
in.close();
}
}
catch (IOException e)
{
wywietlenie informacji o bdzie
}
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 funkcjonalne 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.
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 skomplikowa 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;
}
}
580
Java. Podstawy
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.
Mona te okreli kilka zasobw, np.:
try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")),
PrintWriter out = new PrintWriter("out.txt"))
{
while (in.hasNext())
out.println(in.next().toUpperCase());
}
Rozdzia 11.
581
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 wyjtkw metody close, wywoujc metod getSuppressed.
Nie musisz si mczy. Jeli musisz zamkn jaki zasb, zawsze uywaj instrukcji try
z zasobami.
Instrukcja try z zasobami te moe zawiera klauzule catch i finally, ktre s
wykonywane po zamkniciu zasobw. W praktyce jednak lepiej jest nie adowa
tak duo do jednej instrukcji try.
582
Java. Podstawy
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 wywoanie 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
Rozdzia 11.
factorial(n);
java.lang.Throwable 1.0
Ustawia powd cause dla obiektu lub zgasza wyjtek, jeli obiekt ten ma ju powd.
Zwraca this.
Zwraca obiekt wyjtku, ktry zosta ustawiony jako powd tego obiektu,
lub warto null, jeli aden powd nie zosta ustawiony.
void addSuppressed(Throwable t) 7
Throwable[] getSuppressed() 7
String getFileName()
583
584
Java. Podstawy
int getLineNumber()
String getClassName()
String getMethodName()
boolean isNativeMethod()
String toString()
Rozdzia 11.
585
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
3. Waciwie wykorzystuj hierarchi wyjtkw.
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.
Rozdzia 11.
587
{
InputStream in = new FileInputStream(filename);
. . .
}
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.
Lepszym wyjciem z tej sytuacji s asercje, poniewa mona je automatycznie usun po
zakoczeniu testowania.
W Javie dostpne jest sowo kluczowe assert. Istniej dwa podstawowe sposoby jego stosowania:
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 konstruktora obiektu AssertionError i zamieniane na acuch komunikatu.
588
Java. Podstawy
Makro assert w jzyku C zamienia warunek asercji w acuch, ktry jest drukowany 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 wyczone, 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
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.
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.
generowanie wyjtkw,
dzienniki,
asercje.
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.
Przeanalizujmy typowy scenariusz sprawdzanie parametrw metody. Czy do sprawdzenia
niedozwolonych indeksw lub referencji null powinnimy uy asercji? Aby odpowiedzie
na to pytanie, trzeba zajrze do dokumentacji tej metody. Zamy, e implementujemy metod
sortujc.
/**
Sortuje okrelony zakres danych wskazanej tablicy w rosncej kolejnoci liczbowej.
Pocztek sortowanego zakresu wyznacza parametr fromIndex (wcznie), a koniec parametr toIndex
(wycznie).
@param a tablica do posortowania
@param fromIndex indeks pierwszego elementu (wcznie) zakresu do posortowania
@param toIndex indeks ostatniego elementu zakresu do posortowania (wycznie)
@throws IllegalArgumentException, jeli fromIndex > toIndex
@throws ArrayIndexOutOfBoundsException, jeli fromIndex < 0 lub toIndex > a.length
*/
static void sort(int[] a, int fromIndex, int toIndex)
590
Java. Podstawy
Czy powinnimy utworzy asercj zapewniajc, e a nie ma wartoci null? Nie. Dokumentacja 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 dziaania 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.
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 testowaniu i debugowaniu. Natomiast rejestracja danych w dzienniku jest narzdziem strategicznym uywanym w caym cyklu ycia programu. Dziennikami zajmiemy si w kolejnym podrozdziale.
java.lang.ClassLoader 1.0
11.5. Dzienniki
adnemu programicie nie jest obce wstawianie instrukcji System.out.println do kodu sprawiajcego 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 najwaniejszych zalet:
Zarwno rejestratory (ang. logger), jak i procedury obsugi mog filtrowa rekordy.
Filtr odsiewa niepotrzebne dane przy uyciu kryteriw podanych przez programist.
592
Java. Podstawy
Natomiast wywoanie:
Logger.global.setLevel(Level.OFF);
Podobnie jak nazwy pakietw, nazwy rejestratorw wykazuj struktur hierarchiczn. W rzeczywistoci s one bardziej hierarchiczne ni pakiety. Pomidzy pakietem a jego przodkiem
nie ma adnych zwizkw semantycznych. Natomiast rejestratory potomne dziel pewne wasnoci ze swoimi przodkami. Jeli na przykad ustawimy priorytet informacji zapisywanych
przez rejestrator com.mycompany, rejestratory potomne odziedzicz ten priorytet.
Istnieje siedem poziomw wanoci komunikatw:
SEVERE,
WARNING,
Rozdzia 11.
INFO,
CONFIG,
FINE,
FINER,
FINEST.
593
Domylnie wczone s trzy pierwsze poziomy. Aby ustawi inny poziom, mona uy poniszej instrukcji:
logger.setLevel(Level.FINE);
W domylnym rekordzie dziennika znajduje si nazwa klasy i metody zawierajcej wywoanie rejestratora, zgodnie z danymi pobranymi ze stosu wywoa. Jeli jednak maszyna wirtualna zoptymalizuje wykonywanie, precyzyjne informacje mog by niedostpne. Aby uzyska dokadne informacje o lokalizacji klasy i metody odpowiedzialnych za to wywoanie,
mona uy metody logp. Jej sygnatura jest nastpujca:
void logp(Level l, String className, String methodName, String message)
594
Java. Podstawy
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);
}
Rozdzia 11.
595
Aby zmieni domylny poziom rejestracji, naley w pliku konfiguracyjnym zmieni poniszy
wiersz:
.level=INFO
596
Java. Podstawy
11.5.4. Lokalizacja
Czasami konieczna jest lokalizacja komunikatw dziennika, aby byy zrozumiae dla uytkownikw z rnych krajw. Lokalizacji powicilimy rozdzia 5. drugiego tomu. Tutaj krtko
opisujemy, o czym trzeba pamita przy lokalizacji komunikatw dziennika.
Dane lokalizacyjne aplikacji s przechowywane w tak zwanych pakietach lokalizacyjnych
(ang. resource boundle). Pakiet taki zawiera odwzorowania dla poszczeglnych lokalizacji
(jak Stany Zjednoczone czy Niemcy). Na przykad pakiet lokalizacyjny moe odwzorowywa
acuch reading-File na acuchy Reading file po angielsku i Achtung! Datei wird eingele
sen po niemiecku.
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 lokalizacji. 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");
Rozdzia 11.
597
Podobnie jak rejestratory, obiekty Handler maj poziomy rejestracji. Aby rekord zosta zapisany, 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 konfiguracyjnego 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 rejestratora (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.
Aby wysa rekordy do domylnego obiektu Handler zapisujcego do plikw, mona uy
poniszego kodu:
FileHandler handler = new FileHandler();
logger.addHandler(handler);
598
Java. Podstawy
Domylne zachowanie obiektu Handler zapisujcego do pliku mona zmieni za pomoc
rnych ustawie w konfiguracji menedera dziennikw (tabela 11.1) lub przy uyciu innego
konstruktora (zobacz wycigi z API na kocu tego podrozdziau).
Opis
Warto domylna
java.util.logging.FileHandler.level
Level.ALL
Handler
java.util.logging.FileHandler.append
false
Okrela, czy handler powinien
dopisywa dane do istniejcego
pliku, czy dla kadego
uruchomionego program otwiera
nowy plik
java.util.logging.FileHandler.limit
domylnego menedera
dziennikw
java.util.logging.FileHandler.pattern
%h/java%u.log
java.util.logging.FileHandler.count
1 (brak rotacji)
java.util.logging.FileHandler.filter
Brak filtru
java.util.logging.FileHandler.encoding
Kodowanie znakw
Kodowanie platformy
java.util.logging.FileHandler.formatter
Formater rekordw
java.util.logging.
XMLFormatter
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.
Dobrym pomysem jest te wczenie rotacji plikw. Pliki dziennikw s przechowywane
w kolejnoci rotacyjnej myapp.log.0, myapp.log.1, myapp.log.2 itd. Kiedy plik przekroczy
dopuszczalny rozmiar, najstarszy dziennik jest usuwany, pozostae pliki maj zmieniane nazwy
i tworzony jest nowy plik z numerem pokolenia 0.
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 rysunek 11.2).
Rozdzia 11.
599
Opis
%h
%t
%u
%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
class WindowHandler extends StreamHandler
{
. . .
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
}
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 zaimplementowaniu 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)
Rozdzia 11.
601
Dla wygody do klas, ktre robi duo zapisw, mona doda pola statyczne:
private static final Logger logger = Logger.getLogger("com.mycompany.myprog");
602
Java. Podstawy
try
{
. . .
}
catch (SomeException e)
{
logger.log(Level.FINE, "objanienie", e);
}
java.awt.*;
java.awt.event.*;
java.io.*;
java.util.logging.*;
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.
603
604
Java. Podstawy
{
logger.entering("ImageViewerFrame.FileOpenListener", "actionPerformed",
event);
// Okno wyboru plikw
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// Akceptowanie wszystkich plikw z rozszerzeniem .gif
chooser.setFileFilter(new javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".gif") ||
f.isDirectory();
}
public String getDescription()
{
return "Obrazy GIF";
}
});
// Wywietlanie okna dialogowego wyboru plikw
int r = chooser.showOpenDialog(ImageViewerFrame.this);
// 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.
{
} // nie jest wywoywana
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
public void publish(LogRecord record)
{
if (!frame.isVisible()) return;
super.publish(record);
flush();
}
}
java.util.logging.Logger 1.4
Zwraca rejestrator o podanej nazwie. Jeli rejestrator ten nie istnieje, tworzy go.
Parametry:
loggerName
bundleName
605
606
Java. Podstawy
Level getLevel()
void setLevel(Level l)
Logger getParent()
void setParent(Logger l)
Handler[] getHandlers()
Rozdzia 11.
void addHandler(Handler h)
void removeHandler(Handler h)
607
boolean getUseParentHandlers()
void setUseParentHandlers(boolean b)
Filter getFilter()
void setFilter(Filter f)
Oprnia bufor.
Filter getFilter()
void setFilter(Filter f)
Formatter getFormatter()
void setFormatter(Formatter f)
Level getLevel()
void setLevel(Level l)
ConsoleHandler()
FileHandler(String pattern)
608
Java. Podstawy
pattern
limit
count
append
java.util.logging.LogRecord 1.4
Level getLevel()
String getLoggerName()
ResourceBundle getResourceBundle()
String getResourceBundleName()
String getMessage()
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()
long getMillis()
Rozdzia 11.
609
long getSequenceNumber()
int getThreadID()
Zwraca warto true, jeli dany rekord dziennika powinien zosta zapisany.
java.util.logging.Formatter 1.4
String getHead(Handler h)
String getTail(Handler h)
lub
Logger.global.info("x=" + x);
610
Java. Podstawy
Jeli x jest liczb, zostanie przekonwertowany na acuch. Jeli jest obiektem,
zostanie wywoana na jego rzecz metoda toString(). Aby sprawdzi stan obiektu
parametru niejawnego, naley wydrukowa stan obiektu this.
Logger.global.info("this=" + this);
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
Rozdzia 11.
drukuje ich obiekty i lad stosu oraz ponownie je generuje, aby mogy odnale
swoj procedur obsugi.
try
{
. . .
}
catch (Throwable t)
{
t.printStackTrace();
throw t;
}
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
611
612
Java. Podstawy
zapis informacji w pliku dziennika
};
});
do poniszych:
[Opened
[Opened
[Opened
[Opened
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
...
/usr/local/jdk5.0/jre/lib/rt.jar]
/usr/local/jdk5.0/jre/lib/jsse.jar]
/usr/local/jdk5.0/jre/lib/jce.jar]
/usr/local/jdk5.0/jre/lib/charsets.jar]
java.lang.Object from shared objects file]
java.io.Serializable from shared objects file]
java.lang.Comparable from shared objects file]
java.lang.CharSequence from shared objects file]
java.lang.String from shared objects file]
java.lang.reflect.GenericDeclaration from shared objects file]
java.lang.reflect.Type from shared objects file]
java.lang.reflect.AnnotatedElement from shared objects file]
java.lang.Class from shared objects file]
java.lang.Cloneable from shared objects file]
-Xlint:fallthrough
-Xlint:finally
-Xlint:none
-Xlint:path
-Xlint:serial
-Xlint:unchecked
Rozdzia 11.
613
11. Maszyna wirtualna Javy moe te monitorowa aplikacje i zarzdza nimi. Polega
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.
narzdzie Swing graphics debugger. Nawet jeli nie piszemy wasnych klas
komponentw, ciekawe i ksztacce jest podejrzenie, jak dokadnie rysowana jest
Rozdzia 11.
615
DebugGraphics.LOG_OPTION
w buforze pozaekranowym.
DebugGraphics.NONE_OPTION
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.
3. Jeli chcesz otrzyma rejestr wszystkich zdarze AWT wygenerowanych w aplikacji
616
Java. Podstawy
Rozdzia 11.
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
Object proxy = Proxy.newProxyInstance(null, new Class[] {
eventSet.getListenerType() },
handler);
// Dodanie obiektu poredniego jako suchacza do komponentu
Method addListenerMethod = eventSet.getAddListenerMethod();
try
{
addListenerMethod.invoke(c, proxy);
}
catch (ReflectiveOperationException e)
{
}
// W razie wystpienia wyjtku nie dodawa suchaczy
}
}
Aby wysa zdarzenie nacinicia klawisza, naley kaza robotowi, aby zasymulowa nacinicie i zwolnienie klawisza:
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
618
Java. Podstawy
W przypadku myszy najpierw naley ni poruszy, a potem nacisn i zwolni przycisk:
robot.mouseMove(x, y);
// x i y to bezwzgldne wsprzdne piksela na ekranie.
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
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 kolejnego przycisku. Na zakoczenie symulujemy kliknicie przyciskiem myszy trzeciego przycisku (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 testowania interfejsu uytkownika. Moe natomiast suy jako podstawa narzdzia testujcego.
Profesjonalne narzdzie powinno przechwytywa, zapisywa i powtarza scenariusze zachowania uytkownika oraz znajdowa lokalizacje komponentw na ekranie, dziki czemu miejsca klikni mysz nie s wybierane na drodze prb i bdw.
Listing 11.4. robot/RobotTest.java
package robot;
import
import
import
import
java.awt.*;
java.awt.event.*;
java.awt.image.*;
javax.swing.*;
/**
* @version 1.04 2012-05-17
* @author Cay Horstmann
*/
public class RobotTest
Rozdzia 11.
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
ButtonFrame frame = new ButtonFrame();
frame.setTitle("ButtonTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
// Powizanie robota z ekranem
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
*/
619
620
Java. Podstawy
public static void runTest(Robot robot)
{
// Symulacja nacinicia spacji
robot.keyPress(' ');
robot.keyRelease(' ');
// Symulacja nacinicia klawisza Tab i spacji
robot.delay(2000);
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
robot.keyPress(' ');
robot.keyRelease(' ');
// Symulacja kliknicia mysz prawego przycisku w oknie
robot.delay(2000);
robot.mouseMove(220, 40);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
// Zrobienie zrzutu ekranu i wywietlenie obrazu
robot.delay(2000);
BufferedImage image = robot.createScreenCapture(new Rectangle(0, 0, 400, 300));
ImageFrame frame = new ImageFrame(image);
frame.setVisible(true);
}
}
/**
* 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);
JLabel label = new JLabel(new ImageIcon(image));
add(label);
}
}
java.awt.GraphicsEnvironment 1.2
Rozdzia 11.
621
GraphicsDevice getDefaultScreenDevice()
Robot(GraphicsDevice device)
key
x, y
eventMask
rect
622
Java. Podstawy
program w normalnym tempie i zatrzymuje si w punkcie wstrzymania, w ktrym programista
moe obejrze wszystkie interesujce go dane.
Listing 11.5 przedstawia celowo uszkodzon wersj programu ButtonTest z rozdziau 8.
Klikanie przyciskw w oknie niczego nie zmienia. Spjrzmy na kod kliknicie przycisku
powinno ustawia w tle kolor okrelony w nazwie przycisku.
Rozdzia 11.
623
W tak krtkim programie znalezienie bdu moe by moliwe dziki samemu przeczytaniu
kodu rdowego. Zamy jednak, e analiza kodu rdowego nie jest praktycznym rozwizaniem. Nauczysz si teraz lokalizowa bdy za pomoc debugera dostpnego w rodowisku
Eclipse.
Jeli uywasz niezalenego debugera, takiego jak JSwat (http://code.google.com/
p/jswat/), czy sdziwego i niezwykle niezdarnego jdb, musisz skompilowa swj
program z opcj -g. Na przykad:
javac -g BuggyButtonTest.java
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.
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"))
626
Java. Podstawy
12
Programowanie oglne
W tym rozdziale:
Metody oglne
Ograniczenia i braki
Typy wieloznaczne
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 testowanie implementacji zajy ekspertom okoo piciu lat.
Zalet programowania oglnego jest moliwo pisania bezpieczniejszego i atwiejszego do
odczytu kodu w porwnaniu do programw usianych zmiennymi Object i konwersjami typw.
Typy oglne s szczeglnie przydatne w klasach kolekcyjnych, jak wszdobylska ArrayList.
Typy parametryzowane, przynajmniej na pierwszy rzut oka, przypominaj szablony w C++.
Szablony w tym jzyku, podobnie jak typy parametryzowane w Javie, zostay wprowadzone
ze wzgldu na kolekcje ze cis kontrol typw. Jednak z czasem odkryto dla nich wiele
nowych zastosowa. Niewykluczone, e po przeczytaniu tego rozdziau odkryjesz wasne
nowatorskie zastosowania dla typw oglnych w swoich programach.
628
Java. Podstawy
Metoda ta ma dwie wady. Po pierwsze, kada pobierana warto musi zosta poddana rzutowaniu:
ArrayList files = new ArrayList();
. . .
String filename = (String) names.get(0);
Dziki temu kod jest mniej zawiy. Od razu wida, e dana lista tablicowa przechowuje
obiekty typu String.
Jak ju wspominalimy, w Java SE 7 i nowszych typ oglny w konstruktorze mona
opuci:
private Object[] elementData;
Rozdzia 12.
Programowanie oglne
629
Poytki pynce z tych informacji mog zosta wykorzystane take przez kompilator. Wywoanie metody get nie pociga za sob koniecznoci wykonywania konwersji, poniewa kompilator 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(". . ."));
O wiele korzystniej jest spowodowa bd kompilatora ni wyjtek konwersji w trakcie dziaania programu.
W tym tkwi atrakcyjno parametrw typowych: pozwalaj uproci kod i s bezpieczniejsze.
630
Java. Podstawy
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 zmiennych lokalnych. Na przykad:
private T first;
Rozdzia 12.
Programowanie oglne
631
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)
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 klasach 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. Kompilator 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");
Rozdzia 12.
Programowanie oglne
633
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
Rozwizanie polega na ograniczeniu T do klas, ktre implementuj interfejs Comparable
standardowy interfejs zawierajcy tylko jedn metod o nazwie compareTo. W tym celu naley
zdefiniowa ograniczenie dla zmiennej typowej T:
public static <T extends Comparable> T min(T[] a) . . .
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 koncepcji podtypw, a poza tym nie chcieli wprowadza nowego sowa kluczowego, na przykad sub.
Zmienna typowa lub typ wieloznaczny mog mie wiele ogranicze. Na przykad:
T extends Comparable & Serializable
Znakiem rozdzielajcym poszczeglne typy graniczne jest ampersand (&), poniewa przecinek oddziela zmienne typowe.
Tak samo jak w przypadku dziedziczenia, wrd nadtypw moe istnie dowolna liczba interfejsw, ale tylko jedna klasa. Jeli wrd typw granicznych znajduje si klasa, musi by ona
umieszczona na pocztku listy.
W kolejnym przykadowym programie (listing 12.2) przerobilimy metod minmax na metod
ogln. Znajduje ona najmniejsz i najwiksz warto tablicy parametryzowanej i zwraca
obiekt typu Pair<T>.
Listing 12.2. pair2/PairTest2.java
package pair2;
import java.util.*;
Rozdzia 12.
Programowanie oglne
/**
* @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),
new GregorianCalendar(1815, Calendar.DECEMBER, 10),
new GregorianCalendar(1903, Calendar.DECEMBER, 3),
new GregorianCalendar(1910, Calendar.JUNE, 22),
};
Pair<GregorianCalendar> mm = ArrayAlg.minmax(birthdays);
System.out.println("min = " + mm.getFirst().getTime());
System.out.println("max = " + mm.getSecond().getTime());
}
}
635
// G. Hopper
// A. Lovelace
// J. von Neumann
// K. Zuse
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);
}
}
636
Java. Podstawy
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).
Na przykad typ surowy odpowiadajcy typowi Pair<T> wyglda nastpujco:
public class Pair
{
private Object first;
private Object second;
public Pair(Object first, Object second)
{
this.first = first;
this.second = second;
}
public Object getFirst() { return first; }
public Object getSecond() { return second; }
public void setFirst(Object newValue) { first = newValue; }
public void setSecond(Object newValue) { second = newValue; }
}
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. Zadeklarujmy teraz nieco inny typ:
public class Interval<T extends Comparable & Serializable> implements Serializable
{
private T lower;
private T upper;
. . .
public Interval(T first, T second)
{
if (first.compareTo(second) <= 0) { lower = first; upper = second; }
else { lower = second; upper = first; }
}
}
Rozdzia 12.
Programowanie oglne
637
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.
Z czyszczeniem metod z typw generycznych wi si pewne komplikacje. Przyjrzyjmy si
poniszemu fragmentowi programu:
class DateInterval extends Pair<Date>
{
public void setSecond(Date second)
{
if (second.compareTo(getFirst()) >= 0)
super.setSecond(second);
}
. . .
}
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 klasie DateInterval
// przesania metod zdefiniowan w klasie Pair, aby wywoywaa pierwsz metod
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 wirtualnej metod identyfikuj typy parametrw i typ zwrotny. Dlatego kompilator moe utworzy kod bajtowy dwch metod rnicych si tylko typem zwrotnym, ktry zostanie prawidowo obsuony przez maszyn wirtualn.
Metody pomostowe nie ograniczaj si tylko do typw oglnych. W rozdziale 5.
zauwaylimy, e metoda przesaniajca inn metod moe mie bardziej ograniczony typ zwrotny. Na przykad:
public class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException { ... }
}
// zdefiniowana powyej
// zsyntetyzowana metoda pomostowa przesania metod Object.clone
640
Java. Podstawy
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")));
. . .
// ostrzeenie
Kompilator nie ma pewnoci, co metoda setLabelTable moe zrobi z obiektem typu Dic
tionary. Moe na przykad zamieni wszystkie klucze na acuchy, co zamaoby gwarancj, 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 przypadku 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
Rozdzia 12.
Programowanie oglne
641
// BD
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 oglnymi bdzie skutkowao zgoszeniem przez kompilator ostrzeenia, majcego na celu powiadomienie 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
// bd
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 powodowaaby 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].
Mona deklarowa tablice typw wieloznacznych, a nastpnie poddawa je rzutowaniu:
Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];
Wynik nie jest bezpieczny. Jeli zapiszesz Pair<Employee> w elemencie table[0], a nastpnie 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.
Rozdzia 12.
Programowanie oglne
643
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.
Ostrzeenie to mona stumi na dwa sposoby. Mona doda adnotacj @SuppressWarnings
("unchecked") do metody zawierajcej wywoanie addAll lub (od Java SE 7) adnotacj
@SafeVarargs do metody addAll:
@SafeVarargs
public static <T> void addAll(Collection<T> coll, T... ts)
// bd
644
Java. Podstawy
Niestety istniej pewne komplikacje. Nie mona uy takiego wywoania:
first = T.class.newInstance();
// bd
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 egzemplarzem (i to jedynym) klasy Class<String>. Dlatego metoda makePair moe wywnioskowa
typ pary, ktr tworzy.
Nie mona utworzy tablicy generycznej:
public static <T extends Comparable> T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // bd
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.
. . .;
return (T[]) mm;
Programowanie oglne
645
Wyjtek ClassCastException jest generowany, kiedy referencja do typu Object[] jest rzutowana na typ Comparable[] podczas zwracania wartoci przez metod.
W takiej sytuacji mona skorzysta z refleksji i wywoa metod Array.newInstance:
public static <T extends Comparable> T[] minmax(T[] a)
{
T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType(), 2);
. . .
}
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.
// bd
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(...)
}
}
// OK
Przypumy, e metoda ta znajduje si w klasie Block. Gdy zastosujemy ponisze wywoanie, kompilator uzna, e t staje si wyjtkiem niekontrolowanym.
Block.<RuntimeException>throwAs(t);
Rozdzia 12.
Programowanie oglne
647
Ponisza konstrukcja zamienia wszystkie wyjtki w takie, ktre dla kompilatora s niekontrolowane:
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;
}
648
Java. Podstawy
}
.toThread().start();
}
}
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 wymazywanie typw bya moliwa, klasa lub zmienna typowa nie moe by w jednym czasie podtypem dwch interfejsw bdcych rnymi wersjami parametrycznymi tego samego interfejsu. Na przykad poniszy kod jest zy:
class Calendar implements Comparable<Calendar> { . . . }
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>
{ . . . }
// bd
Rozdzia 12.
Programowanie oglne
649
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>>
// bd
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).
650
Java. Podstawy
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<Employee> employeeBuddies = managerBuddies;
// niedozwolone, ale zamy, e tak
employeeBuddies.setFirst(lowlyEmployee);
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>.
Wanie zostaa opisana wana rnica pomidzy typami oglnymi i tablicami
w Javie. Tablic Manager[] mona przypisa do zmiennej typu Employee[]:
Manager[] managerBuddies = { ceo, cfo };
Employee[] employeeBuddies = managerBuddies;
// OK
Tablice jednak maj specjaln ochron. Prba zapisu zwykego pracownika w employeeBuddies[] zakoczy si wygenerowaniem przez maszyn wirtualn wyjtku ArrayStoreException.
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 pobierze 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> implementuje 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.
Rozdzia 12.
Programowanie oglne
651
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;
wildcardBuddies.setFirst(lowlyEmployee);
// OK
// bd kompilacji
Uszkodzenie jest niemoliwe. Wywoanie metody setFirst spowodowao bd nieprawidowego typu. Aby dowiedzie si dlaczego, szczegowo przeanalizujemy typ Pair<? extends
Employee>. Jego metody s nastpujce:
? extends Employee getFirst()
void setFirst(? extends Employee)
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.
Jest to kluczowa cecha typw wieloznacznych z ograniczeniami. Teraz dysponujemy moliwoci rozrnienia bezpiecznych metod akcesora i niebezpiecznych metod mutatora.
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
Do czego moe si to przyda? Typy wieloznaczne z ograniczeniami nadtypw s przeciwiestwem typw wieloznacznych opisanych w podrozdziale 12.8, Typy wieloznaczne.
Mona przekazywa do metod parametry, ale nie mona uywa ich wartoci zwrotnych.
Na przykad klasa Pair<? super Manager> zawiera nastpujce metody:
void setFirst(? super Manager)
? super Manager getFirst()
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 kierownicy 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);
}
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 napisa 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 implementuje interfejs Comparable<Calendar>. Dlatego klasa GregorianCalendar implementuje interfejs Comparable<Calendar>, a nie Comparable<GregorianCalendar>.
W takiej sytuacji na ratunek przychodz nadtypy:
public static <T extends Comparable<? super T>> T min(T[] a) . . .
Moe ona przyjmowa obiekty typu T jeli T jest na przykad GregorianCalendar, albo nadtypu 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 zainteresowani 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 przeklinali 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 pomidzy typami Pair<?> i Pair: metod setObject surowej klasy Pair mona wywoa z dowolnym
obiektem typu Object.
Mona zastosowa wywoanie setFirst(null).
Do czego moe si przyda taki typ? Znajduje on zastosowanie w bardzo prostych dziaaniach. 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.
Metod swapHelper moemy wywoa w metodzie swap:
public static void swap(Pair<?> p) { swapHelper(p); }
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.
Chwytanie typu wieloznacznego jest dozwolone tylko w cile okrelonych warunkach.
Kompilator musi by w stanie zagwarantowa, e symbol wieloznaczny reprezentuje jeden
okrelony typ. Na przykad T w ArrayList<Pair<T>> nie moe uchwyci typu wieloznacznego w ArrayList<Pair<?>>. Lista ta moe zawiera dwa typy Pair<?>, a symbol ? w kadym
z nich moe reprezentowa inny typ.
Program przedstawiony na listingu 12.3 demonstruje zastosowanie w praktyce omwionych
do tej pory technik.
Listing 12.3. pair3/PairTest3.java
package pair3;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest3
{
public static void main(String[] args)
Rozdzia 12.
{
Programowanie oglne
class PairAlg
{
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}
public static void swap(Pair<?> p) { swapHelper(p); }
public static <T> void swapHelper(Pair<T> p)
{
657
658
Java. Podstawy
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
}
Metoda newInstance zwraca egzemplarz tej klasy utworzony za pomoc konstruktora domylnego. 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 podtypem 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.
Metody getConstructor i getDeclaredConstructor zwracaj obiekt typu Constructor<T>. Klasa
Constructor rwnie jest ju oglna, dlatego jej metoda newInstance ma odpowiedni typ
zwrotny.
java.lang.Class<T> 1.0
T newInstance() 5.0
Zwraca obj, jeli jest null, lub moe by przekonwertowany na typ T, w przeciwnym
przypadku generuje wyjtek BadCastException.
Zwraca tablic zapenion wartociami, jeli T jest typem wyliczeniowym, lub null
w przeciwnym przypadku.
Rozdzia 12.
Programowanie oglne
659
Zwraca nadklas tej klasy lub null, jeli T nie jest w ogle klas lub jest klas Object.
660
Java. Podstawy
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.
Informacje o typach zawarte w plikach klas, ktre umoliwiaj stosowanie refleksji
wzgldem typw oglnych, nie s zgodne ze starszymi wersjami maszyny wirtualnej.
W pakiecie java.lang.reflect znajduje si interfejs o nazwie Type, ktrego celem jest informowanie o deklaracjach typw oglnych. Interfejs ten ma nastpujce podtypy:
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.
Rozdzia 12.
Programowanie oglne
661
662
Java. Podstawy
}
public static void printMethod(Method m)
{
String name = m.getName();
System.out.print(Modifier.toString(m.getModifiers()));
System.out.print(" ");
printTypes(m.getTypeParameters(), "<", ", ", "> ", true);
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);
}
public static void printType(Type type, boolean isDefinition)
{
if (type instanceof Class)
{
Class<?> t = (Class<?>) type;
System.out.print(t.getName());
}
else if (type instanceof TypeVariable)
{
TypeVariable<?> t = (TypeVariable<?>) type;
System.out.print(t.getName());
if (isDefinition)
printTypes(t.getBounds(), " extends ", " & ", "", false);
}
else if (type instanceof WildcardType)
{
WildcardType t = (WildcardType) type;
System.out.print("?");
printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
printTypes(t.getLowerBounds(), " super ", " & ", "", false);
}
else if (type instanceof ParameterizedType)
{
ParameterizedType t = (ParameterizedType) type;
Type owner = t.getOwnerType();
if (owner != null)
Rozdzia 12.
Programowanie oglne
663
{
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
Zwraca zmienne typowe typu oglnego, jeli typ ten zosta zadeklarowany
jako oglny, lub tablic o dugoci 0 w przeciwnym przypadku.
Zwraca typ oglny nadklasy, ktra zostaa zadeklarowana dla tego typu, lub null,
jeli typem tym jest Object albo typ niebdcy klas.
Zwraca typy oglne interfejsw, ktre zostay zadeklarowane dla tego typu,
przy zachowaniu kolejnoci z deklaracji, lub tablic o dugoci 0, jeli typ ten
nie implementuje interfejsw.
java.lang.reflect.Method 1.1
664
Java. Podstawy
java.lang.reflect.TypeVariable 5.0
String getName()
Type[] getBounds()
Type[] getLowerBounds()
Type[] getUpperBounds()
Type getRawType()
Type[] getActualTypeArguments()
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()
13
Kolekcje
W tym rozdziale:
Interfejsy kolekcyjne
Architektura kolekcji
Algorytmy
Stare kolekcje
Dobr struktur danych do uycia w programie moe mie niebagatelne znaczenie dla pniejszej implementacji metod i wydajnoci caej aplikacji. Decydujc si na konkretne struktury, naley odpowiedzie sobie na pytania typu: czy konieczne bdzie przeszukiwanie tysicy
(moe nawet milionw) posortowanych elementw? Czy konieczne bdzie szybkie wstawianie 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 informatycznych przedmiot dotyczcy struktur danych trwa z reguy jeden semestr, dziki czemu
ta wana tematyka zostaa wyczerpujco przedstawiona w bardzo licznych publikacjach i opracowaniach. 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.
666
Java. Podstawy
Byo to z pewnoci mdre posunicie ze strony projektantw jzyka opracowanie penej
biblioteki klas kolekcyjnych wymaga czasu i dowiadczenia.
Projektanci doszli do wniosku, e czas na zaprezentowanie penego zestawu struktur danych
nadszed w Java 1.2. Musieli dokona wielu trudnych wyborw, poniewa chcieli stworzy
bibliotek majc niewielkie rozmiary i atw do opanowania dla programistw. Starali si
unikn poziomu zoonoci charakteryzujcego bibliotek C++ STL (ang. Standard Template
Library), jednoczenie prbujc skorzysta z dobrodziejstw algorytmw uoglnionych, ktrych pionierem bya wanie wymieniona biblioteka. Stare klasy musiay pasowa do nowej
architektury. Jak to zwykle bywa przy projektowaniu bibliotek kolekcji, kilkakrotnie stawano
przed trudnym wyborem, co zaowocowao kilkoma osobliwymi decyzjami projektowymi.
W tym podrozdziale przedstawiamy podstawow struktur architektury kolekcji Javy, pokazujemy sposoby jej wykorzystania oraz wyjaniamy, czym kierowali si projektanci, podejmujc niektre bardziej kontrowersyjne decyzje.
Sam interfejs nie zawiera adnych danych na temat implementacji kolejki. Z dwch najczciej spotykanych implementacji kolejki jedna dziaa na zasadzie listy cyklicznej, a druga listy
powizanej (rysunek 13.2).
Rozdzia 13.
Kolekcje
667
// klasa niebiblioteczna
// klasa niebiblioteczna
668
Java. Podstawy
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 adnych informacji na temat wydajnoci. Lista cykliczna jest nieco szybsza od listy powizanej, 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 okrelimy 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 bibliotek. W razie (mao prawdopodobnej) potrzeby zaimplementowania wasnej klasy kolejki atwiej
rozszerzy klas AbstractQueue, ni zaimplementowa wszystkie metody interfejsu Queue.
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 realizuje si za pomoc ptli w stylu for each:
for (String element : c)
{
obrbka elementu
}
Interfejs Collection rozszerza interfejs Iterable. Dziki temu ptli for each mona uywa
do dziaa na wszystkich kolekcjach ze standardowej biblioteki.
Kolejno odwiedzania elementw zaley od rodzaju kolekcji. W przypadku listy ArrayList
iterator zaczyna od indeksu o numerze 0 i zwiksza ten numer w kadym kolejnym powtrzeniu (iteracji). Natomiast dostp do elementw w zbiorze HashSet odbywa si w zasadzie
670
Java. Podstawy
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 problemu, 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 interfejsu Iterator speniaj t sam funkcj co nextElement i hasMoreElements
w interfejsie Enumeration. Projektanci biblioteki kolekcyjnej mogli wykorzysta w swojej pracy interfejs Enumeration, ale nie podobay im si niezgrabne nazwy jego metod.
Dlatego utworzono nowy interfejs z krtszymi nazwami metod.
Pomidzy iteratorami w bibliotece kolekcyjnej w Javie a iteratorami z innych bibliotek istnieje wana rnica koncepcyjna. Modelem iteratorw w tradycyjnych bibliotekach, takich jak
Standard Template Library w C++, s indeksy tablicowe. Za pomoc takiego tradycyjnego
iteratora element znajdujcy si w okrelonym miejscu mona odszuka w podobny sposb
jak element a[i] w tablicy, jeli znany jest indeks i. Niezalenie od wyszukiwania, iterator
taki mona przesun do kolejnego elementu, co niczym si nie rni od operacji zwikszania indeksu za pomoc instrukcji i++, bez wyszukiwania. Iteratory w Javie dziaaj nieco
inaczej. Wyszukiwanie i zmiana pooenia s ze sob cile zwizane. Jedynym sposobem
na znalezienie elementu jest wywoanie metody next, a to powoduje przejcie do kolejnego
elementu.
Iteratory w Javie naley wyobraa sobie jako obiekty znajdujce si pomidzy elementami. W chwili wywoania metody next iterator przeskakuje kolejny element i zwraca referencj do elementu, ktry wanie przeskoczy (zobacz rysunek 13.3).
Istnieje jeszcze inna ciekawa analogia. Instrukcj Iterator.next mona traktowa
jako odpowiednik instrukcji InputStream.read. Odczyt bajta ze strumienia automatycznie oznacza jego poknicie. Kolejne wywoanie metody read powoduje poknicie i zwrcenie nastpnego bajta z danych wejciowych. W podobny sposb seria
wywoa metody next zwraca wszystkie elementy znajdujce si w kolekcji.
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 rzuceniem 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 przydatne, 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
W rzeczywistoci w interfejsie Collection znajduje si spora liczba przydatnych metod,
ktre musz by udostpniane przez wszystkie implementujce go klasy. Nale do nich:
int size()
boolean isEmpty()
boolean contains(Object obj)
boolean containsAll(Collection<?> c)
boolean equals(Object other)
boolean addAll(Collection<? extends E> from)
boolean remove(Object obj)
boolean removeAll(Collection<?> c)
void clear()
boolean retainAll(Collection<?> c)
Object[] toArray()
<T> T[] toArray(T[] arrayToFill)
Przeznaczenie wielu z tych metod atwo odgadn po nazwie. Peny ich opis znajduje si na
kocu podrozdziau w wycigach z API.
Oczywicie definiowanie tylu metod we wszystkich klasach implementujcych interfejs
Collection jest bardzo uciliwe. Aby uatwi ycie programistom, utworzono klas
AbstractCollection implementujc wszystkie metody tego interfejsu w kategorii metod
size i iterator, ktre jako jedyne pozostay w niej abstrakcyjne. Na przykad:
public abstract class AbstractCollection<E>
implements Collection<E>
{
. . .
public abstract Iterator<E> iterator();
public boolean contains(Object obj)
{
for (E element : c)
// wywouje metod iterator()
if (element.equals(obj))
return = true;
return false;
}
. . .
}
Iterator<E> iterator()
Rozdzia 13.
Kolekcje
int size()
boolean isEmpty()
Zwraca warto true, jeli kolekcja zawiera obiekt identyczny z obiektem obj.
Usuwa obiekt obj z kolekcji. Zwraca warto true, jeli obiekt zosta znaleziony
i usunity.
void clear()
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()
673
674
Java. Podstawy
java.util.Iterator<E> 1.2
boolean hasNext()
E next()
void remove()
Rozdzia 13.
Kolekcje
675
Opis
ArrayList
LinkedList
ArrayDeque
HashSet
TreeSet
Uporzdkowany zbir
EnumSet
LinkedHashSet
PriorityQueue
HashMap
TreeMap
EnumMap
LinkedHashMap
WeakHashMap
Mapa, ktrej wartoci mog zosta usunite przez system zbierania nieuytkw,
jeli nie s uywane gdzie indziej
IdentityHashMap
Rysunek 13.4.
Usuwanie
elementu
z tablicy
Wiele osb ukoczyo kurs struktur danych, na ktrym uczy si implementacji list powizanych. 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>();
staff.add("Ania");
staff.add("Bartek");
676
Java. Podstawy
Rozdzia 13.
Kolekcje
677
Metoda previous, podobnie jak next, zwraca obiekt, ktry wanie przeskoczya.
Metoda listIterator z klasy LinkedList zwraca obiekt iteratora implementujcy interfejs
ListIterator.
ListIterator<String> iter = staff.listIterator();
Metoda add dodaje element przed iteratorem. Na przykad ponisze instrukcje pomijaj pierwszy 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 kolejnoci podawania. Kady z nich jest dodawany przed aktualnym miejscem pobytu iteratora.
Jeli metoda add zostanie uyta z nieuywanym jeszcze iteratorem utworzonym przez metod 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 wstawia 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|
Rozdzia 13.
Kolekcje
679
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 powizanych zostay zaprojektowane w taki sposb, aby wykrywa tego typu modyfikacje. Jeli iterator odkryje, e jego kolekcja zostaa zmodyfikowana przez inny iterator lub metod samej
kolekcji, wyrzuca wyjtek ConcurrentModificationException. Przeanalizujmy poniszy przykadowy kod:
List<String> list = . . .;
ListIterator<String> iter1 = list.listIterator();
ListIterator<String> iter2 = list.listIterator();
iter1.next();
iter1.remove();
iter2.next();
// wyrzuca wyjtek ConcurrentModificationException
Wywoanie metody iter2.next powoduje wyjtek ConcurrentModificationException, poniewa iterator iter2 odkry, e lista zostaa zmodyfikowana przez jaki czynnik zewntrzny.
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. Alternatywnie jeden z nich moe by zarwno do odczytu, jak i zapisu.
Jednoczesne operacje modyfikujce s wykrywane w bardzo prosty sposb. Kolekcja pamita
liczb operacji modyfikujcych (takich jak dodawanie i usuwanie elementw). Kady iterator
pamita, ile tego typu operacji wykona. Przed przystpieniem do dziaania iterator sprawdza,
czy jego liczba zgadza si z liczb kolekcji. Jeli nie, zgasza wyjtek ConcurrentModifica
tionException.
Od operacji wykrywania jednoczesnych operacji modyfikujcych istnieje jeden ciekawy wyjtek. Lista powizana zapamituje tylko zmiany struktury, jak dodawanie i
usuwanie ogniw. Metoda set nie liczy si jako modyfikacja strukturalna. W licie powizanej moe by kilka iteratorw zmieniajcych zawarto ogniw za pomoc metody set.
To zachowanie jest potrzebne w kilku algorytmach w klasie Collections, ktre opisujemy dalej.
Poznalimy wszystkie podstawowe metody klasy LinkedList. Do przemierzania jej w dowolnym kierunku i dodawania oraz usuwania elementw suy obiekt klasy ListIterator.
Wiemy ju z poprzedniego podrozdziau, e wiele przydatnych metod do dziaa na listach
powizanych znajduje si w interfejsie Collection. Wikszo z nich jest zdefiniowana w nadklasie AbstractCollection klasy LinkedList. Na przykad metoda toString wywouje metod
toString na rzecz kadego elementu i tworzy jeden dugi acuch w formacie [A, B, C], co
680
Java. Podstawy
przydaje si podczas debugowania. Aby sprawdzi, czy na licie znajduje si okrelony element, 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 wybranego 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);
Interfejs Iterator listy zawiera take metod informujc o aktualnym pooeniu iteratora.
W rzeczywistoci, ze wzgldu na to, e iteratory w Javie wskazuj miejsce pomidzy elementami, istniej dwie takie metody. Metoda nextIndex zwraca indeks cakowitoliczbowy elementu, 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 obawia 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.
Zalecamy unikanie wszystkich metod okrelajcych pooenie na licie za pomoc indeksw.
Aby mie wolny dostp do wszystkich elementw kolekcji, naley uywa obiektw Array
List zamiast list powizanych.
Rozdzia 13.
Kolekcje
681
Program przedstawiony na listingu 13.1 demonstruje praktyczne zastosowanie list powizanych. Tworzy on dwie listy, scala je, usuwa co drugi element z drugiej z nich, a na kocu
testuje dziaanie metody removeAll. Zachcamy do przeledzenia przepywu sterowania w
tym programie i przyjrzenia si iteratorom. Pomocne mog si okaza rysunki przedstawiajce pooenie iteratorw:
|ACE |BDFG
A|CE |BDFG
AB|CE B|DFG
...
682
Java. Podstawy
{
}
}
System.out.println(b);
// Usunicie wszystkich sw znajdujcych si w licie b z listy a
a.removeAll(b);
System.out.println(a);
}
}
Warto zauway, e instrukcja System.out.println(a) drukuje wszystkie elementy listy powizanej a za pomoc metody toString z klasy AbstractCollection.
java.util.List<E> 1.2
ListIterator<E> listIterator()
Zwraca iterator listy, ktrego pierwsze wywoanie metody next zwrci element
znajdujcy si pod podanym indeksem.
E remove(int i)
E get(int i)
E set(int i, E element)
Rozdzia 13.
Kolekcje
java.util.ListIterator<E> 1.2
Zastpuje ostatni element odwiedzony przez metod next lub previous nowym
elementem. Zgasza wyjtek IllegalStateException, jeli lista zostaa
zmodyfikowana od czasu ostatniego wywoania metody next lub previous.
boolean hasPrevious()
E previous()
int nextIndex()
int previousIndex()
LinkedList()
E getFirst()
E getLast()
E removeFirst()
E removeLast()
683
684
Java. Podstawy
Kod mieszajcy
Lee
76268
lee
107020
eel
100300
Rozdzia 13.
Kolekcje
685
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 okreli 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 nazywamy 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 identycznych 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. Niektrzy 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 automatyczna reorganizacja, w wyniku ktrej tworzona jest tablica o dwukrotnie wikszej liczbie
komrek. W wikszoci zastosowa najlepiej pozostawi wspczynnik 0,75 bez zmian.
Za pomoc tablic mieszajcych mona zaimplementowa kilka bardzo wanych struktur
danych. Najprostsz z nich jest zbir (ang. set). Zbir to kolekcja unikatowych elementw.
Metoda add zbioru najpierw sprawdza, czy w zbiorze nie ma wstawianego obiektu, i wstawia
go, jeli nic nie znajdzie.
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 dodawany 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 porozrzucane po tablicy, iterator odwiedza je w losowej kolejnoci. Dlatego zbioru HashSet naley
uywa wycznie wwczas, gdy kolejno elementw w kolekcji nie jest wana.
Przykadowy program zaprezentowany na kocu tego podrozdziau (listing 13.2) wczytuje
sowa ze strumienia System.in, dodaje je do zbioru, a nastpnie drukuje je na ekranie.
Mona na przykad wprowadzi tekst Alice in Wonderland (ktry mona pobra ze strony
http://www.gutenberg.net) za pomoc poniszego polecenia:
java SetTest < alice30.txt
Rozdzia 13.
Kolekcje
687
Naley zachowa ostrono przy modyfikowaniu elementw zbioru. Jeli kod mieszajcy elementu zmieni si, element ten bdzie si znajdowa w niewaciwym
miejscu w strukturze danych.
java.util.HashSet<E> 1.2
HashSet()
HashSet(int initialCapacity)
int hashCode()
688
Java. Podstawy
Wartoci zostan wydrukowane w kolejnoci alfabetycznej: Ania, Bartek, Karol. Jak wskazuje 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 Leiserson, Ronald Rivest i Clifford Stein, WNT, Warszawa 2007). Kady nowy element jest umieszczany w odpowiednim miejscu zgodnie z kolejnoci. Dziki temu elementy odwiedzane przez
iterator s zawsze posortowane.
Operacja dodawania elementw do drzewa jest wolniejsza ni w przypadku tablicy mieszajcej, ale i tak znacznie szybsza ni dodawanie ich w odpowiednim miejscu tablicy lub listy
powizanej. Jeli drzewo zawiera n elementw, znalezienie odpowiedniego pooenia dla nowego
elementu wymaga okoo log2n testw. Jeli na przykad drzewo zawiera 1000 elementw,
dodanie nowego elementu wie si z przeprowadzeniem okoo 10 sprawdze.
W zwizku z tym dodawanie elementw do zbioru TreeSet odbywa si nieco wolniej ni
do zbioru HashSet (tabela 13.3 przedstawia dane porwnawcze) ale TreeSet automatycznie
sortuje elementy.
Tabela 13.3. Dodawanie elementw do zbiorw HashSet i TreeSet
Dokument
Liczba wszystkich sw
Liczba unikatowych sw
HashSet
TreeSet
Alice in Wonderland
28 195
5909
5 sekund
7 sekund
46 630
37 545
75 sekund
98 sekund
java.util.TreeSet<E> 1.2
TreeSet()
Rozdzia 13.
Kolekcje
689
Porwnywanie dwch cakowitych liczb dodatnich, takich jak numery seryjne, mona zaimplementowa, wykorzystujc ich rnic. Jeli rnica jest ujemna, pierwszy element powinien 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.
Niestety zastosowanie interfejsu Comparable do definicji kolejnoci sortowania jest obwarowane ograniczeniami. Jedna klasa moe implementowa go tylko jeden raz. Co w takim
razie zrobi, jeli w jednej kolekcji elementy musz by posortowane wedug numerw seryjnych, a w innej wedug opisw? Dodatkow trudnoci s obiekty klas, ktrych twrcy
nie zadali sobie trudu implementacji interfejsu Comparable.
W tego rodzaju sytuacjach naley zmusi zbir TreeSet do uycia rnych metod porwnujcych, przekazujc obiekt Comparator do konstruktora TreeSet. Interfejs Comparator zawiera metod compare z dwoma parametrami jawnymi:
690
Java. Podstawy
public interface Comparator<T>
{
int compare(T a, T b);
}
Metoda compare, podobnie jak compareTo, zwraca ujemn liczb cakowit, jeli a wystpuje 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 porwnywane za pomoc tego komparatora.
Naley zauway, e komparator ten nie zawiera adnych danych, a jedynie metod porwnujc. Tego typu obiekty czasami nazywane s obiektami funkcyjnymi.
Obiekty funkcyjne s czsto definiowane w locie jako egzemplarze anonimowych klas
wewntrznych:
SortedSet<Item> sortByDescription = new TreeSet<Item>(new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});
Rozdzia 13.
Kolekcje
691
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 automatycznie 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.
Przeanalizujmy na przykad przypadek zbioru prostoktw. Jeli uyjemy zbioru TreeSet,
musimy dostarczy obiekt Comparator<Rectangle>. Powstaje pytanie, jakie kryteria zastosowa do porwnywania tych figur. Mona na przykad porwnywa pola powierzchni, ale to
nie jest dobre rozwizanie, poniewa dwa inaczej wygldajce prostokty o innych wsprzdnych mog mie takie samo pole. Zbir TreeSet musi by zorganizowany w porzdku
liniowym. Musi istnie moliwo porwnania dowolnych dwch elementw zbioru, a wynikiem porwnywania, jeli obiekty s rwne, musi by zero. Istnieje porzdek, ktry nadaje
si do zastosowania z prostoktami (porzdek leksykograficzny dla wsprzdnych), ale jest
on nienaturalny i sprawia trudnoci obliczeniowe. Funkcja mieszajca jest ju natomiast
zdefiniowana w klasie Rectangle. Jej dziaanie polega na mieszaniu wsprzdnych.
Od Java SE 6.0 klasa TreeSet implementuje interfejs NavigableSet. Znajduje
si w nim kilka bardzo przydatnych metod sucych do lokalizowania elementw
i przemierzania zbiorw wstecz. Szczegowe informacje na ten temat znajduj si
w wycigach z API.
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 listingu 13.4 przedstawiona jest klasa Item.
Listing 13.3. treeSet/TreeSetTest.java
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
parts.add(new Item("Modem", 9912));
System.out.println(parts);
SortedSet<Item> sortByDescription = new TreeSet<>(new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});
sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}
}
Rozdzia 13.
Kolekcje
693
Porwnuje dwa obiekty i zwraca warto ujemn, jeli pierwszy z nich znajduje
si przed drugim, zero, jeli s identyczne, lub warto dodatni, jeli pierwszy
z nich wystpuje za drugim.
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
E first()
E last()
694
Java. Podstawy
java.util.NavigableSet<E> 6
E higher(E value)
E lower(E value)
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()
TreeSet()
Tworzy zbir TreeSet i sortuje jego elementy przy uyciu okrelonego komparatora.
Rozdzia 13.
Kolekcje
java.util.Queue<E> 5.0
Wstawia element na kocu kolejki i zwraca warto true, jeli kolejka nie jest
pena. Jeli w kolejce nie ma miejsca, pierwsza z powyszych metod zgasza
wyjtek IllegalStateException, a druga warto false.
E remove()
E poll()
Usuwa i zwraca element znajdujcy si na czole kolejki, jeli nie jest ona pusta.
Jeli kolejka jest pusta, pierwsza z powyszych metod zgasza wyjtek
NoSuchElementException, a druga warto null.
E element()
E peek()
Zwraca element znajdujcy si na czole kolejki (ale go nie usuwa), pod warunkiem
e kolejka nie jest pusta. Jeli kolejka jest pusta, pierwsza z powyszych metod
zgasza wyjtek NoSuchElementException, a druga warto null.
java.util.Deque<E> 6
E removeFirst()
E removeLast()
E pollFirst()
E pollLast()
Usuwa i zwraca element znajdujcy si na czole kolejki, jeli nie jest ona pusta.
Jeli kolejka jest pusta, pierwsze dwie z powyszych metod zgaszaj wyjtek
NoSuchElementException, a pozostae dwie zwracaj warto null.
E getFirst()
E getLast()
E peekFirst()
695
696
Java. Podstawy
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)
Rozdzia 13.
Kolekcje
697
PriorityQueue()
PriorityQueue(int initialCapacity)
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 wykonywany rodzaj wyszukiwania zazwyczaj do dyspozycji jest jaki rodzaj informacji kluczowej i zadanie polega na znalezieniu zwizanego z ni elementu. Tego typu struktury realizowane 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.
W bibliotece Javy znajduj si dwie implementacje map oglnego przeznaczenia: HashMap
i TreeMap. Obie te klasy implementuj interfejs Map.
Struktura HashMap miesza klucze, natomiast w strukturze TreeMap klucze s zorganizowane
w drzewie poszukiwa posortowanym w porzdku liniowym. Funkcja mieszajca lub porwnujca jest wywoywana wycznie na rzecz kluczy. Wartoci zwizane z tymi kluczami nie
s mieszane ani porwnywane.
698
Java. Podstawy
Ktr struktur wybra: HashMap czy TreeMap? Podobnie jak ze zbiorami, mieszanie jest
nieco szybsze i naley je wybiera, gdy kolejno kluczy nie ma znaczenia.
Poniej tworzona jest struktura HashMap do przechowywania danych pracownikw:
Map<String, Employee> staff = new HashMap<String, Employee>(); // Klasa HashMap
// implementuje interfejs Map
Employee harry = new Employee("Henryk Kwiatek");
staff.put("987-98-9996", harry);
. . .
Dla kadego obiektu dodawanego do mapy HashMap naley poda jego klucz. W tym przypadku 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 warto 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.
Do usuwania elementw z okrelonym kluczem z mapy suy metoda remove. Natomiast
metoda size zwraca liczb elementw znajdujcych si w mapie.
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.
Mona na przykad sporzdzi list wszystkich kluczy znajdujcych si w mapie:
Set<String> keys = map.keySet();
for (String key : keys)
{
dziaania na kluczu
}
Rozdzia 13.
Kolekcje
699
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 pozbawione sensu.
700
Java. Podstawy
// wyszukanie wartoci
System.out.println(staff.get("157-62-7935"));
// iteracja przez wszystkie pozycje
for (Map.Entry<String, Employee> entry : staff.entrySet())
{
String key = entry.getKey();
Employee value = entry.getValue();
System.out.println("key=" + key + ", value=" + value);
}
}
}
java.util.Map<K, V> 1.2
V get(K key)
Zwraca warto skojarzon z kluczem key. Jeli nie znajdzie klucza w mapie,
zwraca warto null. Klucz moe mie warto null.
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.
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()
Collection<V> values()
Rozdzia 13.
Kolekcje
K getKey()
V getValue()
V setValue(V newValue)
HashMap()
HashMap(int initialCapacity)
Zwraca komparator uyty do sortowania kluczy lub warto null, jeli klucze
s porwnywane za pomoc metody compareTo z interfejsu Comparable.
K firstKey()
K lastKey()
701
702
Java. Podstawy
Rozdzia 13.
Kolekcje
703
Porzdek wedug kolejnoci dostpu znajduje zastosowanie w usuwaniu najduej nieuywanych elementw z pamici podrcznej. Na przykad czsto uywane obiekty mog by przechowywane w pamici, a rzadziej uywane w bazie danych. Jeli nie uda si znale jakiego
obiektu w tablicy i jest ona zapeniona, mona do niej wpuci iterator, ktry usunie kilka
pierwszych elementw. Bd to te obiekty, ktre byy uywane w najdalszej przeszoci.
704
Java. Podstawy
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 podrczna 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;
}
};
Rozdzia 13.
Kolekcje
705
WeakHashMap()
WeakHashMap(int initialCapacity)
LinkedHashSet()
LinkedHashSet(int initialCapacity)
LinkedHashMap()
LinkedHashMap(int initialCapacity)
706
Java. Podstawy
Mona jednak j przedefiniowa, aby selektywnie zwracaa warto true,
na przykad gdy najstarsza pozycja spenia okrelone warunki lub mapa przekracza
okrelony rozmiar.
java.util.EnumSet<E extends Enum<E>> 5.0
EnumMap(Class<K> keyType)
IdentityHashMap()
IdentityHashMap(int expectedMaxSize)
Zwraca ten sam kod mieszajcy (obliczony na podstawie adresu w pamici obiektu)
co metoda Object.hashCode, nawet jeli w klasie, do ktrej naley obiekt obj,
przedefiniowano metod hashCode.
Rozdzia 13.
Kolekcje
707
Uytkownik architektury rozszerza jej klasy, dziki czemu nie musi opracowywa niektrych podstawowych mechanizmw od nowa. Na przykad Swing jest architektur interfejsw 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 udowodnilimy 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
Lista to kolekcja uporzdkowana. Elementy s dodawane w okrelonych miejscach zbiornika.
Obiekt mona wstawi w odpowiednie miejsce na dwa sposoby: wedug indeksu cakowitoliczbowego lub przez iterator listy. W interfejsie List znajduj si metody dajce dostp do
dowolnego elementu kolekcji:
void add(int index, E element)
E get(int index)
void remove(int index)
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 powolnych 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
}
Rozdzia 13.
Kolekcje
709
Klasy te mona rozszerza przy tworzeniu wasnych klas kolekcyjnych, dziki czemu dziedziczy si po nich wiele rutynowych procedur.
Konkretne klasy dostpne w bibliotece Javy to:
LinkedList
ArrayList
ArrayDeque
HashSet
TreeSet
PriorityQueue
HashMap
TreeMap
Zostay one wcielone do kolekcji rysunek 13.12. Omawiamy te klasy nieco dalej.
710
Java. Podstawy
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, zapenia 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.
Widoki maj kilka wanych zastosowa w architekturze kolekcji. Opisujemy je w poniszych podrozdziaach.
Rozdzia 13.
Kolekcje
711
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 odrnieniu od metody nCopies, ktra tworzy list). Zwrcony obiekt implementuje niemodyfikowalny 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
Na przedziale mona wykonywa dowolne dziaania, a ich rezultat bdzie automatycznie
widoczny w caej licie. Mona na przykad usun cay przedzia:
group2.clear();
// redukcja personelu
Przedzia group2 jest teraz pusty, a z listy staff zostay usunite te elementy, ktre znajdoway
si w tym przedziale.
Przedziay uporzdkowanych zbiorw i map tworzy si przy uyciu kolejnoci sortowania,
a nie pozycji elementw w kolekcji. W interfejsie SortedSet znajduj si trzy metody:
SortedSet<E> subSet(E from, E to)
SortedSet<E> headSet(E to)
SortedSet<E> tailSet(E from)
Zwracaj one podzbiory wszystkich elementw, ktre s wiksze ni lub rwne from i mniejsze 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.
Wprowadzony w Java SE 6 interfejs NavigableSet daje wiksze moliwoci kontroli dziaa
na przedziaach. Mona okreli, czy wartoci graniczne maj by wliczane (ang. inclusive):
NavigableSet<E> subSet(E from, boolean fromInclusive, E to, boolean toInclusive)
NavigableSet<E> headSet(E to, boolean toInclusive)
NavigableSet<E> tailSet(E from, boolean fromInclusive)
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
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.
Widok kontrolowany wykryje taki bd. Zdefiniujmy nastpujc bezpieczn list:
List<String> safeStrings = Collections.checkedList(strings, String.class);
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.
Rozdzia 13.
Kolekcje
715
niezwykle trudnym zadaniem zaspokojenia sprzecznych wymaga. Z jednej strony uytkownicy wymagaj, aby biblioteka bya atwa do nauki, wygodna w uyciu, w peni uoglniona
i niepodatna na bdy, a z drugiej strony ma by tak samo wydajna jak rcznie optymalizowane algorytmy. Spenienie tych wszystkich da naraz (czy choby zblienie si do nich)
jest najzwyczajniej niemoliwe. Jednak we wasnej praktyce programistycznej nie bdziesz
czsto rozwizywa tak ekstremalnych problemw. Powinno by moliwe znalezienie rozwiza, ktre nie polegaj na wykorzystaniu ostatecznego rodka w postaci opcjonalnych
operacji interfejsu.
java.util.Collections 1.2
716
Java. Podstawy
Rozdzia 13.
Kolekcje
717
Wykorzystujemy tutaj fakt, e kada kolekcja posiada konstruktor, ktrego parametr jest
inn kolekcj przechowujc wartoci pocztkowe.
Teraz uyjemy metody retainAll:
result.retainAll(b);
Zwraca ona wszystkie elementy, ktre znajduj si take w zbiorze b. W ten sposb znalelimy cz wspln dwch zbiorw bez tworzenia ptli.
Mona pj nawet dalej i zastosowa operacj zbiorcz do widoku. Wyobramy sobie, e
dysponujemy map przechowujc obiekty typu Employee z kluczami w postaci identyfikatorw ID oraz zbiorem identyfikatorw pracownikw, ktrzy maj zosta zwolnieni.
Map<String, Employee> staffMap = . . .;
Set<String> terminatedIDs = . . .;
Poniewa zbir kluczy jest widokiem mapy, klucze i skojarzeni z nimi pracownicy s automatycznie usuwani z tej mapy.
Przy uyciu widoku podprzedziau mona ograniczy operacje zbiorcze do podlist i podzbiorw. 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));
718
Java. Podstawy
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 implementowa 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 przypadku pustego kontenera lub zawierajcego tylko jeden element. Perspektywa cigego testowania 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 wyglda 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 przekonalimy 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 powizanych, listach tablicowych i tablicach.
720
Java. Podstawy
Technika ta daje bardzo due moliwoci. W bibliotece standardowej C++ znajduje si mnstwo 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 list 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++ autorstwa Roberta Sedgewicka, Addison-Wesley, 1998, s. 336 369). W Javie jednak nie wykorzystano 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 uporzdkowanej kolejnoci z powrotem do listy.
Rozdzia 13.
Kolekcje
721
Algorytm sortowania przez scalanie stosowany w bibliotece kolekcji ustpuje nieco prdkoci 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 kolejnoci 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 zarobkw, a potem wedug nazwisk.
Poniewa kolekcje nie musz implementowa wszystkich swoich metod opcjonalnych, wszystkie 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.
Waciwoci list mona przedstawi nastpujco:
Lista zezwala na zmian jej rozmiaru (ang. resizable), jeli obsuguje metody
add i remove.
W klasie Collections dostpna jest te metoda shuffle, ktra dziaa odwrotnie do sortowania tasuje elementy listy. Na przykad:
ArrayList<Card> cards = . . .;
Collections.shuffle(cards);
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
Tasuje element listy. Dugo czasu dziaania tego algorytmu to O(n a(n)),
gdzie n to dugo listy, a a(n) to redni czas dostpu do elementu.
Rozdzia 13.
Kolekcje
723
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 okrelenia 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 powizanych 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
724
Java. Podstawy
innego, zawsze musimy rozszyfrowa intencje innego programisty. Kiedy widzimy wywoanie metody typu Collections.max, od razu wiemy, jakie jest przeznaczenie danego fragmentu kodu.
Poniszy wycig z API opisuje proste algorytmy dostpne w klasie Collections.
java.util.Collections 1.2
Zwraca indeks pierwszej lub ostatniej podlisty listy l rwnej s lub -1, jeli adna
podlista l nie jest rwna s. Jeli na przykad lista l to [s, t, a, r], a s to [t, a, r],
obie metody zwrc indeks 1.
Rozdzia 13.
Kolekcje
725
list [a, r, t]. Dugo czasu dziaania tej metody to O(n), gdzie n okrela
dugo listy
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 elementw, a do tego nadaje si podstawowy interfejs Collection. Poniej znajduje si poprawiona 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 przykad 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
Jeli piszemy metod zwracajc kolekcj, moemy zdecydowa si na zwrcenie take
interfejsu zamiast klasy, poniewa pniej moemy zmieni zdanie i zaimplementowa nasz
metod ponownie przy uyciu innej kolekcji.
Napiszmy na przykad metod o nazwie getAllItems zwracajc wszystkie elementy menu.
List<MenuItem> getAllItems(JMenu menu)
{
ArrayList<MenuItem> items = new ArrayList<MenuItem>()
for (int i = 0; i < menu.getItemCount(); i++)
items.add(menu.getItem(i));
return items;
}
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();
}
};
}
Rozdzia 13.
Kolekcje
727
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 wiernymi odpowiednikami metod hasNext i next z interfejsu Iterator.
Na przykad metoda elements z klasy Hashtable tworzy wyliczenie z wartoci znajdujcych
si w tablicy:
Enumeration<Employee> e = staff.elements();
while (e.hasMoreElements())
{
Employee e = e.nextElement();
. . .
}
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.
boolean hasMoreElements()
E nextElement()
Zwraca kolejny element do odwiedzenia. Nie naley wywoywa tej metody, jeli
metoda hasMoreElements() zwrci warto false.
java.util.Hashtable<K, V> 1.0
Enumeration<K> keys()
728
Java. Podstawy
Enumeration<V> elements()
Enumeration<E> elements()
Properties()
Properties(Properties defaults)
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 teoretycznego 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)
E pop()
E peek()
Zwraca element z wierzchu stosu, nie usuwajc go. Nie naley wywoywa
tej metody na pustym stosie.
Szablon bitset w C++ ma tak sam funkcjonalno jak klasa BitSet w Javie.
730
Java. Podstawy
java.util.BitSet 1.0
BitSet(int initialCapacity)
int length()
Pobiera bit.
Ustawia bit.
Usuwa bit.
Usuwa wszystkie bity ze zbioru bitw, ktre s ustawione w innym zbiorze bitw.
Rozdzia 13.
Kolekcje
731
732
Java. Podstawy
Mimo e sito nie jest dobrym testem wydajnoci, nie moglimy si oprze pokusie zmierzenia czasu obu implementacji algorytmu. Oto wyniki uzyskane na notebooku 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.
Test ten przeprowadzilimy we wszystkich dziewiciu wydaniach tej ksiki i w ostatnich piciu Java bije C++ na gow. Trzeba jednak ujawni, e jeli zoptymalizujemy
kompilator C++, pobije on Jav, uzyskujc czas 20 milisekund. Taki wynik w Javie mona
by byo uzyska tylko wtedy, gdyby program dziaa na tyle dugo, aby wczy si kompilator JIT Hotspot.
#include <bitset>
#include <iostream>
#include <ctime>
using namespace std;
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++;
}
clock_t cend = clock();
double millis = 1000.0 * (cend - cstart) / CLOCKS_PER_SEC;
Rozdzia 13.
Kolekcje
733
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
Klasa Executors
Synchronizatory
736
Java. Podstawy
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 wspdzieleniu 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.
Wielowtkowo jest niezwykle praktycznym narzdziem. Wiadomo na przykad, e przegldarka powinna mie moliwo pobierania kilku obrazw jednoczenie, a serwer sieciowy
musi obsugiwa wiele da w tym samym czasie. Programy z graficznym interfejsem uytkownika dysponuj osobnym wtkiem do zbierania zdarze z interfejsu pochodzcych od
rodowiska operacyjnego. Ten rozdzia dotyczy pisania aplikacji wielowtkowych w Javie.
Ostrzeenie: programy wielowtkowe bywaj bardzo skomplikowane. My opisujemy wszystkie
narzdzia, ktrych moe potrzebowa programista. Jednak w poszukiwaniu opisw bardziej
zaawansowanych technik programowania systemowego odsyamy do innych rde, na przykad ksiki Java. Wspbieno dla praktykw, ktrej autorami s Brian Goetz, Tim Peierls,
Joshua Bloch, Joseph Bowbeer, David Holmes i Doug Lea (Helion, Gliwice 2007).
Rozdzia 14.
Wielowtkowo
737
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.
Na kocu kodu prezentowanego w tym podrozdziale znajduje si ponisza
instrukcja:
comp.paint(comp.getGraphics())
Mieci si ona w metodzie addBall klasy BounceFrame. Jest to bardzo dziwna technika
programowania normalnie wywoano by metod repaint, a reszt zajaby si biblioteka AWT. Jeli jednak wywoamy metod repaint w tym programie, panel nie zostanie
nigdy odwieony, poniewa metoda addBall cakowicie zaja wszystkie procesy. Dodatkowo 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 chcielibymy, 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 zobaczeniu 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 najwaniejszych czci kodu w osobnym wtku.
Kod programu jest przedstawiony na listingach 14.1 14.3.
Listing 14.1. bounce/Bounce.java
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");
comp = new BallComponent();
add(comp, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start", new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
addBall();
}
});
addButton(buttonPanel, "Zamknij", new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
add(buttonPanel, BorderLayout.SOUTH);
Rozdzia 14.
Wielowtkowo
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);
for (int i = 1; i <= STEPS; i++)
{
ball.move(comp.getBounds());
comp.paint(comp.getGraphics());
Thread.sleep(DELAY);
}
}
catch (InterruptedException e)
{
}
}
}
739
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);
}
}
Rozdzia 14.
Wielowtkowo
741
millis
Liczba milisekund
742
Java. Podstawy
public interface Runnable
{
void run();
}
4. Uruchom wtek:
t.start();
Umieszczenie programu odbijajcej si piki w osobnym wtku wymaga tylko zaimplementowania klasy BallRunnable i umieszczenia kodu animacji w metodzie run:
class BallRunnable implements Runnable
{
. . .
public void run()
{
try
{
for (int i = 1; i <= STEPS; i++)
{
ball.move(component.getBounds());
component.repaint();
Thread.sleep(DELAY);
}
}
catch (InterruptedException exception)
{
}
}
. . .
}
Rozdzia 14.
Wielowtkowo
743
Rysunek 14.2.
Uruchomienie
kilku wtkw
744
Java. Podstawy
class BallRunnable implements Runnable
{
private Ball ball;
private Component component;
public static final int STEPS = 1000;
public static final int DELAY = 5;
/**
* Tworzy obiekt Runnable
* @param aBall pika
* @param aComponent komponent, w ktrym odbija si pika
*/
public BallRunnable(Ball aBall, Component aComponent)
{
ball = aBall;
component = aComponent;
}
public void run()
{
try
{
for (int i = 1; i <= STEPS; i++)
{
ball.move(component.getBounds());
component.repaint();
Thread.sleep(DELAY);
}
}
catch (InterruptedException e)
{
}
}
}
/**
* 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
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 tworzenie 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()
void run()
void()
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 wywoywana na rzecz wtku, ktry blokuj metody takie jak sleep czy wait, wywoania blokujce
zostaj zakoczone przez wyjtek InterruptedException (istniej metody blokujce wejcia-wyjcia, ktrych nie mona przerwa; w takiej sytuacji naley rozway uycie ich
przerywalnych zamiennikw szczegowe informacje na ten temat znajduj si w rozdziaach 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 kontynuowa prac. Czsto jednak przerwanie jest przez wtek interpretowane jako danie zamknicia. 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.
}
Wywoywanie metody isInterrupted staje si bezcelowe, jeli po kadej iteracji wywoywana jest metoda sleep (lub inna pozwalajca na przerwanie). Jeli metoda sleep zostanie
wywoana na rzecz wtku z ustawionym statusem przerwania, wtek ten nie zostanie upiony.
Zamiast tego jego status zostanie wyzerowany oraz zostanie zgoszony wyjtek Interrupted
Exception. Dlatego jeli ptla zawiera wywoanie metody sleep, nie naley w niej sprawdza statusu przerwania. W zamian naley przechwyci wyjtek InterruptedException:
public void run()
{
try
{
. . .
while (instrukcje)
{
instrukcje
Thread.sleep(delay);
}
}
catch(InterruptedException e)
748
Java. Podstawy
{
// Wtek zosta przerwany w czasie upienia.
}
finally
{
Czyszczenie w razie potrzeby
}
// Wyjcie z metody run powoduje zamknicie wtku.
}
// Nie ignoruj!
Nie naley tego robi! Jeli nie masz adnego dobrego pomysu na klauzul catch, masz
jeszcze dwa inne dobre wyjcia:
Rozdzia 14.
Wielowtkowo
749
java.lang.Thread 1.0
void interrupt()
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()
NEW (nowy),
RUNNABLE (wykonywalny),
BLOCKED (zablokowany),
WAITING (oczekujcy),
TERMINATED (zakoczony).
750
Java. Podstawy
Kiedy wtek usiuje zaoy blokad wewntrzn na obiekt (ale nie utworzy obiekt
klasy Lock z biblioteki java.util.concurrent), ktry jest aktualnie w dyspozycji
innego wtku, zostaje zablokowany (blokady java.util.concurrent opisujemy
w podrozdziale 14.5.3, Obiekty klasy Lock, a blokady wewntrzne obiektw
w podrozdziale 14.5.5, Sowo kluczowe synchronized). Wtek zostaje
odblokowany, gdy wszystkie pozostae wtki zwolni blokad, a algorytm planujcy
zezwoli mu na przejcie tego obiektu.
Rozdzia 14.
Wielowtkowo
751
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()
Zwraca stan wtku: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING lub TERMINATED.
void stop()
void suspend()
void resume()
Rozdzia 14.
Wielowtkowo
753
Kiedy algorytm planujcy musi wybra nowy wtek, decyduje si na ten o najwyszym
priorytecie. Naley jednak pamita, e priorytety wtkw s w duym stopniu uzalenione od systemu. Jeli maszyna wirtualna korzysta z implementacji wtkw lecej u podoa platformy, priorytety Javy s odwzorowywane na poziomy platformy, ktra moe dysponowa wiksz lub mniejsz liczb poziomw priorytetu.
Na przykad system Windows wyrnia siedem poziomw priorytetu, a wic niektre z priorytetw 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.
Pocztkujcy programici miewaj tendencj do naduywania priorytetw wtkw. Naley
jednak pamita, e powodw do manipulowania nimi jest niewiele. W szczeglnoci nie
naley projektowa programu w taki sposb, aby jego poprawne funkcjonowanie byo zalene
od wysokoci priorytetw.
Kady, kto zdecyduje si na uycie priorytetw, powinien wystrzega si powszechnego bdu popenianego przez pocztkujcych. Jeli istnieje kilka wtkw o wysokim priorytecie, ktre nigdy nie s dezaktywowane, wtki o niszych priorytetach mog
nigdy nie doj do gosu. Algorytm planujcy, wybierajc wtek do uruchomienia, zawsze
wybierze jeden spord tych o najwyszym priorytecie, nawet jeli ma to oznacza, e
wtki o niszych priorytetach nie bd w ogle wykonywane.
java.lang.Thread 1.0
Najwyszy priorytet, jaki moe mie wtek. Maksymalna warto priorytetu to 10.
754
Java. Podstawy
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 odstpach 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.
Niektrzy pocztkujcy programici popeniaj bd uywania demonw w celu uniknicia
obsugi zamykania zasobw. Metoda ta moe by jednak niebezpieczna. Demon nie powinien
nigdy mie dostpu do zasobw staych, jak plik czy baza danych, poniewa moe zakoczy
dziaanie w kadej chwili, nawet w rodku operacji.
java.lang.Thread 1.0
Okrela, czy wtek jest demonem, czy zwykym wtkiem. Wywoanie tej metody
musi nastpi przed uruchomieniem wtku.
Rozdzia 14.
1.
Wielowtkowo
755
Dane ze ledzenia stosu z pewnoci kady widzia ju wiele razy w swoich programach.
java.lang.Thread 1.0
static Thread.UncaughtExceptionHandler
getDefaultUncaughtExceptionHandler() 5.0
java.lang.ThreadGroup 1.0
Wywouje metod nadrzdnej grupy wtkw, jeli taka istnieje, lub domyln
procedur obsugi klasy Thread, jeli istnieje domylna procedura, lub w przeciwnym
przypadku drukuje dane ze ledzenia stosu w standardowym strumieniu bdw
(jeli e jest obiektem typu ThreadDeath, dane ze ledzenia stosu s tumione;
obiekty ThreadDeath s generowane przez odradzan metod stop).
756
Java. Podstawy
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).
Poniej znajduje si kod klasy TransferRunnable. Jej metoda run przelewa pienidze z okrelonego 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 przechodzi 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 wszystkich kontach i drukuje wynik.
Program ten nigdy si nie koczy. Aby go zamkn, naley nacisn kombinacj klawiszy
Ctrl+C.
Oto typowy wydruk z programu:
. . .
Thread[Thread-11,5,main] 588.48 z 11 na 44 Saldo oglne: 100000.00
Thread[Thread-12,5,main] 976.11 z 12 na 22 Saldo oglne: 100000.00
Thread[Thread-14,5,main] 521.51 z 14 na 22 Saldo oglne: 100000.00
Thread[Thread-13,5,main] 359.89 z 13 na 81 Saldo oglne: 100000.00
. . .
Thread[Thread-36,5,main] 401.71 z 36 na 73 Saldo oglne: 99291.06
Thread[Thread-35,5,main] 691.46 z 35 na 77 Saldo oglne: 99291.06
Thread[Thread-37,5,main] 78.64 z 37 na 3 Saldo oglne: 99291.06
Thread[Thread-34,5,main] 197.11 z 34 na 69 Saldo oglne: 99291.06
Thread[Thread-36,5,main] 85.96 z 36 na 4 Saldo oglne: 99291.06
. . .
Thread[Thread-4,5,main]Thread[Thread-33,5,main] 7.31 z 31 na 32 Saldo oglne:
99979.24
627.50 z 4 na 5 Saldo oglne: 99979.24
. . .
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 obliczeniach 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. Sprbuj odszuka bd w kodzie, a rozwizanie zagadki znajdziesz w kolejnym podrozdziale.
Listing 14.5. unsynch/UnsynchBankTest.java
package unsynch;
/**
* 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;
public static void main(String[] args)
{
Bank b = new Bank(NACCOUNTS, INITIAL_BALANCE);
int i;
for (i = 0; i < NACCOUNTS; i++)
{
TransferRunnable r = new TransferRunnable(b, i, INITIAL_BALANCE);
Thread t = new Thread(r);
t.start();
}
}
}
Rozdzia 14.
Wielowtkowo
/**
* Zwraca sum sald wszystkich kont.
* @return saldo oglne
*/
public double getTotalBalance()
{
double sum = 0;
for (double a : accounts)
sum += a;
return sum;
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
public int size()
{
return accounts.length;
}
}
759
760
Java. Podstawy
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 pojawienie 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 wykonana w nastpujcy sposb:
1.
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 przeprowadzajcego test).
Jakie jest ryzyko wystpienia tego bdu? Zwikszylimy je, przeplatajc instrukcje drukowania 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
//Field accounts:[D
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
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 sekcji krytycznej spowoduje wyjtek, blokada musi zosta zdjta. W przeciwnym przypadku 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.
. . .
public void transfer(int from, int to, int amount)
Rozdzia 14.
Wielowtkowo
763
{
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 poczeka, 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 dziaanie, 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.
Na przykad metoda transfer wywouje metod getTotalBalance, ktra blokuje obiekt
bankLock majcy obecnie licznik o wartoci 2. Kiedy metoda getTotalBalance koczy dziaanie, warto licznika spada do 1. Zakoczenie metody transfer zmniejsza go do 0 i wtek
zwalnia blokad.
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()
void unlock()
Zwalnia blokad.
java.util.concurrent.locks.ReentrantLock 5.0
ReentrantLock()
ReentrantLock(boolean fair)
Rozdzia 14.
Wielowtkowo
765
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 uzyskay blokad, ale nie mog robi nic poytecznego, su obiekty warunkw. W tym rozdziale 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 ktrych 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 minimalnej potrzebnej kwoty. Naley przypilnowa, aby aden wtek nie zmodyfikowa salda pomidzy 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
nazw, ktre w jaki sposb przypominaj reprezentowane warunki. Na przykad w poniszym
fragmencie programu tworzymy obiekt warunku reprezentujcy warunek wystarczajcych
rodkw.
class Bank
{
private Condition wystSrodki;
. . .
public Bank()
{
. . .
wystSrodki = bankLock.newCondition();
}
}
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 uruchomiony. To moe prowadzi do nieprzyjemnych zakleszcze (ang. deadlock). Jeli prawie 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 wtkw oczekujcych. Na przykad wtki powinny mie moliwo sprawdzenia salda na koncie 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();
}
}
Po uruchomieniu programu przedstawionego na listingu 14.8 wida, e nie ma adnych bdw w obliczeniach. Saldo oglne cay czas wynosi 1000 dolarw. Saldo adnego z kont nigdy
nie jest ujemne (przypominamy, e aby zakoczy program, trzeba wcisn kombinacj klawiszy Ctrl+C). Da si rwnie zauway, e program dziaa nieco wolniej jest to cena, jak
pacimy za synchronizacj.
Listing 14.8. synch/Bank.java
package synch;
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
public class Bank
{
private final double[] accounts;
private Lock bankLock;
private Condition sufficientFunds;
/**
* 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
Poprawne zastosowanie warunkw w praktyce moe by sporym wyzwaniem. Przed podjciem prby zaimplementowania wasnych obiektw warunkw dobrze by byo najpierw wzi
pod uwag jedn z konstrukcji opisanych w podrozdziale 14.10, Synchronizatory.
java.util.concurrent.locks.Lock 5.0
Condition newCondition()
void await()
void signalAll()
void signal()
770
Java. Podstawy
Interfejsy Lock i Condition umoliwiaj programistom zyskanie wikszej kontroli nad blokadami. 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.
Innymi sowy, poniszy kod:
public synchronized void method()
{
ciao metody
}
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.
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 prbuj wej do metody synchronizowanej. Warunek zajmuje si wtkami, ktre wywoay
metod wait.
Metody synchronizowane s wzgldnie proste. Jednak pocztkujcy czsto szarpi
si z warunkami. Przed przejciem do uywania metod wait i notifyAll lepiej
zastanowi si nad uyciem jednej z konstrukcji opisanych w podrozdziale 14.10,
Synchronizatory.
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.
Wewntrzne blokady i warunki maj pewne ograniczenia. Oto niektre z nich:
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.
Interfejsw Lock i Condition naley uywa, gdy nie mona si oby bez dodatkowych
funkcji, ktre one udostpniaj.
772
Java. Podstawy
Rozdzia 14.
Wielowtkowo
return sum;
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
public int size()
{
return accounts.length;
}
}
java.lang.Object 1.0
void notifyAll()
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()
millis
Liczba milisekund
nanos
773
774
Java. Podstawy
W tym przypadku obiekt lock zosta utworzony tylko po to, aby mona byo uy blokady,
ktr posiada kady obiekt w Javie.
Czasami programici wykorzystuj blokady obiektw do implementacji dodatkowych niepodzielnych operacji. Technika ta nazywa si blokowaniem po stronie klienta (ang. client-side
locking). Wemy na przykad klas Vector implementujc listy, ktrych metody s synchronizowane. Wyobramy sobie, e salda kont w naszym banku zapisalimy w licie
Vector<Double>. Oto naiwna implementacja metody transfer:
public void transfer(Vector<Double> accounts, int from, int to, int amount)
{
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
System.out.println(. . .);
}
// bd
Mimo e metody get i set klasy Vector s synchronizowane, nic nam to nie daje. Istnieje moliwo, 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 modyfikujce w klasie Vector maj wewntrzne blokady. Czy tak jest jednak naprawd? W dokumentacji tej klasy nic takiego nie napisano. Trzeba bardzo uwanie przestudiowa jej kod
rdowy i mie nadziej, e w przyszoci nie zostan wprowadzone mutatory niesynchronizowane. Stanowi to dowd na to, e blokowanie po stronie klienta jest bardzo niepewn technik, 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 wielowtkowoci bezpieczn technik, nie zmuszajc jednoczenie programistw do zajmowania 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:
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.
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 zabezpiecze 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 techniczny 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.
Brian Goetz jest autorem motta synchronizacyjnego: jeli zapisujesz zmienn,
ktra moe nastpnie zosta odczytana przez inny wtek, albo odczytujesz zmienn,
ktra moga zosta zapisana przez inny wtek, musisz skorzysta z synchronizacji.
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 bardziej kopotliwe.
Rozsdnym wyjciem z tej sytuacji jest zadeklarowanie pola jako volatile:
private volatile boolean done;
public boolean isDone() { return done; }
public void setDone() { done = true; }
// podzielna
778
Java. Podstawy
Dostpne s te klasy AtomicBoolean, AtomicLong i AtomicReference oraz atomowe tablice
wartoci logicznych, liczb cakowitych, wartoci dugich i referencji. Ich uycie naley pozostawi programistom systemw, ktrzy programuj narzdzia wspbienoci. Programici
aplikacji nie powinni ich uywa.
14.5.11. Zakleszczenia
Blokady i warunki nie wystarcz do rozwizania wszystkich problemw zwizanych z wielowtkowoci. 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
Innym sposobem na spowodowanie zakleszczenia jest uczynienie i-tego wtku odpowiedzialnym za umieszczenie pienidzy na i-tym koncie zamiast za pobranie ich z i-tego konta. Istnieje
wtedy szansa, e wszystkie wtki zbiegn si nad jednym kontem i bd prbowa usun
z niego wicej pienidzy, ni si na nim znajduje. Aby to wyprbowa, naley w programie
780
Java. Podstawy
SynchBankTest znale metod run w klasie TransferRunnable. W wywoaniu metody transfer
zamie miejscami parametry fromAccount i toAccount. Uruchom program i przekonaj si, e
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.
Mona by byo uy klasy pomocniczej ThreadLocal, aby kademu wtkowi da osobny
generator, ale w Java SE 7 udostpniono specjaln klas, dziki ktrej praca ta jest wygodniejsza. Wystarczy wykona ponisze wywoanie:
int random = ThreadLocalRandom.current().nextInt(upperBound);
782
Java. Podstawy
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()
void set(T t)
void remove()
Metody lock nie mona przerwa. Jeli wtek oczekujcy na blokad zostanie przerwany,
pozostaje on zablokowany, dopki metoda lock nie bdzie dostpna. Jeli wystpi zakleszczenie, metoda lock moe nigdy nie zakoczy dziaania.
Jeli natomiast metoda tryLock zostanie wywoana z parametrem czasowym, przerwanie oczekujcego wtku spowoduje wygenerowanie wyjtku InterruptedException. Jest to niewtpliwie 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.
Ograniczy czasowo mona take oczekiwanie na warunek:
myCondition.await(100, TimeUnit.MILLISECONDS))
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, blokujc wtek przez czas nie duszy od okrelonego.
W razie powodzenia zwraca warto true.
void lockInterruptibly()
void awaitUninterruptibly()
784
Java. Podstawy
Oto lista czynnoci zwizanych z uyciem blokady odczytu-zapisu:
1.
Lock readLock()
Lock writeLock()
Rozdzia 14.
Wielowtkowo
785
Kolej na metod suspend. W przeciwiestwie do metody stop nie powoduje ona uszkodzenia obiektw. Jeli jednak zawieszony zostanie wtek posiadajcy blokad bdzie ona niedostpna 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.
Sytuacje tego typu czsto zdarzaj si w graficznych interfejsach uytkownika. Zamy, e
mamy graficzn symulacj naszego banku. Przycisk z etykiet Wstrzymaj zawiesza wtki
dokonujce przeleww, a przycisk z etykiet Wznw odwiesza je.
pauseButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
for (int i = 0; i < threads.length; i++)
threads[i].suspend();
// Nie rb tego.
}
});
resumeButton.addActionListener(. . .);
// Wywouje metod resume na rzecz wszystkich
// wtkw przelewowych.
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 przyciskw, jak i ponowne rysowanie odbywaj si w tym samym wtku wtku dystrybucji
zdarze (ang. event dispatch thread). Przeanalizujmy nastpujcy scenariusz:
1.
786
Java. Podstawy
4. Z jakiego powodu konieczne jest ponowne narysowanie wykresu konta.
5. Metoda paintComponent wywouje metod getBalances.
6. Metoda ta prbuje zaoy blokad obiektu bank.
Rozdzia 14.
Wielowtkowo
787
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
Normalne dziaanie
add
Dodaje element.
element
offer
Dodaje element i zwraca warto true. Zwraca warto false, jeli kolejka jest pena.
peek
poll
put
Dodaje element.
remove
take
Metoda getDelay zwraca ilo pozostaego czasu opnienia obiektu. Warto ujemna oznacza, 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.
W Java SE 7 dodano interfejs TransferQueue pozwalajcy wtkowi producenta poczeka, a
konsument bdzie gotowy do przyjcia elementu. Gdy producent wywouje ponisz metod:
q.transfer(item);
wywoanie to zostaje zablokowane do czasu, a blokad usunie inny wtek. Opisywany interfejs jest zaimplementowany w klasie LinkedTransferQueue.
Program przedstawiony na listingu 14.10 demonstruje sposb kontroli zestawu wtkw za
pomoc kolejki blokujcej. Przeszukuje on wszystkie pliki znajdujce si w katalogu i jego
podkatalogach oraz drukuje linijki, ktre zawieraj dane sowo kluczowe.
Rozdzia 14.
Wielowtkowo
789
790
Java. Podstawy
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
*/
class SearchTask implements Runnable
{
private BlockingQueue<File> queue;
private String keyword;
/**
* 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;
}
public void run()
{
try
{
boolean done = false;
while (!done)
{
File file = queue.take();
if (file == FileEnumerationTask.DUMMY)
{
queue.put(file);
done = true;
}
else search(file);
}
}
Rozdzia 14.
Wielowtkowo
791
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
*/
public void search(File file) throws IOException
{
try (Scanner in = new Scanner(file))
{
int lineNumber = 0;
while (in.hasNextLine())
{
lineNumber++;
String line = in.nextLine();
if (line.contains(keyword))
System.out.printf("%s:%d:%s%n", file.getPath(), lineNumber, line);
}
}
}
}
Wtek producenta (producent) tworzy wyliczenie wszystkich plikw znalezionych we wszystkich podkatalogach i wstawia je do kolejki blokujcej. Operacja ta jest bardzo szybka i gdyby
nie ograniczenie pojemnoci, kolejka w szybkim tempie zapeniaby si wszystkimi plikami
znajdujcymi si w systemie plikw.
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, wykorzystalimy 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 synchronizacji uywamy kolejki.
java.util.concurrent.ArrayBlockingQueue<E> 5.0
ArrayBlockingQueue(int capacity)
792
Java. Podstawy
java.util.concurrent.LinkedBlockingQueue<E> 5.0
java.util.concurrent.LinkedBlockingDeque<E> 6
LinkedBlockingQueue()
LinkedBlockingDeque()
LinkedBlockingQueue(int capacity)
LinkedBlockingDeque(int capacity)
DelayQueue()
PriorityBlockingQueue()
PriorityBlockingQueue(int initialCapacity)
initialCapacity
comparator
java.util.concurrent.BlockingQueue<E> 5.0
Rozdzia 14.
Wielowtkowo
E take()
E takeFirst()
E takeLast()
Usuwa i zwraca element z czoa lub ogona i w razie potrzeby wcza blokad.
Usuwa i zwraca element z czoa lub ogona. W razie potrzeby wcza blokad,
a element bdzie dostpny lub upynie wyznaczony czas. W przypadku
niepowodzenia zwraca warto null.
java.util.concurrent.TransferQueue<E> 7
793
794
Java. Podstawy
Rozdzia 14.
Wielowtkowo
795
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)
ConcurrentLinkedQueue<E>()
ConcurrentSkipListSet<E>()
ConcurrentHashMap<K, V>()
Tworzy map haszow, do ktrej mona bezpiecznie uzyska dostp w wielu wtkach.
Parametry:
initialCapacity
loadFactor
zapisujcych.
796
Java. Podstawy
ConcurrentSkipListMap<K, V>()
Jeli podany klucz jest aktualnie skojarzony z podan wartoci, metoda ta usuwa
je i zwraca warto true. W przeciwnym przypadku zwraca warto false.
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 wtkowo dostp.
Rozdzia 14.
Wielowtkowo
797
Naley zapewni, e aden wtek nie bdzie mia dostpu do struktury danych poprzez oryginalne 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.
Zamiast uywa synchronizacyjnych obiektw opakowujcych, zazwyczaj lepiej jest skorzysta z kolekcji z pakietu java.util.concurrent. Mapa ConcurrentHashMap zostaa bardzo
starannie zaimplementowana w taki sposb, aby mona byo uzyska do niej dostp w wielu
wtkach, nie powodujc ich wzajemnego blokowania si, pod warunkiem e dziaaj one na
rnych komrkach. Jeden wyjtek stanowi lista tablicowa, ktra jest czsto modyfikowana.
W takim przypadku synchronizowana lista ArrayList moe si okaza lepsza od listy CopyOn
WriteArrayList.
java.util.Collections 1.2
798
Java. Podstawy
public interface Callable<V>
{
V call() throws Exception;
}
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 przeprowadzajcy 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 przeciwnym przypadku.
Operacj mona przerwa za pomoc metody cancel. Jeli jeszcze si nie rozpocza, zostanie anulowana i nigdy si nie rozpocznie. Jeli jest w toku, zostanie przerwana, gdy parametr
mayInterrupt ma warto true.
Obiekt FutureTask jest wygodnym narzdziem do zamieniania obiektw Callable zarwno
na Future, jak i Runnable, poniewa implementuje oba te interfejsy. Na przykad:
Callable<Integer> myComputation = . . .;
FutureTask<Integer> task = new FutureTask<Integer>(myComputation);
Thread t = new Thread(task);
// Runnable
t.start();
. . .
Integer result = task.get();
// Future
Rozdzia 14.
Wielowtkowo
799
800
Java. Podstawy
* Tworzy obiekt klasy MatchCounter.
* @param directory katalog, od ktrego ma si zacz szukanie
* @param keyword sowo kluczowe do znalezienia
*/
public MatchCounter(File directory, String keyword)
{
this.directory = directory;
this.keyword = keyword;
}
public Integer call()
{
count = 0;
try
{
File[] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for (File file : files)
if (file.isDirectory())
{
MatchCounter counter = new MatchCounter(file, keyword);
FutureTask<Integer> task = new FutureTask<>(counter);
results.add(task);
Thread t = new Thread(task);
t.start();
}
else
{
if (search(file)) count++;
}
for (Future<Integer> result : results)
try
{
count += result.get();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
catch (InterruptedException e)
{
}
return count;
}
/**
* 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()
V get()
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 isCancelled()
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)
Rozdzia 14.
Wielowtkowo
803
Opis
newCachedThreadPool
newFixedThreadPool
newSingleThreadExecutor
newScheduledThreadPool
newSingleThreadScheduledExecutor
Pula wykona powierzone jej zadanie przy najbliszej sposobnoci. Metoda submit zwraca obiekt
typu Future, ktry zawiera informacje o stanie zadania.
Pierwsza z wymienionych metod zwraca do osobliwie wygldajcy typ Future<?>. Na rzecz
tego obiektu mona wywoa metody isDone, cancel lub isCancelled. Natomiast metoda get
w chwili ukoczenia zwraca warto null.
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 prbuje przerwa aktualnie uruchomione.
Oto zestawienie dziaa, ktre naley wykona, aby uy puli wtkw:
1.
Rozdzia 14.
Wielowtkowo
}
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;
}
public Integer call()
{
count = 0;
try
{
File[] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for (File file : files)
if (file.isDirectory())
{
MatchCounter counter = new MatchCounter(file, keyword, pool);
Future<Integer> result = pool.submit(counter);
results.add(result);
}
else
{
if (search(file)) count++;
}
for (Future<Integer> result : results)
try
{
count += result.get();
}
catch (ExecutionException e)
{
e.printStackTrace();
805
806
Java. Podstawy
}
}
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.
Zwraca pul wtkw, ktra wykonuje zadania przy uyciu okrelonej liczby
wtkw.
ExecutorService newSingleThreadExecutor()
Rozdzia 14.
Wielowtkowo
807
java.util.concurrent.ExecutorService 5.0
void shutdown()
Zamyka usug. Koczy przekazane wczeniej zadania, ale nie przyjmuje nowych.
java.util.concurrent.ThreadPoolExecutor 5.0
int getLargestPoolSize()
ScheduledExecutorService newSingleThreadScheduledExecutor()
808
Java. Podstawy
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 udostpniane. Mona to osign za pomoc klasy ExecutorCompletionService.
Naley zacz od utworzenia w normalny sposb egzekutora. Nastpnie tworzymy obiekt
ExecutorCompletionService, do ktrego przekazujemy zadania. Obiekt ten zarzdza kolejk
blokujc zawierajc obiekty typu Future, w ktrych zapisywane s wyniki zada. W zwizku
T invokeAny(Collection<Callable<T>> tasks)
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.
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)
Future<T> take()
Usuwa nastpny wynik lub wcza blokad, jeli nie ma dostpnych adnych
wynikw.
Future<T> poll()
Usuwa nastpny wynik lub zwraca warto null, jeli nie ma dostpnych adnych
wynikw. Druga wersja tej metody odczekuje okrelon ilo czasu.
810
Java. Podstawy
Jednym z przykadw jest przetwarzanie obrazu. Obraz mona poprawi, przeksztacajc
jego grn i doln poow. Jeli ma si do dyspozycji wystarczajc liczb wolnych procesorw, operacje te mona wykonywa rwnoczenie (trzeba bdzie dodatkowo wykona
prac zwizan z poczeniem powek, ale to mao istotny szczeg).
My przeanalizujemy prostszy przykad. Przypumy, e chcemy si dowiedzie, ile elementw tablicy spenia pewien warunek. Dzielimy j na p, wykonujemy obliczenia dla kadej
z powek osobno, a nastpnie sumujemy wyniki.
Aby wykona nasze rekursywne obliczenia w odpowiedni sposb, utworzymy klas rozszerzajc 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 otrzyma sum.
Istnieje te metoda get do pobierania aktualnego wyniku, ale jest ona mniej atrakcyjna, poniewa moe zgasza kontrolowane wyjtki, ktrych nie moemy ponownie
zgasza w metodzie compute.
Rozdzia 14.
Wielowtkowo
Integer compute()
- from < THRESHOLD)
count = 0;
(int i = from; i < to; i++)
if (filter.accept(values[i])) count++;
}
return count;
}
else
{
int mid = (from + to) / 2;
Counter first = new Counter(values, from, mid, filter);
811
812
Java. Podstawy
Counter second = new Counter(values, mid, to, filter);
invokeAll(first, second);
return first.join() + second.join();
}
}
}
14.10. Synchronizatory
W pakiecie java.util.concurrent znajduje si kilka klas, ktre uatwiaj zarzdzanie zbiorami 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 przechowywany 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 potencjalnie 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 wszechstronne, 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.
Oczywicie programici aplikacji nie powinni ponownie wynajdowa kolejek ograniczonych.
Semafory rzadko odpowiadaj typowym sytuacjom programistycznym.
Rozdzia 14.
Wielowtkowo
813
Dziaanie
Zastosowanie
CyclicBarrier
Phaser
Podobny do CyclicBarrier,
ale ze zmiennym licznikiem.
Wprowadzony w Java SE 7.
CountDownLatch
Exchanger
Semaphore
SynchronousQueue
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 fragment 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);
Rozdzia 14.
Wielowtkowo
815
Wyprbujmy ten program. Kliknij przycisk Zy. Kliknij kilkakrotnie pole listy. Porusz paskiem 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 podczas jego wywietlania. Jednak projektanci biblioteki Swing postanowili nie robi nic w kierunku bezpieczestwa dla wtkw, i to z dwch powodw. Synchronizacja jest czasochonna,
a nikt nie chcia, aby Swing by jeszcze wolniejszy. Ponadto zesp pracujcy nad Swingiem wzi pod uwag dowiadczenia innych zespow, ktre pracoway nad zestawami narzdzi 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.
Rozdzia 14.
Wielowtkowo
817
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.
W przypadku aktualizacji etykiety postpu bardziej odpowiednia jest metoda invokeLater.
Uytkownicy raczej przedkadaj szybko dziaania wtku roboczego nad precyzj wskanika postpu.
Obie te metody wykonuj metod run w wtku dystrybucji zdarze nie jest tworzony aden
nowy wtek.
Listing 14.14 przedstawia program demonstrujcy bezpieczn modyfikacj zawartoci listy
rozwijalnej za pomoc metody invokeLater. Kliknicie przycisku Dobry powoduje, e wtek
wstawia i usuwa liczby, ale modyfikacje te odbywaj si w wtku dystrybucji zdarze.
Listing 14.14. swing/SwingThreadTest.java
package swing;
import
import
import
import
java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
/**
* Program udowadniajcy, e wtek dziaajcy rwnolegle z wtkiem dystrybucji zdarze moe
* powodowa bdy w komponentach Swing
818
Java. Podstawy
* @version 1.23 2007-05-17
* @author Cay Horstmann
*/
public class SwingThreadTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new SwingThreadFrame();
frame.setTitle("SwingThreadTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka majca dwa przyciski suce do zapeniania listy w osobnym wtku. Przycisk Dobry
* wykorzystuje kolejk zdarze, a Zy modyfikuje list bezporednio.
*/
class SwingThreadFrame extends JFrame
{
public SwingThreadFrame()
{
final JComboBox<Integer> combo = new JComboBox<>();
combo.insertItemAt(Integer.MAX_VALUE, 0);
combo.setPrototypeDisplayValue(combo.getItemAt(0));
combo.setSelectedIndex(0);
JPanel panel = new JPanel();
JButton goodButton = new JButton("Dobry");
goodButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
new Thread(new GoodWorkerRunnable(combo)).start();
}
});
panel.add(goodButton);
JButton badButton = new JButton("Zy");
badButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
new Thread(new BadWorkerRunnable(combo)).start();
}
});
panel.add(badButton);
panel.add(combo);
add(panel);
Rozdzia 14.
Wielowtkowo
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.
*/
class BadWorkerRunnable implements Runnable
{
private JComboBox<Integer> combo;
private Random generator;
public BadWorkerRunnable(JComboBox<Integer> aCombo)
{
combo = aCombo;
generator = new Random();
}
public void run()
{
try
{
while (true)
{
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)
{
}
}
}
/**
* 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;
public GoodWorkerRunnable(JComboBox<Integer> aCombo)
{
combo = aCombo;
generator = new Random();
}
public void run()
{
819
820
Java. Podstawy
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
Rozdzia 14.
Wielowtkowo
821
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
java.awt.*;
java.awt.event.*;
java.io.*;
java.util.*;
java.util.List;
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
class SwingWorkerFrame extends JFrame
{
private JFileChooser chooser;
private JTextArea textArea;
private JLabel statusLine;
private JMenuItem openItem;
private JMenuItem cancelItem;
private SwingWorker<StringBuilder, ProgressData> textReader;
public static final int TEXT_ROWS = 20;
public static final int TEXT_COLUMNS = 60;
public SwingWorkerFrame()
{
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
textArea = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
add(new JScrollPane(textArea));
statusLine = new JLabel(" ");
add(statusLine, BorderLayout.SOUTH);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
openItem = new JMenuItem("Otwrz");
menu.add(openItem);
openItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Wywietlenie okna dialogowego wyboru pliku
int result = chooser.showOpenDialog(null);
// Jeli plik zosta wybrany, zostanie on ustawiony jako ikona etykiety
if (result == JFileChooser.APPROVE_OPTION)
{
textArea.setText("");
openItem.setEnabled(false);
textReader = new TextReader(chooser.getSelectedFile());
textReader.execute();
cancelItem.setEnabled(true);
}
}
});
cancelItem = new JMenuItem("Anuluj");
menu.add(cancelItem);
cancelItem.setEnabled(false);
cancelItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
textReader.cancel(true);
}
Rozdzia 14.
Wielowtkowo
});
pack();
}
private class ProgressData
{
public int number;
public String line;
}
private class TextReader extends SwingWorker<StringBuilder, ProgressData>
{
private File file;
private StringBuilder text = new StringBuilder();
public TextReader(File file)
{
this.file = file;
}
// 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;
}
// Ponisze metody s wykonywane w wtku dystrybucji zdarze
@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");
}
823
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);
}
};
}
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.
Aby pokaza uytkownikowi, e co si dzieje, w pasku stanu bdziemy wywietla liczb
wczytanych linijek tekstu. Dlatego dane postpu skadaj si z aktualnej liczby linii tekstu
oraz aktualnej linii tekstu. Dane te pakujemy w prostej klasie wewntrznej:
private class ProgressData
{
public int number;
public String line;
}
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 wywoujemy 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 przetestowa 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 aktualizacji interfejsu uytkownika.
Aby program dziaa pynniej, pole tekstowe mona aktualizowa w wtku roboczym.
Nie jest to jednak moliwe w przypadku wszystkich komponentw Swing. Prezentujemy 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 zdarze elementu menu Otwrz.
Ta prosta technika pozwala na wykonywanie czasochonnych zada przy zachowaniu wraliwoci interfejsu uytkownika.
javax.swing.SwingWorker<T, V> 6
abstract T doInBackground()
Rozdzia 14.
Wielowtkowo
827
void execute()
SwingWorker.StateValue getState()
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 komponentw nie by realizowany. Komponent jest realizowany, kiedy moe odbiera zdarzenia 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 najwyszego poziomu. Nie byo potrzeby kopotliwego planowania obiektw implementujcych
interfejs Runnable na wtku dystrybucji zdarze.
Niestety niektrzy programici komponentw nie zwaali na misterno pierwotnej zasady
jednego wtku. Uruchamiali dziaania na rzecz wtku dystrybucji zdarze bez sprawdzenia,
czy komponent zosta zrealizowany. Na przykad wywoanie metod setSelectionStart lub
setSelectionEnd na rzecz komponentu JTextComponent spowoduje, e przesunicie karetki
zostanie wykonane w wtku dystrybucji zdarze, chocia komponent jest niewidoczny.
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 programistycznych. Mamy nadziej, e podobaa Ci si podr przez podstawowe zagadnienia zwizane z Jav i e udao Ci si tu znale przydatne wiadomoci. Dodatkowe informacje na
temat programowania sieciowego, zaawansowanego programowania AWT i Swing, bezpieczestwa aplikacji czy internacjonalizacji zostay zawarte w drugim tomie.
Sowa kluczowe
Javy
Sowo kluczowe
Opis
abstract
assert
boolean
Typ logiczny
3.
break
3.
byte
3.
case
3.
catch
char
3.
class
Definicja klasy
4.
const
Nieuywane
continue
3.
default
3.
do
3.
double
3.
else
3.
enum
Wyliczenie
3.
extends
4.
final
5.
finally
11.
float
3.
Rozdzia
5.
11.
11.
830
Java. Podstawy
Sowo kluczowe
Opis
for
Rodzaj ptli
goto
Nieuywane
if
Instrukcja warunkowa
3.
implements
6.
import
Import pakietu
4.
instanceof
5.
int
3.
interface
6.
long
3.
native
new
3.
null
Referencja null
3.
package
4.
private
4.
protected
5.
public
4.
return
3.
short
3.
static
3.
strictfp
2.
super
5.
switch
Instrukcja wyboru
3.
synchronized
this
throw
Zgoszenie wyjtku
11.
throws
11.
transient
try
void
volatile
while
Ptla
Rozdzia
3.
14.
4.
1. (tom II)
11.
3.
14.
3.
Skorowidz
A
abstrakcja, 214
ActiveX, 26, 35, 540
adnotacja, 111, 640
@SafeVarargs, 643
@SuppressWarnings, 643, 648
adres URL, 527, 546
agregacja, 135
akceleratory, 439
akcesorium podgldu, 499
akcesory, 142
akcje, 355, 373
aktualizacje, updates, 39
aktualizowanie preferencji, 558
aktywno komponentu, 375
algorytm, 132, 718
binarySearch, 723
obliczania kodu mieszajcego, 226
QuickSort, 121, 721
znajdujcy najwikszy element, 718
algorytmy
sortujce, 720
w klasie Collections, 724
alokacja listy tablicowej, 235
analiza
funkcjonalnoci klasy, 252
MVC, 397
obiektw w czasie dziaania programu, 257
animacja piki, 736742
animowane gify, 546
anonimowe klasy wewntrzne, 289, 300, 363
API
Javy, 33
JNLP, 525
Logging, 591
832
Java. Podstawy
atrybuty
pozycjonujce, 540
znacznika applet, 537540
znacznika param, 541, 542
autoboxing, 32
automatyczna konwersja typw, 32
automatyczne opakowywanie, 241
AWT, Abstract Window Toolkit, 314
B
bariera cykliczna, 814
bariery, 814
bazowy katalog drzewa pakietu, 187
bezpieczestwo, 25, 35
typw, 639, 649
wtkw, 775
biaa ksiga Javy, 22
biblioteka, 33
AWT, 314, 387
fdlibm, 74
IFC, 314
Java2D, 332, 333
JFC, 314
kolekcji, 666, 707
refleksyjna, 247
STL, 666
Swing, 315, 815
biblioteki
struktur danych, 666
zabezpiecze, 25
bit, 729
blok, 98
inicjujcy, 175
try-catch, 250, 578, 585
try-finally, 578
blokada, 762, 769
jawna, 770
odczytu, 783
uczciwa, 764
wewntrzna, 770
zapisu, 784
bloki synchronizowane, 774
blokowanie po stronie klienta, 774, 775
bd
AssertionError, 587
pomyki o jeden, 719
ThreadDeath, 752
typu, 650
typu cannot read, 46
bdy
danych wejciowych, 564
kompilacji, 49, 153, 181, 629, 646
programisty, 568
przydzielania pamici, 25
urzdze, 564
w Eclipse, 49
w kodzie, 565
wejcia-wyjcia, 565
wewntrzne, 568
wykonawcze, 644
zabezpiecze, 25
zaokrglania, 64, 333
C
catch, 250
cechy
jzyka, 22, 33
komponentu, 393
certyfikat
bezpieczestwa, 524
niebezpieczny, 525
wasny, 524
chwytanie typu wieloznacznego, 655
ciao metody, 60
czasochonne zadania, 816
czcionki, 343
bezszeryfowe, 346
nazwy, 343
nazwy logiczne, 344
PostScript type 1, 345
styl, 345
TrueType, 345
wysoko, 346
D
dane, 195
binarne, 26
wejciowe, 89
wyjciowe, 91
debuger, 28, 621
debuger JSwat, 623
debugowanie, 609
debugowanie aplikacji z GUI, 614
definiowanie
klasy, 148
klasy oglnej, 630
kolorw, 340
suchacza, 356
staej klasowej, 69
wtku, 745
wyjtkw kontrolowanych, 567
zmiennej, 66, 68
zmiennej obiektowej, 138
zmiennej tablicowej, 117
Skorowidz
dekompilowanie pliku, 761
dekrementacja, 71
delegacja, 265
delegacja zdarze, 356
demony, 753
dezaktywacja elementw menu, 441
diagram
dziedziczenia klasy, 215, 534
dziedziczenia zdarze AWT, 387
hierarchii wyjtkw, 565
klas, 136
przepywu sterowania, 100106, 110
dodawanie
akcji do menu, 375
elementu do listy powizanej, 678
elementu do mapy, 702
ikony, 435
klasy do pakietu, 182
klauzuli throws, 96
komponentw, 401
dokumentacja, 194
API, 8587
JSR, 627
zaoe, 590
dopasowywanie typw, 659
dopenienie, 459
wewntrzne, 452
zewntrzne, 452
dostp
chroniony, 219
do apletu, 539
do elementw kolekcji, 672, 708
do elementw listy tablicowej, 236
do elementu tablicy, 117
do formatera, 781
do komponentw, 473
do pakietw, 518
do plikw lokalizacyjnych, 516
do pliku, 96
do pliku JNLP, 521
do pl, 155
do pl generycznych, 637
do prywatnych pl nadklasy, 202
do sekcji krytycznej, 762
do stanu obiektu, 289
do usugi, 526
do wartoci, 243
do wza drzewa, 555
do zasobw lokalnych, 525
do zmiennej warunkowej, 775
do zmiennych finalnych, 297
jednoczesny wtkw, 761
swobodny, 719, 723
wtkw do struktury danych, 757
drukowanie, 31
drukowanie informacji o klasie, 252
drzewo katalogw, 42
due liczby, big numbers, 62
dymki, tooltips, 446
dyrektywa
#include, 182
import, 90
dziaanie
kontrolera, 395
metody transfer, 761
dziedziczenia klasy Applet, 534
dziedziczenie, inheritance, 133, 199, 268
hierarchia, 206
klasy abstrakcyjne, 214
klasy finalne, 211
metody finalne, 211
ochrona dostpu, 219
polimorfizm, 207
pomidzy klasami par, 649
rzutowanie, 212
typw oglnych, 649
wizanie dynamiczne, 209
wielokrotne, 279
dzielenie
cakowitoliczbowe, 69
modulo, 69
zmiennoprzecinkowe, 69
dzienniki, 591
dzienniki rotacyjne, 599
E
Eclipse, 44, 47
edycja
kodu rdowego, 48
cieki dostpu, 39
edytor tekstowy
Emacs, 44
JEdit, 44
TextPad, 44
edytowalna lista rozwijalna, 423
EE, Enterprise Edition, 38
egzemplarz klasy, 132
elementy menu, 432
aktywowanie, 440
dezaktywowanie, 440
ikony, 435
pola wyboru, 436
przeczniki, 436
elementy tablicy, 116
eliminacja wywoa funkcji, 27
elipsa, 335
833
834
Java. Podstawy
etykiety, 459
HTML, 409
komponentw, 408
ewolucja Javy, 32
F
figury
2D, 332
geometryczne, 333
filtr
plikw, 496, 497, 505
rekordw, 600
firma
Oracle, 32, 34
Sun Fellow, 30
Sun Microsystems, 25, 34
format
binarny liczby, 63
JNLP, 519
Unicode, 26
XML, 556
formatery, 600
formatowanie
danych wyjciowych, 91
daty, 95
funkcja unexpected, 569
funkcje
czysto wirtualne, 216
matematyczne, 73
sieciowe, 24
skadowe, 60
G
GC, Garbage Collector, 702
generowanie
dokumentacji, 194
obiektw klas oglnych, 646
generyczne
klasy, 629
listy tablicowe, 233
generyczny kod tablicowy, 261
graficzny interfejs uytkownika, GUI, 28, 57, 313,
391, 614
grafika, 313
grupa, 754
przyciskw radiowych, 416
wtkw, 755
zada, 808
GTK, 316
harmonogram
wykonywania wtkw, 750
zada, 696
haso, 90, 410
hermetyzacja, 133, 155
hierarchia
dziedziczenia, 206
dziedziczenia interfejsu Type, 660
dziedziczenia klasy Component, 400
dziedziczenia klasy JFrame, 321
interfejsw, 278
wyjtkw, 566, 586
zdarze, 387
historia Javy, 30
HTML, 33
I
IDE, 39, 47
identyfikacja klas, 134
IFC, Internet Foundation Classes, 314
ikony, 435
ikony komunikatw, 475
implementacja
apletw, 533
ArrayList, 644
interfejsu, 271, 273
klasy Bank, 770
klasy oglnej, 629
kolejki, 667
import
klas, 180
statyczny, 182
indeks, 123
indeks argumentu, 95
informacje
o klasie, 252
o typach, 28
o typach czasu wykonywania, 248
o typach generycznych, 659
o typach obiektw, 248
o uruchomionym programie, 613
o zdarzeniach, 602
inicjalizacja
pl, 172
pl statycznych, 176
pl wartociami domylnymi, 171
tablic, 118
z podwjn klamr, 302
zmiennej obiektowej, 138
zmiennych, 68
Skorowidz
inkrementacja, 71
instalacja
bibliotek, 41
dokumentacji, 41
filtru, 600
JDK, 38, 39
programw, 42
instrukcja
break, 111114
break z etykiet, 112
case, 111
continue, 113
do-while, 104
for, 77
goto, 111
if, 100, 113
if-else, 100
if-else if, 102
import, 180, 182
lock, 762
return, 578
switch, 109111
try, 580, 581
while, 103
instrukcje
sterujce, 98
warunkowe, 98, 109, 113
zoone, 99
interfejs
Action, 373, 379, 433
ActionListener, 286, 300, 357, 364, 373, 389
AdjustmentListener, 389
AppletContext, 547
AutoCloseable, 580
BasicService, 527
BlockingDeque<E>, 793
BlockingQueue<E>, 792
ButtonModel, 397, 398, 417
Callable, 797
Callable<V>, 801
Cloneable, 282
Collection, 668672, 679, 707, 711
Collection<E>, 672
Comparable, 272, 279, 308, 634, 654, 689
Comparator<T>, 690, 693
Condition, 769771
Delayed, 792
Deque<E>, 695
Enumeration, 670, 727
ExecutorService, 803, 808
FileFilter, 497
Filter, 600
FocusListener, 389
Formattable, 92
Future, 798
Future<V>, 801
GenericArrayType, 660, 664
InvocationHandler, 307, 311
ItemListener, 389
Iterable, 117, 669
Iterator, 669, 677, 680
Iterator<E>, 674
klasy, 146
LayoutManager, 469
LayoutManager2, 469
LinkedList<E>, 683
List, 684, 708, 711
List<E>, 682
ListIterator, 677, 708
ListIterator<E>, 683
Lock, 764, 769, 771, 783
KeyListener, 389
Map, 707
Map<K, V>, 700
MenuListener, 441
MouseListener, 381, 383, 389
MouseMotionListener, 381, 383, 389
MouseWheelListener, 389
nasuchu, 356
NavigableMap, 709
NavigableMap<K, V>, 716
NavigableSet, 709, 712
NavigableSet<E>, 694, 716
ParameterizedType, 660, 664
PersistenceService, 527
PersistentService, 528
Powered, 278
Queue, 666, 667
Queue<E>, 695
RandomAccess, 708, 721
Runnable, 797
ScheduledExecutorService, 807
Set, 708
Shape, 333
SortedMap, 709
SortedMap<K, V>, 716
SortedSet, 709, 712
SortedSet<E>, 693, 716
SwingConstants, 278, 408
Thread.UncaughtExceptionHandler, 754
TransferQueue, 788
TransferQueue<E>, 793
Type, 660
TypeVariable, 660, 664
WildcardType, 660, 664
WindowFocusListener, 389
WindowListener, 369, 372, 389
WindowStateListener, 372, 389
835
836
Java. Podstawy
J
JAR, Java Archive, 512
Java look and feel, 315
Java Micro Edition, 23
Java Plug-in, 540
Java Runtime System, 26
Java Web Start, 511, 519
JavaBeans, 247
JavaFX, 317
jawna inicjalizacja pl, 172
JDK, Java Development Kit, 37
jednostki kodowe, 66, 81
jzyk
Algol, 165
C, 25
C#, 28, 34
C++, 23, 24
HTML, 33
J#, 28
J++, 28
Java, 22
JavaScript, 35
UML, 136
Visual Basic, 23, 137
jzyki
interpretowane, 34
obiektowe, 24
proceduralne, 24
JFC, Java Foundation Classes, 314
JIT, just-in-time compiler, 27
JNLP, Java Network Launch Protocol, 519
JRE, Java Runtime Environment, 38
JSR, Java Specification Requests, 627
K
kalendarz, 139
kalkulator, 403, 521
karta
HSB, 506
RGB, 506
Swatches, 506
katalog
bazowy drzewa pakietu, 187
bin, 39
com, 183
gutenberg, 820
src, 43
klas
coupling, 135
diagramy, 136
dziedziczenie, 133, 200
identyfikacja, 134
implementacja interfejsu, 271
komentarze, 191
konstruktory, 137, 152
metody, 133
metody prywatne, 157
metody statyczne, 160
nadklasy, 200
plik rdowy, 189
podklasy, 200
pola statyczne, 159
predefiniowanie, 137
projektowanie, 195
relacje, 135
rozszerzanie, 133
pola stae, 158
cieka, 187
klasa, 58, 132
ExampleFileView, 499
AbstractAction, 374, 377
AbstractButton, 419, 434436, 440
AbstractCollection, 672
AbstractList, 726
AbstractQueue, 668
AbstractSequentialList, 723
AbstractSet, 223
AccessibleObject, 257, 261
ActionMap, 376
AnonymousInnerClassTest, 301
Applet, 533, 537, 545, 549
AppletContext, 540, 548, 549
Array, 262
ArrayAlg, 663
ArrayBlockingQueue<E>, 791
ArrayDeque, 694
ArrayDeque<E>, 696
Skorowidz
ArrayDequeue, 668
ArrayList, 234236, 240, 628, 642, 674, 684
ArrayList<T>, 650
ArrayListTest, 238
Arrays, 118, 121, 123
AtomicInteger, 777
AWTEvent, 387
BallRunnable, 742
BasicButtonUI, 398
BasicService, 528, 531
bazowa Object, 133, 220
BigDecimal, 64, 114, 116
BigInteger, 114, 115
BigIntegerTest, 115
BitSet, 726, 729
BlockingQueueTest, 789
BorderFactory, 419, 421
BorderLayout, 401, 402
BounceFrame, 737
BuggyButtonTest, 622
ButtonFrame, 360
ButtonGroup, 417, 418
ButtonModel, 417, 418
ButtonUIListener, 398
Calendar, 140
CalendarTest, 145
CheckBoxTest, 415
CircularArrayQueue, 668
Class, 233, 248251, 255, 261, 658
Class<T>, 658, 663
CloneTest, 284
Collections, 679, 711, 715, 724
Color, 340, 342, 343
ColorAction, 360, 362
Component, 322, 325, 332, 343, 399, 408
ConcurrentHashMap, 794
ConcurrentHashMap<K, V> 5.0, 795
ConcurrentLinkedQueue<E>, 795
ConcurrentSkipListMap, 794
ConcurrentSkipListSet<E>, 795
Console, 90, 91
ConsoleHandler, 600, 607
Constructor, 250252, 256, 658
ConstructorTest, 177
Container, 362, 399
CopyOfTest, 263
CountDownLatch, 813
Cursor, 381
CyclicBarrier, 814
Date, 139, 140
DateFormatSymbols, 144, 148
DateInterval, 638
Dimension, 330
Double, 276
DrawTest, 337
Ellipse2D, 335
Ellipse2D.Double, 340
Employee, 148, 151, 163, 184, 639
EmployeeSortTest, 275
EmployeeTest, 149
Enum, 246
EnumMap, 704
EnumMap<K extends Enum<K>, V>, 706
EnumSet, 704
EnumSet<E extends Enum<E>>, 706
EnumTest, 246
EOFException, 570
EqualsTest, 230
Error, 565
EventHandler, 364, 365
EventObject, 363, 387
EventTracer, 616
Exception, 251, 565, 583
Exchanger, 814
Executors, 802
ExtendedService, 527
Field, 252, 256, 258, 261, 265
File, 497
FileContents, 531
FileFilter, 504
FileHandler, 600, 607
FileInputStream, 567
FileNameExtensionFilter, 505
FileOpenService, 526, 532
FileSaveService, 532
FileView, 497, 505
Filter, 609
FlowLayout, 400
Font, 345, 349
FontMetrics, 351
FontParamApplet, 541
FontRenderContext, 346
FontTest, 348
ForkJoinTest, 811
Formatter, 600, 609
Frame, 318, 326
FutureTask<V>, 802
FutureTest, 799
GenericReflectionTest, 661
Graphics, 332, 343, 350, 353
Graphics2D, 332, 333, 343, 351
GraphicsDevice, 325, 617
GraphicsEnvironment, 344, 620
GregorianCalendar, 139143, 146, 654
GridBagConstraints, 454, 458
GridLayout, 399, 405
GroupLayout, 459, 463, 466
Handler, 598, 607
837
838
Java. Podstawy
klasa
HashMap<K, V>, 701
HashSet, 684, 686
HashSet<E>, 687
Hashtable, 709, 726
IdentityHashMap, 705
IdentityHashMap<K, V>, 706
ImageIcon, 327, 351
ImagePreviewer, 499
ImageTest, 352
InnerClassTest, 292
InputEvent, 386
InputMap, 376
Integer, 242, 276, 308
Iterator, 290
JApplet, 533
JButton, 361, 397
JCheckBox, 415
JCheckBoxMenuItem, 436
JColorChooser, 505, 509
JComboBox, 425, 725
JComponent, 328, 347, 351, 379, 408, 419, 438
JDialog, 484, 488
JEditorPane, 412
JFileChooser, 495, 503
JFrame, 318, 321, 331, 434, 484
klasa JLabel, 408, 499
klasa JList, 425
JMenu, 433
JMenuBar, 432
JMenuItem, 434, 440
JOptionPane, 288, 474, 481, 484
JPanel, 330
JPasswordField, 406, 410
JPopupMenu, 437
JRadioButton, 418
JRadioButtonMenuItem, 436
JScrollPane, 413
JSlider, 426, 431, 640
JTextArea, 406, 410, 412
JTextComponent, 406
JTextField, 406, 408
JToolBar, 445, 447
KeyStroke, 375, 379
LayoutManager, 472
Line2D.Double, 340
LineBorder, 422
LineMetrics, 347, 350
LinkedBlockingQueue<E>, 792
LinkedHashMap, 702, 704
LinkedHashMap<K, V>, 705
LinkedHashSet, 702
LinkedHashSet<E>, 705
LinkedList, 290, 668, 675, 684, 694, 713
LinkedListQueue, 668
LinkedListTest, 681
LinkedTransferQueue, 788
ListIterator, 679
Lock, 762
Logger, 605
LogManager, 595
LogRecord, 609
LookAndFeelInfo, 368
Manager, 232
ManagerTest, 205
MapTest, 699
Math, 73, 74
MenuListener, 441
Method, 252, 265, 663
MethodTableTest, 266
Modifier, 252, 256
MouseEvent, 380, 386
MouseHandler, 383
MouseMotionHandler, 383
MouseMotionListener, 381
Object, 220
ObjectAnalyzer, 258
ObjectAnalyzerTest, 259
PackageTest, 183, 184
Pair, 303, 637, 641, 648, 655
Pair<T>, 659
PairTest1, 631
PairTest2, 635
PairTest3, 656
ParallelGroup, 468
ParamTest, 169
PasswordChooser, 490
Paths, 97
PersistenceService, 532
PersonTest, 217
PlafFrame, 368
Point2D, 335
Point2D.Double, 340
Preferences, 556, 560
PreferencesFrame, 558
PrintWriter, 97
PriorityBlockingQueue<E>, 792
PriorityQueue, 697
PriorityQueueTest, 697
Properties, 550555, 709, 728
PropertiesTest, 551
Proxy, 307, 311
ProxyTest, 309
Rectangle2D, 334, 335
Rectangle2D.Double, 334, 339
Rectangle2D.Float, 334, 339
RectangularShape, 335, 339
RecursiveTask<T>, 810
Skorowidz
ReentrantLock, 762764, 783
ReentrantReadWriteLock, 783
ReflectionTest, 253
ResourceBundle, 596
ResourceTest, 517
Robot, 617, 618
Runnable, 746
RuntimeException, 566568, 583
Scanner, 89, 90, 97
SequentialGroup, 468
ServiceManager, 531
SetTest, 686
ShuffleTest, 721
SimpleDateFormat, 781
SimpleFrame, 319
SimpleFrameTest, 318
Singleton, 645
SizedFrameTest, 324
SoftBevelBorder, 421, 422
SortedMap<K, V>, 701
SQLException, 576
Stack, 709, 729
StackTraceElement, 581, 583
StackTraceTest, 582
StaticInnerClassTest, 305
StaticTest, 162
StreamHandler, 598
StrictMath, 74
String, 77, 83, 86, 211
StringBuilder, 86, 88
SwingThreadTest, 818
SwingUtilities, 494
SwingWorker, 820, 824, 826
SwingWorkerTest, 821
SynchronousQueue, 815
System, 43, 554
SystemColor, 341
TalkingClock, 289, 295297
Thread, 647, 746, 749753
ThreadGroup, 754, 755
ThreadLocal, 781
ThreadLocal<T>, 782
ThreadPoolTest, 804
Throwable, 251, 565, 570, 581, 583
TimePrinter, 291295
Timer, 286288
TimerTest, 287
ToolBarTest, 447
Toolkit, 288, 323, 327, 386
TraceHandler, 307
TransferRunnable, 780
TreeMap<K, V>, 701
TreeSet, 688, 691
839
840
Java. Podstawy
klauzula
catch, 572, 748
finally, 576, 578
throws, 96, 567
try, 572
klawiatura, 375
klawisze specjalne, 380
klonowanie obiektw, 280, 285
klucz URL, 528
klucze, 698
klucze nalece do wza, 560
kod
bajtowy, 26
bdu, 570
kod generyczny, 630
kod maszynowy, 26
kod mieszajcy, hash code, 225, 684, 687
kod oglny, 635
kod wyjcia, exit code, 60
kodowanie
Unicode, 65, 67
UTF-16, 66, 82
kolejka, queue, 666
ArrayBlockingQueue, 788
DelayQueue, 788
Deque, 694
LinkedBlockingQueue, 787
PriorityBlockingQueue, 788
Queue, 694
kolejki
blokujce, 786
dostpu, 472
priorytetowe, 696
synchroniczne, 815
kolejno
dostpu, 703
dostpu do komponentw, 473
ogranicze, 637
kolekcja, 117, 665
par, 698
wtkw, 754
kolekcje
bezpieczne wtkowo, 794, 796
ograniczone, 668
uporzdkowane, 677, 708
w bibliotece, 675
kolizja nazw, 180
kolizje, 685
kolor, 340
kolor ta, 341, 507
komentarze, 61
do klas, 191
do metod, 191
do pakietw, 194
do pl, 192
dokumentacyjne, 190
oglne, 192
komparator, 690
kompilacja
programu, 44
w czasie rzeczywistym, 26
kompilator, 25, 45, 776
czasu rzeczywistego, 34
javac, 188
JIT, 27, 212
komponenty
Swing, 391510
tekstowe, 411
kompresja ZIP, 512
komunikacja
midzy apletami, 540, 547
midzyprocesowa, 736
komunikat, 592
o bdzie, 46, 50, 569
o wyjtkach, 816
konektor UML, 136
konfiguracja
komponentw, 319
menedera dziennikw, 598
projektu, 48
konflikt metod, 648
konkatenacja, 78
konsola, 44
konstruktor, 137, 152
bezargumentowy, 172
domylny, 172
kopiujcy, 140
przeciony, 172
wirtualny, 250
konstruktory
klasy FileHandler, 607
klasy HashSet<E>, 687
klasy TreeMap<K, V>, 701
klasy TreeSet<E>, 694
kontekst
graficzny, 328
urzdzenia, 328
kontener, 330, 399
kontrola
dostpu, 290
nazw, 290
typw, 627
kontroler, controller, 394
konwersja
acucha na liczb, 242
pomidzy kolekcjami a tablicami, 718
programw na aplety, 536
tablic, 718
typw, 650
typw numerycznych, 74
Skorowidz
koczenie dziaania programu, 60
kopie
acucha, 80
obrazu, 352
kopiowanie
gbokie, 281
obiektw, 280
pytkie, 281
tablicy, 119
zmiennej tablicowej, 119
koszty uzyskania certyfikatu, 524
kowariantne typy zwrotne, 209, 639
kubeek, bucket, 685
kursory, 382
kwalifikator .this, 294
L
licencja GPL, 34
liczba
klikni, 381
parametrw, 244
liczby
cakowite, 62
zmiennoprzecinkowe, 64
linia
bazowa, 346, 347
dolna pisma, 346
grna pisma, 346
lista
ArrayList, 117
kluczy, 556, 698
modyfikowalna, 721
rozwijalna, combo box, 423
wtkw, 779
listy
cykliczne, 666, 668
dwukierunkowe, 25, 238, 674
powizane, 668, 674, 680
tablicowe, 234, 236, 684
surowe, 239
z typem, 240
lokalizacja, 143, 596
komunikatw, 596
pliku, 546
lokalne klasy wewntrzne, 289, 296
adowanie
klas, 307
pliku w osobnym wtku, 821
zasobw, 516
841
acuch
_blank, 548
dziedziczenia, 206
null, 81
prompt, 91
pusty, 81
testowy, 407
wyjtkw, 575
acuchy, 77
czenie, 78
modyfikowanie, 79
porwnywanie, 79
skadanie, 86
wspdzielenie, 79
zmienialne, mutable, 80
czenie narastajce, 27
M
makro assert, 588
manifest, 512
mapa, 697
akcji, 376
HashMap, 698
haszowa, 795
wejcia, 376
wasnoci, property map, 550, 553, 728
mapy klawiaturowe, 376
maska bitowa, 380
maszyna wirtualna, JVM, 26, 514
opcja -verbose, 612
opcja -Xlint, 612
opcja -Xprof, 614
ME, Micro Edition, 38
meneder
dziennikw, 595
zabezpiecze, 522
menu, 432
menu podrczne, pop-up menu, 437
metadane, 32
metoda, 133
accept, 504
acquire, 812
actionPerformed, 286, 291, 357, 368, 414, 433
add, 142, 237, 399, 433, 629, 677
addActionListener, 364
addAll, 629, 672, 682
addBall, 737, 742
addChangeListener, 426
addChoosableFileFilter, 504
addComponent, 467
addContainerGap, 468
addFirst, 683
addGap, 467
842
Java. Podstawy
metoda
addGroup, 467
addItem, 423, 426
addLast, 683
addPropertyChangeListener, 373
addSeparator, 434, 447
addSuppressed, 583
addWindowListener, 370
akcesora get, 141
and, 730
andNot, 730
append, 87, 413
appendCodePoint, 88
Arrays.hashCode, 227
Arrays.toString, 230
asList, 711
await, 766, 769, 783, 814
awaitUninterruptibly, 783
beep, 289
binarySearch, 123, 308, 722
BorderFactory, 421
brighter, 341
call, 801
cancel, 802
canRead, 532
canWrite, 532
cast, 658
ceiling, 694
charAt, 83
checkedCollection, 713
clear, 672, 730
clone, 280284
close, 179, 580, 600, 607, 762
codePointAt, 83
codePointCount, 84
compare, 276, 690, 693
compareTo, 83, 246, 272274, 299, 634, 653,
693, 720
Component.show, 320
config, 605
console, 91
contains, 672, 680, 686
containsAll, 672, 673
containsKey, 700
containsValue, 700
copy, 724
copyArea, 352, 354
copyOf, 123
countdown, 813
create, 365
createCompoundBorder, 422
createCustomCursor, 382, 386
createEmptyBorder, 421
createEtchedBorder, 421
createFont, 345
createLineBorder, 421
createLoweredBevelBorder, 421
createMatteBorder, 421
createParallelGroup, 467
createRaisedBevelBorder, 421
createScreenCapture, 618
createTitledBorder, 422
darker, 341
decrementAndGet, 777
metoda delay, 621
metoda delete, 88
deriveFont, 345, 350
destroy, 537
disjoint, 725
divide, 115
doInBackground, 824826
draw, 333, 340
drawImage, 353
drawString, 329, 341, 347, 351
elements, 728
endsWith, 83
ensureCapacity, 236
entering, 600, 605
entrySet, 700
equals, 79, 83, 124, 221, 242, 648, 705
equalsIgnoreCase, 83
execute, 827
exiting, 600, 605
fill, 340, 343, 724
fillMenu, 725
finalize, 179
fine, 605
finer, 605
finest, 605
firstKey, 701
floor, 694
flush, 600, 607
Font.createFont, 345
format, 609
formatMessage, 609
formatTo, 92
forName, 248, 251
frame.setUndecorated, 320
frequency, 725
get, 142, 236, 261, 264, 628, 682
getActionCommand, 363, 388, 417
getActionMap, 379
getActualTypeArguments, 664
getAllItems, 726
getAncestorOfClass, 494
getApplet, 540
getAppletContext, 547, 549
getAppletInfo, 545
Skorowidz
getApplets, 548, 549
getAudioClip, 547
getAutoCreateContainerGaps, 467
getAutoCreateGaps, 467
getAvailableFontFamilyNames, 344
getBackground, 343
getBounds, 664
getCause, 583
getCenterX, 339
getClass, 222, 248, 641
getClassName, 368, 584
getClickCount, 380, 386
getCodeBase, 528, 531
getColor, 343, 510
getColumns, 408
getComponentPopupMenu, 438
getConstructor, 658, 659
getConstructors, 252, 255
getContentPane, 327, 331
getDeclardeFields, 261
getDeclaredConstructor, 658
getDeclaredConstructors, 252, 256
getDeclaredField, 261
getDeclaredFields, 252, 255, 258
getDeclaredMethods, 252, 255
getDeclaringClass, 256
getDefaultScreenDevice, 621
getDefaultToolkit, 288, 323, 327
getDelay, 788, 792
getDescent, 350
getDescription, 505
getDocumentBase, 546
getDouble, 258, 610
getEnumConstants, 658
getExceptionTypes, 256
getExtendedState, 326
getFamily, 349
getField, 261
getFields, 252, 255, 261
getFileName, 583
getFilter, 607
getFirst, 637, 652, 683
getFirstDayOfWeek, 143
getFont, 350, 408
getFontMetrics, 347, 351
getFontName, 349
getFontRenderContext, 346, 351
getForeground, 343
getFormatter, 607
getGenericComponentType, 664
getGenericInterfaces, 663
getGenericParameterTypes, 663
getGenericReturnType, 663
getGenericSuperclass, 663
getHandlers, 606
getHead, 609
getHeight, 347
getHonorsVisibility, 467
getIcon, 409, 505
getIconImage, 326
getImage, 327, 547
getInheritsPopupMenu, 438
getInputMap, 376, 379
getInputStream, 526, 531
getInstalledLookAndFeels, 368
getKey, 701
getKeyStroke, 375
getLast, 683
getLeading, 350
getLength, 262, 264
getLevel, 606, 607
getLineMetrics, 347, 350
getLineNumber, 584
getLocalGraphicsEnvironment, 344
getLogger, 605
getLoggerName, 608
getLowerBounds, 664
getMaxX, 339
getMessage, 571, 608
getMethodName, 584
getMethods, 255
getMethodsi, 252
getMillis, 608
getMinX, 339
getModifiers, 252, 256
getModifiersEx, 381, 386
getModifiersExText, 386
getMonths, 148
getName, 150, 249, 349, 505, 664
getNewState, 372
getOldState, 372
getOutputStream, 526, 532
getOwnerType, 664
getPaint, 343
getParameter, 541, 545
getParameterInfo, 546
getParameters, 608
getParameterTypes, 256
getParent, 606
getPassword, 410
getPoint, 386
getPredefinedCursor, 381
getPreferredSize, 330
getProperties, 550, 554
getProperty, 553, 728
getProxyClass, 311
getRawType, 664
getResource, 516
843
844
Java. Podstawy
metoda
getResourceBundle, 608
getResourceBundleName, 608
getReturnType, 256
getRootPane, 494
getSalary, 214, 265
getScreenSize, 323, 327
getSelectedFile, 504
getSelectedItem, 423426
getSelectedObjects, 417
getSelection, 417, 418
getSequenceNumber, 609
getServiceNames, 531
getShortMonths, 148
getShortWeekdays, 144, 148
getSource, 388, 424
getSourceClassName, 608
getSourceMethodName, 608
getStackTrace, 581, 583
getState, 752
getStringBounds, 346
getSuperclass, 659
getSuppressed, 581
getTail, 609
getText, 406, 409
getThreadID, 609
getThrown, 608
getTitle, 322, 326
getTotalBalance, 764
getType, 252
getTypeDescription, 505
getTypeParameters, 663
getUpperBounds, 664
getUseParentHandlers, 607
getValue, 373, 379, 701
getWeekdays, 148
getWidth, 334, 335, 339, 346
getX, 339, 386
getY, 386
hashCode, 225, 684, 706
hasMoreElements, 670, 727
hasNext, 91, 669, 674, 677
hasNextDouble, 91
hasNextInt, 91
headMap, 712, 716
headSet, 712, 716
higher, 694
IconImage, 522
in.close, 580
incrementAndGet, 777
indexOf, 84
indexOfSubList, 724
info, 605
init, 536
initCause, 583
initialize, 782
initialValue, 781
insert, 88, 434
insertItemAt, 426
InsertItemAt, 424
insertSeparator, 434
interrupt, 746, 749
interrupted, 748, 749
intValue, 243
invoke, 265267, 311
invokeAll, 808, 810
invokeAndWait, 820
invokeAny, 808
invokeLater, 817, 820
isAbstract, 256
isAccessible, 261
isDispatchThread, 820
isDone, 777
isEditable, 406, 425
isEmpty, 672, 673
isEnabled, 373, 379
isFinal, 252, 256
isInterface, 256
isInterrupted, 746749
isJavaIdentifierPart, 67
isJavaIdentifierStart, 67
isLocationByPlatform, 323, 326
isLoggable, 600, 609
isNative, 256
isNativeMethod, 584
isPopupTrigger, 438
isPrivate, 252, 256
isProtected, 256
isProxyClass, 311, 312
isPublic, 252, 256
isResizable, 326
isSelected, 415, 436
isStatic, 256
isStrict, 257
isSynchronized, 257
isTraversable, 498, 505
isUndecorated, 326
isVisible, 325
isVolatile, 257
isWebBrowserSupported, 531
itemComparator, 720
iterator, 668, 672
join, 752, 810
JTextField, 406
keyPress, 621
keyRelease, 621
keySet, 700
KeyStroke, 379
Skorowidz
lastIndexOf, 84
lastIndexOfSubList, 724
lastKey, 701
layoutContainer, 472
length, 84, 730
linkSize, 466
listFiles, 497
listIterator, 677, 682
load, 554, 728
lock, 762, 764, 782
lockInterruptibly, 783
log, 593, 606
logp, 606
logrb, 606
lookup, 531
lower, 694
main, 59, 148, 162, 249
makeButton, 362
makePair, 644
Math.random, 122
Math.round, 75
menuCanceled, 441
menuDeselected, 441
menuSelected, 441
minimumLayoutSize, 472
mod, 115
modifiers, 256
mouseClicked, 380, 381
mouseDragged, 382
mouseEntered, 383
mouseExited, 383
mouseMove, 621
mouseMoved, 381, 382
mousePress, 621
mousePressed, 380, 381
mouseRelease, 621
mouseReleased, 380
move, 736
multiply, 115, 116
mutatora set, 142
newCondition, 765, 769
newFixedThreadPool, 803
newInstance, 249251, 262, 658
newProxyInstance, 307, 311
newScheduledThreadPool, 807
newSingleThreadScheduledExecutor, 807
next, 669, 679
nextDouble, 89, 91, 610
nextElement, 670, 727
nextInt, 89, 91
nextLine, 89, 91
node, 560
notify, 773
notifyAll, 773
Object.clone, 282
Objects.hashCode, 227
offer, 787, 793
offerFirst, 793
offerLast, 793
offsetByCodePoints, 83
openFileDialog, 526, 532
openMultiFileDialog, 532
or, 730
ordinal, 247
pack, 330, 332
paintComponent, 328, 347, 475, 569
parse, 244
parseInt, 242, 243
peek, 729, 787
play, 546
poll, 787, 793, 809
pollFirst, 694, 793
pollLast, 694, 793
pop, 729
preferredLayoutSize, 472
previous, 677680
previousIndex, 680
print, 96
printBuddies, 651
printf, 92, 244
println, 60, 96, 229, 310
printStack, 251
printStackTrace, 251, 581, 611
process, 826
publish, 599, 607, 825
push, 729
put, 556, 787, 792
putFirst, 793
putIfAbsent, 794
putLast, 793
putValue, 373, 379
raiseSalary, 284
readConfiguration, 595
readLine, 91
readLock, 784
readPassword, 91
release, 812
remove, 434, 669673, 678, 682
removeAll, 672, 673, 681
removeAllItems, 426
removeEldestEntry, 705
removeFirst, 683
removeHandler, 607
removeItem, 424, 426
removeItemAt, 424, 426
removeLast, 683
removeLayoutComponent, 472
removePropertyChangeListener, 373
845
846
Java. Podstawy
metoda
repaint, 329, 332, 827
replace, 84, 796
replaceAll, 724
res.close, 580
resetChoosableFileFilters, 504
resize, 537
resume, 752
retainAll, 672, 717
revalidate, 407, 408
reverse, 724
reverseOrder, 722
rotate, 724
run, 647, 754
Runtime.addShutdownHook, 179
saveAsFileDialog, 532
saveFileDialog, 526
schedule, 807
scheduleAtFixedRate, 807
scheduleWithFixedDelay, 808
ServiceManager, 526
set, 142, 236, 261, 679, 682
setAccelerator, 440
setAccessible, 257, 261
setAccessory, 504
setAction, 434
setActionCommand, 363, 417, 419
setAutoCreateContainerGaps, 467
setAutoCreateGaps, 467
setBackground, 341, 343
setBorder, 419, 423
setBounds, 321, 322, 325, 468
setCharAt, 88
setColor, 343, 510, 625
setColumns, 408, 410, 413
setComponentPopupMenu, 438
setCursor, 386
setDaemon, 754
setDebugGraphicsOptions, 615
setDefaultButton, 494
setDefaultCloseOperation, 320, 536
setDefaultUncaughtExceptionHandler, 611
setDone, 777
setEditable, 406, 423, 425
setEnabled, 373, 379, 441
setExtendedState, 325, 326
setFileFilter, 497, 504
setFileSelectionMode, 503
setFileView, 499, 504
setFilter, 600, 607
setFirst, 652
setFont, 350, 408
setForeground, 341, 343
setFormatter, 601, 607
setFrameFromCenter, 337
setFrameFromDiagonal, 336
setHonorsVisibility, 467
setHorizontalGroup, 466
setHorizontalTextPosition, 435
setIcon, 409, 435
setIconImage, 321, 326
setInheritsPopupMenu, 438
setJMenuBar, 432, 434
setLabelTable, 428, 431, 640
setLayout, 399
setLevel, 606, 607
setLineWrap, 411, 413
setLocation, 321, 325
setLocationByPlatform, 322, 326
setLookAndFeel, 368
setMajorTickSpacing, 431
setMinorTickSpacing, 431
setMnemonic, 440
setModel, 424
setMultiSelectionEnabled, 503
setPaint, 340, 341, 343
setPaintLabels, 428, 431
setPaintTicks, 428, 431
setPaintTrack, 432
setParent, 606
setPriority, 753
setRect, 335
setResizable, 321, 326
setRows, 410, 413
setSecond, 638
setSelected, 414, 436
setSelectedFiles, 503
setSize, 325
setSnapToTicks, 432
setSource, 363
setText, 406, 407, 409
setTitle, 321, 326, 537
setToolTip, 446
setToolTipText, 447
setUncaughtExceptionHandler, 754
setUndecorated, 326
setUseParentHandlers, 607
setValue, 701
setVerticalGroup, 466
setVisible, 320, 325, 489, 537, 827
setWrapStyleWord, 413
severe, 605
show, 320, 437
showConfirmDialog, 474, 476, 482
showDialog, 490
showDocument, 531, 548, 549
showInputDialog, 475, 476, 483
showInternalConfirmDialog, 482
Skorowidz
showInternalInputDialog, 484
showInternalMessageDialog, 482
showMessageDialog, 288, 474, 481, 530
showOptionDialog, 474, 476
showSaveDialog, 495
showStatus, 548, 549
shuffle, 721, 722
shutdown, 803
shutdownNow, 804
signal, 769, 780
signalAll, 766769
size, 236, 672, 673
sleep, 737, 742
sort, 121, 274, 720
start, 288
startsWith, 84
stateChanged, 426
stop, 288, 752, 784
store, 550, 554, 728
subList, 716
subMap, 712, 716
submit, 803, 809
subSet, 712, 716
substring, 78, 84, 711
subtract, 115, 116
super.clone, 282
super.paintComponent, 330
suspend, 752, 785
swap, 168, 724
swapHelper, 656
SwingUtilities.updateComponentTreeUI, 366
synchronizedCollection, 713, 797
synchronizedList, 797
synchronizedMap, 713, 797
synchronizedSet, 797
synchronizedSortedMap, 797
synchronizedSortedSet, 797
System.exit, 60, 320
System.out.println, 89
System.runFinalizersOnExit, 179
systemNodeForPackage, 560
systemRoot, 560
tailMap, 712, 716
tailSet, 712, 716
take, 793
takeFirst, 793
takeLast, 793
text, 91
Thread.getAllStackTraces, 581
ThreadLocalRandom.current, 781
throwing, 594, 606
toArray, 237, 645, 672, 718
toBack, 326
toFront, 322, 326
847
848
Java. Podstawy
metody
interfejsu
TypeVariable, 664
WildcardType, 664
WindowListener, 369, 372
klas wewntrznych, 289
klasy
AccessibleObject, 261
Applet, 537, 545, 546
Array, 264
Arrays, 123
BigDecimal, 116
BigInteger, 115
BitSet, 730
BorderFactory, 421
Class, 252, 255
Class<T>, 658
Collections, 712, 715, 722, 724
Component, 325
Console, 91
Constructor, 256
Date, 141
Employee, 151
Executors, 803
FileView, 498, 505
Font, 349
Frame, 326
Graphics, 350
Graphics2D, 351
GregorianCalendar, 147
GroupLayout, 466
Integer, 243
JComboBox, 425
JFileChooser, 503
JFrame, 321
JMenu, 433
JOptionPane, 481
JSlider, 431
JTextArea, 412
JTextComponent, 406
LayoutManager, 472
LineMetrics, 350
Logger, 605
Modifier, 256
Object, 773
Preferences, 560
Properties, 553
RectangularShape, 335, 339
Robot, 621
Scanner, 90
String, 83, 87
StringBuilder, 88
Thread, 749755
ThreadLocal<T>, 782
Throwable, 583
Timer, 288
Toolkit, 327
Window, 326
kolejek blokujcych, 787, 788
komentarze, 191
monitorowe, 775
o zmiennej liczbie parametrw, 244
odradzane, 141
oglne, 632
pomostowe, 638, 649
prywatne, 157
przecianie, 171
przesaniajce, 201
publiczne, 59
rejestrujce, 594
rodzime, 160
statyczne, 73, 160, 645
statyczne ze zmiennymi typowymi, 645
sygnatura, 171, 209
synchronizowane, 771
tworzce niemodyfikowalne widoki, 712
udostpniajce, 141
uoglnione, 671
wstawiane, 154
z parametrami typowymi, 632
zmieniajce warto elementu, 141
mieszanie wsprzdnych, 691
mnemoniki, 438
modalno, 485
model, 394, 395
pola tekstowego, 394
wskanikowy, 24
modu adujcy klasy, 588
modyfikacja
parametru obiektowego, 167
zbioru EnumSet, 704
modyfikator
dostpu, 58, 220
dostpu private, 186
dostpu public, 185
final, 158, 211, 777
volatile, 777
zdarzenia, 386
modyfikowanie elementw zbioru, 687
monitor, 775
motyw Ocean, 316
mutatory, 142
MVC, Model-View-Controller, 392
mysz, 380
Skorowidz
N
nadklasa, superclass, 200
nadtypy, 652, 654
NaN, 64, 70
narzdzia
graficzne, 317
wiersza polece, 44
narzdzie
appletviewer, 53, 535
ButtonTest, 618
ImageViewer, 51, 500
jar, 512, 514
Jar Bundler, 515
javac, 45
javadoc, 190195
javap, 294
jconsole, 595, 613, 779
jmap, 614
Matisse, 460, 461
OptionDialogTest, 477
ReflectionTest, 295, 296
Swing graphics debugger, 614
natywna biblioteka interfejsowa, 33
nawiasy
klamrowe, 59, 300
kwadratowe, 116
ostre, 632
puste, 234
nazwa
akcji, 377
klasy, 45, 58, 134, 197
konstruktora, 137
parametru, 174
pliku, 45
pliku dziennika, 598
rejestratora, 595
zasobu, 516
zmiennej, 67
zmiennej typowej, 630
nazwy
klas komponentw Swing, 318
klas proxy, 311
logiczne czcionek, 344
metod, 197
rodziny czcionek, 343
NetBeans, 38, 44, 459
niezaleno od architektury, 26
niezawodno, 24
niezmienialno acuchw, 79
niszczenie obiektw, 179
notacja wielbdzia, 58
849
O
obiekt, 24, 132
Action, 446
ActionEvent, 357
AssertionError, 587
BasicButtonUI, 398
builder, 86
ButtonGroup, 415
Callable, 803
ColorAction, 360
Comparator, 693
Console, 90
Date, 138
DefaultButtonModel, 398
ExecutorCompletionService, 808
FutureTask, 798
Graphics, 328, 341
GridLayout, 406
Handler, 595, 598, 608
Iterator, 672
JButton, 397
JPanel, 402
JRadioButton, 415
PaintEvent, 388
Path, 97
PrintWriter, 96, 97
Properties, 553
Runnable, 759, 803
Scanner, 96
System.out, 60
WeakReference, 702
obiektw, 133
autoboxing, 241
hermetyzacja, 133
klonowanie, 280
kopiowanie, 280
metody, 133
metody prywatne, 157
niszczenie, 179
polimorfizm, 204
porwnywanie, 221
skadowe, 133, 155
stan, 133, 134
tosamo, 134
waciwoci, 133
zachowanie, 133
obiekty
blokady, 762
funkcyjne, 690
klasy Class, 249
klasy Lock, 762
klasy oglnej, 646
nasuchujce zdarze, listener objects, 287, 371
850
Java. Podstawy
obiekty
obsugujce wywoanie, 307
opakowujce kolekcje, 711
proxy, 308
typu wyliczeniowego, 548
warunkw, 765
zdarze, event objects, 356
obliczanie kodu mieszajcego, 226
obramowanie, 419
obrazy, 351
obsuga
apletw, 533
bdw, 564
kliknicia przycisku, 357
nieprzechwyconych wyjtkw, 754
przycisku OK, 486
ramek, 325
wyjtkw, 249, 564, 646
zdarze, 329, 355
zdarze AWT, 389, 390
zdarze myszy, 380, 383
obszar
surogatw, surrogates area, 66
tekstowy, text area, 406, 410
ochrona bloku kodu, 762
odczyt plikw, 96
odmierzanie czasu, 782
odpakowywanie, 242
odradzane metody, 141
odwoanie, 193
odwzorowanie nazw czcionek, 345
ograniczenia
blokad wewntrznych, 771
nadtypw, 652, 653
typw generycznych, 240
widocznoci metody, 219
widokw, 714
zmiennych typowych, 633
okna dialogowe
modalne, 474
niemodalne, 474
okno, 369, 372
dialogowe
opcji, 474, 483
potwierdzenia, 482
przyjmowania danych, 484
typu O programie, 485
wyboru kolorw, 505
wyboru plikw, 495
z komunikatem, 482
z polem hasa, 489
dokumentacji API, 85
konsoli, 44
przegldarki apletw, 53
wyboru plikw, 498, 558
P
pakiet
com.horstmann.corejava, 183
com.mycompany.mylib, 588
com.mycompany.util, 518
com.sun.java, 366
domylny, 183
java.awt, 186
java.awt.event, 286, 388
java.lang, 83, 90
java.lang.reflect, 252, 660
java.math, 114
Skorowidz
java.sql, 181
java.util, 90, 181, 387
java.util.concurrent, 762, 771, 797
java.util.concurrent.atomic, 777
java.util.concurrent.locks, 783
javax.swing, 286, 319
javax.swing.event, 390
JDK, 38
org.omg.CORBA, 243
Swing, 314
pakiety, packages, 180
dodawanie klasy, 182
komentarze, 194
lokalizacyjne, 596
zasig, 185
panel
JPanel, 398
przewijany, scroll pane, 411, 413
z przyciskami, 359, 398
para klucz warto, 556, 561, 697
parametr, 61
anchor, 452
fill, 452
gridheight, 451, 452
gridwidth, 451, 452
gridx, 451, 452
gridy, 451, 452
parametry
jawne, 153
konfiguracyjne obiektu Handler, 598
acuchowe, 96
metod, 164
niejawne, 153, 174
obiektowe, 167
okrelajce cel, 549
typowe, 628, 641
wiersza polece, 120
pasek
menu, 432
narzdzi, toolbar, 444
ptla
do-while, 102
for, 98, 106108
for each, 32, 98, 117, 126
w stylu for each, 669
while, 101, 102
piaskownica, sandbox, 519, 522
piecztowanie pakietw, 186, 518
plik
AboutDialog.java, 487
ActionFrame.java, 377
AnonymousInnerClassTest.java, 301
ArrayListTest.java, 238
Ball.java, 739
BallComponent.java, 740
Bank.class, 761
Bank.java, 758, 767, 772
BigIntegerTest.java, 115
BlockingQueueTest.java, 789
BorderFrame.java, 420
Bounce.java, 737
BounceThread.java, 743
BuggyButtonTest.java, 622
ButtonFrame.java, 360
ButtonPanel.java, 481
Calculator.jnlp, 519
CalculatorFrame.java, 528
CalculatorPanel.java, 403
CalendarTest.java, 144
Chart.java, 543
CheckBoxTest.java, 414
CircleLayout.java, 469
CircleLayoutFrame.java, 472
CloneTest.java, 284
ColorChooserPanel.java, 508
ComboBoxFrame.java, 424
CompoundInterest.java, 125
ConstructorTest.java, 177
CopyOfTest.java, 263
DataExchangeFrame.java, 491
deskryptora, 519
DialogFrame.java, 486
doc-files, 191
DrawTest.java, 337
Employee.java, 151, 184, 205, 231, 275, 284
EmployeeSortTest.java, 275
EmployeeTest.java, 149, 151
en.properties, 596
EnumTest.java, 246
EqualsTest.java, 230
EventTracer.java, 615
FileIconView.java, 503
fontconfig.properties, 345
FontFrame.java, 454, 463
FontTest.java, 347
forkJoinTest.java, 810
FutureTest.java, 799
GBC.java, 456
GenericReflectionTest.java, 661
ImagePreviewer.java, 502
ImageTest.java, 352
ImageViewer.java, 51
ImageViewerFrame.java, 500
InnerClassTest.java, 292
InputTest.java, 89
Item.java, 692
javan.log, 597
javaws.jar, 526
851
852
Java. Podstawy
plik
jogging.properties, 594
LinkedListTest.java, 681
LoggingImageViewer.java, 602
LotteryArray.java, 128
LotteryDrawing.java, 121
LotteryOdds.java, 108
Manager.java, 206, 232
ManagerTest.java, 204
MANIFEST.MF, 512
manifestu
klasa gwna, 514
sekcja gwna, 512
wstawianie sekcji, 518
zmienianie zawartoci, 513
MapTest.java, 699
MenuFrame.java, 442
MethodTableTest.java, 266
MouseComponent.java, 383
MouseFrame.java, 383
NotHelloWorld.java, 330, 534
ObjectAnalyzerTest.java, 259
OptionDialogFrame.java, 477
overview.html, 194
PackageTest.java, 184
PairTest1.java, 631
PairTest2.java, 634
PairTest3.java, 656
ParamTest.java, 169
PasswordChooser.java, 492
Person.java, 217
PersonTest.java, 217
PlafFrame.java, 367
PreferencesTest.java, 557
PriorityQueueTest.java, 696
program.properties, 551
PropertiesTest.java, 551
ProxyTest.java, 309
RadioButtonFrame.java, 417
ReflectionTest.java, 253
ResourceTest.java, 517
Retirement.java, 103
Retirement2.java, 105
RobotTest.java, 618
rt.jar, 187
SetTest.java, 686
ShuffleTest.java, 721
Sieve.cpp, 731
Sieve.java, 731
SimpleFrameTest.java, 318
SizedFrameTest.java, 324
SliderFrame.java, 428
src.zip, 41, 42
StackTraceTest.java, 582
StaticInnerClassTest.java, 305
StaticTest.java, 162
Student.java, 218
swing.properties, 366
SwingThreadTest.java, 817
SwingWorkerTest.java, 821
System.java, 43
TalkingClock$TimePrinter.class, 294
TextComponentFrame.java, 411
ThreadPoolTest.java, 804
TimerTest.java, 287
ToolBarTest.java, 446
TransferRunnable.java, 759
TreeSetTest.java, 691
UnsynchBankTest.java, 757
Welcome.java, 45
WelcomeApplet.class, 53
WelcomeApplet.html, 53
WelcomeApplet.java, 55
pliki
.class, 514
.exe, 514
.java, 58
.jnlp, 519
audio, 546
cookie, 527
graficzne, 546
JAR, 187, 512, 514
obrazw, 515
podpisane cyfrowo, 523
tekstowe, 515
XML, 600
z danymi binarnymi, 515
zasobw, 516
rdowe, 151, 189
pobieranie
hasa, 90
waciwoci systemowych, 554
podklasa, subclass, 200
Properties, 726
Stack, 726
podklasa, subclass, 200
podkradanie pracy, 812
podacuchy, 78
podmenu, 432
podpakiety, 180
podpis cyfrowy, 26, 523
podpisywanie kodu, 523
podtyp typu granicznego, 634
podziaka, 427
pola
finalne, 211
hase, 406, 410
klasowe, 159
Skorowidz
niestatyczne, 159
prywatne, 155
publiczne, 155
statyczne, 159, 176
tekstowe, 394, 406
ulotne, 776
weight, 451
wyboru, 413
pole value, 243
polecenie
cmd, 41
dir, 46
jcontrol, 536
polimorfizm, 204, 207, 269
poczenie na poziomie gniazd, 24
pooenie przyciskw, 402
porwnywanie
elementw tablic, 225
acuchw, 79, 81
obiektw, 221, 689
obiektw osonowych, 242
w ptli, 107
w podklasach, 277
powiadamianie o zdarzeniach, 358
powizana tablica mieszajca, 703
poziom
FINE, 595, 597, 601
rejestracji obiektu Handler, 597
poziomy
rejestracji, 595
wanoci komunikatw, 592
pozycjonowanie
bezwzgldne, 468
ramki, 321
preferencje
uytkownika, 549, 555
wza, 561
priorytet
informacji, 592
wtkw, 750
wtku, 752, 753
operatorw, 76
procedura
obsugi bdw, 565
obsugi zdarze, 355
proces, 736
proces inliningu, 212
program, Patrz narzdzie
programowanie
interfejsw graficznych, 391
interfejsu uytkownika, 317
obiektowe, 24, 132
oglne, generic programming, 627
paskw narzdzi, 445
proceduralne, 133
sieciowe, 33
wielowtkowe, 28
programy
jednowtkowe, 736
refleksyjne, 247
wielowtkowe, 735
projektant formy, 398
projektowanie klas, 195
prostokt, 334
protokoy sieciowe, 24
prywatne pola, 155
przechwytywanie
strumienia bdw, 611
wielu typw wyjtkw, 574
wyjtkw, 250, 571, 747
przeciganie paska narzdzi, 445
przecianie
konstruktorw, 172
metod, 171
przedzia, 712
przegldarka
apletw, 535
HotJava, 31
pamici podrcznej, 521
przejmowanie blokady, 774
przekazywanie
obiektu, 138
przez warto, 167
wyjtkw, 586
przecznik, radio button, 413
przeczniki, 415
przeczniki w elementach menu, 436
przenono, 26, 70
przepyw sterowania, 98, 111, 298
przerywanie
dziaania ptli, 111
procesu adowania, 737
wtkw, 746
przesanianie metod, 202, 225, 647
przestrzenie numeracyjne, 66
przesuwanie iteratora, 671
przeszukiwanie liniowe, 723
przetwarzanie XML, 33
przycisk, 358, 397
domylny, 490
JButton, 398
OK, 486
pooenie, 402
rozmiar, 402
w rozkadzie brzegowym, 401
przyciski radiowe, 415
przywileje klasowe, 157
853
854
Java. Podstawy
publiczne metody
akcesora, 155
mutatora, 155
publiczne pola, 155
pule wtkw, 802804
punkt wstrzymania, 623, 624
pusta mapa wasnoci, 553
R
ramka, frame, 318
nadrzdna, 485, 490
wywietlajca tekst, 327
ramki
pozycjonowanie, 321
rozmiar, 323
warstwy, 327
wasnoci, 322
wywietlanie, 320
reaktywacja wtkw, 766
referencja
do elementw typu Object, 628
do klasy zewntrznej, 291, 293
do obiektu, 138
do parametru niejawnego, 203
null, 250, 565
refleksja, reflection, 199, 247, 270, 658
analiza
funkcjonalnoci klasy, 252
obiektw w czasie dziaania programu, 257
generyczny kod tablicowy, 261
rejestr, 556
rejestr zdarze, 615
rejestratory, logger, 591, 597
rejestrujcy obiekt poredni, 610
rekordy dziennika, 597
relacje
agregacja, 135
dziedziczenie, 136
zaleno, 135
reorganizacja tablicy mieszajcej, 686
repozytorium Preferences, 555
robot, 618
rodzaje
atakw, 25
modalnoci, 485
obramowa, 419
suwakw, 429
rozkad
brzegowy, 400, 402, 448
cigy, 448
GridBagLayout, 448453
grupowy, 459, 461
komponentw, 407
siatkowy, 402, 448
sprynowy, 449
SpringLayout, 449
rozmiar
apletu, 537
ekranu, 323
ikon, 522
interpretera, 23
pola tekstowego, 407
przyciskw, 402
ramki, 323
tablicy, 117
rozmieszczanie komponentw, 398
rozstrzyganie przeciania, 171, 209
rozszerzanie
klasy, 133, 216
klasy Throwable, 646
programw, 211
stylu, 317
rysowanie, 314
figur, 333, 337
na komponencie, 328
obrazu, 354
wykresu, 543
rzutowanie, casting, 75, 212, 637, 644, 718
S
sandbox, 519, 522
scalanie list, 681
SDK, Software Development Kit, 38
SE, Standard Edition, 38
semafory, 812
separator, 445
serializacja, 539
serwer pochodzenia, 523
serwer Tomcat, 519, 520
siatka, 450
sito Eratostenesa, 730
skaner, 89
skad tekstw, 346
skadanie acuchw, 86
skadnia
diamentowa, diamond syntax, 234
Javy, 23
klas wewntrznych, 289
wewntrznych klas anonimowych, 371
skadowe, 133, 155
skadowe chronione, 220
skrty klawiszowe, 439
sabe referencje, 702
sowo kluczowe, 829
abstract, 215
assert, 587
catch, 250
class, 58
Skorowidz
extends, 200, 634
final, 68, 158, 211, 299
implements, 273, 634
import, 180
instanceof, 213
interface, 272
package, 183, 186
private, 152, 158, 220
protected, 190, 219
public, 58, 152, 220
static, 69, 160, 304
strictfp, 70
super, 202
synchronized, 762, 769, 775
this, 154, 160, 174
throws, 569
try, 250
void, 60
volatile, 776
suchacz
akcji, 414, 416
przycisku, 359
z rdami zdarze, 373
zdarze, event listener, 356, 364, 388
sortowanie, 275, 299, 720
kluczy, 701
listy elementw, 720
tablicy, 121
specyfikacja, 59
specyfikacja wyjtku, 568
specyfikator
dostpu, 150
formatu, 92, 96
throws, 283, 569, 573
sprawdzanie
parametrw, 589
pl obiektu, 265
typw, 641
typw pl, 257
zakresu, 120
sprzenie zwrotne, 286
staa, 68
ACCELERATOR_KEY, 374
ACTION_COMMAND_KEY, 374
BorderLayout.SOUTH, 401
DEFAULT, 374
Double.NaN, 64
Double.NEGATIVE_INFINITY, 64
Double.POSITIVE_INFINITY, 64
LONG_DESCRIPTION, 374
MNEMONIC_KEY, 374
NAME, 374
SHORT_DESCRIPTION, 374
SMALL_ICON, 374
stae
interfejsu Action, 374
interfejsu SwingConstants, 409
klasowe, 69
klasy BorderLayout, 401
acuchowe, 80
matematyczne, 73
statyczne, 159
stan
obiektu, 133, 134
okna, 372
wtkw, 749, 751
standard
ECMA-262, 35
IEEE 754, 64
ISO/ANSI, 80
wyraania czasu, 139
status przerwania wtku, 746
statyczne
funkcje skadowe, 60
klasy wewntrzne, 303
sterta, heap, 120, 140, 696
STL, Standard Template Library, 666
stopie powiza midzy klasami, 135
stopy oprocentowania, 126
stos, stack, 120, 729
stos wywoa, 251, 582
stosowanie
blokady, 765
dziedziczenia, 269
refleksji, 270
warunkw, 769
wyjtkw, 584
struktura
katalogw, 43
ramki JFrame, 328
struktury danych, 665
strumie
ByteArrayInputStream, 526
ByteArrayOutputStream, 526
InputStream, 526
PrintStream, 526
wejciowy, 89
styl
GTK, 316
Metal, 315, 366
Nimbus, 317
Synth, 317
style
obramowa, 419
projektowania, 196
suwak, slider, 413, 426, 430
suwak z podziak, 427
SWT, 317
855
856
Java. Podstawy
cieka
dostpu, 39
klas, 187, 189
cieki
bezwzgldne, 97
wzgldne, 97
cisa kontrola typw, 62, 274
ledzenie
przepywu wykonywania, 593
stosu, 251, 581, 755
rodowisko dziaania apletu, 547
rodowisko programistyczne
Eclipse, 44, 47
NetBeans, 44
T
tabela metod, 210
tablic, 116
inicjowanie, 118
kopiowanie, 119
numerowanie, 116
przegldanie, 117
sortowanie, 121
tablica
accounts, 760
args, 120
arrayToFill, 673
par klucz warto, 556
referencji, 628
result, 123
trjktna, 129
tablice
anonimowe, 118
generyczne, 644
kopiowane przy zapisie, 796
mieszajce, 226, 428, 684, 703
postrzpione, 127
Skorowidz
typ
boolean, 66
byte, 62
char, 65
Date, 92
double, 64
float, 63
graniczny, 634
int, 26, 62
Integer, 243
long, 63
MIME, 519, 520
osony, 265
parametryzowany, 650
short, 62
short int, 26
surowy, 636, 641, 650
wbudowany, 137
wieloznaczny, 629, 634
wieloznaczny z ograniczeniem nadtypw, 654
wyliczeniowy, 77
zwrotny, 639
typy
cakowite, 62
interfejsowe, 667
kolekcji, 675
komunikatw, 475
konwersji, 74
kursorw, 382
listowe, 651
oglne, 627, 658
parametryzowane, 627
podstawowe, 221
sparametryzowane, generic types, 32
surowe, 234
tablicowe, 221
wieloznaczne, 650, 653
bez ogranicze, 655
mechanizm chwytania, 656
wyliczeniowe, 245, 704
zmiennoprzecinkowe, 63
zwrotne kowariantne, 639
U
ukrywanie danych, 133
UML, Unified Modeling Language, 136
Unicode, 65
uruchamianie
apletu, 53, 535
aplikacji graficznej, 50
aplikacji Java Web Start, 519, 520
osobnego wtku, 741
programw w konsoli, 45
programu, 44, 49
uruchomienie kilku wtkw, 743
usugi
API, 526
JNLP, 526
ustawianie
preferencji, 557
cieki klas, 189
usuwanie
elementw, 670
elementw z kolekcji, 673
elementu z listy powizanej, 676
elementu z tablicy, 675
nieuytkw, garbage collecting, 22, 702
przedziau, 712
UTC, Coordinated Universal Time, 139
UTF-16, 66
utrata wyjtku, 579
V
varargs, 244
W
warstwa
implementacji, 666
interfejsw, 666
ramki, 327
wartoci
graniczne przedziau, 712
zwrotne okna potwierdzenia, 476
warto
NaN, 64, 70
null, 77, 81, 139, 172
skrtu, hash value, 227
warunek wstpny, 590
warunki, 765, 769
wtek, thread, 735, 815
dystrybucji zdarze, 319, 741, 785, 819, 828
roboczy, 824
sterowania, thread of control, 735
TransferRunnable, 785
wyliczeniowy, 791
zablokowany, 747
zamknity, 747
wtki
koczenie dziaania, 746
oczekujce, 766
niesynchronizowane, 763
priorytet, 752
stan
857
858
Java. Podstawy
wtki
stan
BLOCKED, 749
NEW, 749
RUNNABLE, 749
TERMINATED, 749
TIMED WAITING, 749
WAITING, 749
synchronizowane, 763
usunite z kolejki, 766
wywaszczanie, 760
zamienianie w demona, 753
zamknicie, 752
wczytywanie zasobw, 516
wejcie, 89
wejcie System.in, 96
wersje Javy, 32
wze, 556, 560
wizanie
dynamiczne, 204, 209211
statyczne, 209
widoczno metod, 211
widok, view, 394, 710
kolekcji, 715
listowy elementw, 716
mapy, 698
podprzedziau, 717
pola tekstowego, 394
widoki
kontrolowane, 714
niemodyfikowalne, 712
przedziaowe, 711
synchronizowane, 713
wielkie liczby, 114
wielko liter, 58
wielowtkowo, 28, 33, 735
wielozadaniowo, 735
wiersz polece, 44, 120
wizualne budowanie interfejsw, 51
wasne typy wyjtkw, 571
wasnoci
czcionki, 450, 454
interfejsw, 276
interfejsu ButtonModel, 397
klas proxy, 311
metody equals, 222
monitorw, 775
ramek, 322, 551
wtkw, 752
wasno, property, 322
waciwoci
list, 721
systemowe, 554
wczanie
asercji, 588
zegara, 293
wnioskowanie o typie, 632
wprowadzanie tekstu, 406
wskaniki, 25
do funkcji, 286
do metod, 264
do obiektw, 140
wspczynnik zapenienia tablicy, 686
wsprzdne
figur, 333336
kodowe znakw, 66, 81
siatki, 451
wstawianie komentarzy, 190
wybr
kolorw, 505
plikw, 495
wyciszanie wyjtkw, 586
wydajno, 27
wyduenie
dolne, 346
grne, 346
wyjtek
ArrayIndexOutOfBounds, 117
ArrayIndexOutOfBoundsException, 566, 816
ArrayStoreException, 642, 650
BadCastException, 658
Class.forName, 250
ClassCastException, 213, 262, 650, 714
CloneNotSupportedException, 283
ConcurrentModificationException, 679, 797
EmptyStackException, 584, 586
FileNotFoundException, 97, 528, 569, 648
IllegalAccessException, 257
IllegalMonitorStateException, 773
IllegalStateException, 670, 674, 683
InterruptedException, 737, 742, 748
IOException, 569, 572
NoSuchElementException, 674, 695
NullPointerException, 566, 586, 590
RuntimeException, 565, 566
ServletException, 575
ThreadDeath, 785
typu RuntimeException, 583
UnavailableServiceException, 526
UnsupportedOperationException, 711715
wyjtki
kontrolowane, 249, 567, 646
konwersji, 629
niekontrolowane, 250, 567, 647
typu IOException, 569
typu SQLException, 576
zabezpiecze, 554
wykonawcze, 565
Skorowidz
wyjtkw, 563
deklarowanie, 567
powtrne generowanie, 575
przechwytywanie, 250, 571
przechwytywanie wielu typw, 574
przekazywanie, 586
wasne typy, 571
zgaszanie, 569
wyjcie, 89
wyjcie System.out, 96
wykres, 543
wykres supkowy, 542
wyliczenia, 727
wyczanie
asercji, 588
dziedziczenia, 211
sprawdzania wyjtkw, 646
wymazywanie typw, 637648
wymiana danych, 489
wymuszanie rysowania, 329
wypenianie figur, 340
wyraenia generyczne, 637
wyrwnywanie etykiet i pl, 462
WYSIWYG, 395
wysyanie
rekordw, 597
zdarze, 319
wyszukiwanie binarne, 722
wycig, 756, 760
wywietlanie
elementw w przegldarce, 548
informacji, 327
komponentw, 614
obrazw, 52, 351
ramki, 320
rekordw dziennika, 604
tekstu, 327, 329
zasobu, 515
wywaszczanie wtku, 750, 760
wywoanie
dowolnych metod, 264
innego konstruktora, 174
przez nazw, 165
przez referencj, 164
przez warto, 164
setFirst(null), 655
wzorce
nazw plikw dziennika, 599
projektowe, 392
wzorzec
Composite, 393
Decorator, 393
MVC, 392396
Strategy, 393
859
X
XML, 33
Z
zachowanie obiektu, 133
zagniedanie
blokw instrukcji, 98
ptli, 113
zakleszczenie, deadlock, 766, 778
zaleno, 135
zamiana parametrw obiektowych, 168
zamykanie
aplikacji, 320
ramki, 320
wtkw, 752
zapenianie tablicy, 237
zapis
bdw w pliku, 611
danych w repozytorium, 556
do dziennika, 592594
plikw, 96
preferencji uytkownika, 549
zarzdca rozkadu, layout manager, 391, 400, 448
CircleLayout, 469
brzegowego, 400
cigego, 398
FlowLayout, 402
grupowego, 449
niestandardowy, 469
siatkowego, 402
zasada
jednego wtku, 816, 827
uczciwoci, 764
zamienialnoci, 207
zasig
blokowy, 98
pakietw, 185
zmiennych, 98
zasoby, resources, 515
zastosowanie
asercji, 589, 590
klas abstrakcyjnych, 279
klas wewntrznych, 294
kolejek priorytetowych, 696
parametrw Class<T>, 659
refleksji, 252
typw wyliczeniowych, 246
zawarto pl danych, 257
zawijanie wierszy, 411
zbir, set, 686, 697
HashSet, 684
TreeSet, 688691
uporzdkowany, 688
860
Java. Podstawy
znacznik
@author, 192
@deprecated, 193
@link, 194
@Override, 225
@param, 192
@see, 193
@since, 193
@version, 192
append, 599
applet, 54, 537
object, 540
param, 541, 542
znaczniki
dokumentacyjne, 190
polecenia printf, 93
znak
$, 67
/, 516, 560
ampersand, 634
konwersji, 92
koca pliku, 565
koca wiersza, 514
powrotu karetki, 60
rwnoci, 68
trzykropka, 244
znaki
dodatkowe, 66
echa, 410
konwersji Date i Time, 94, 95
konwersji polecenia printf, 93
nowego wiersza, 477
specjalne, 65
zniszczenie danych, 757
zoptymalizowane wywoywanie metod, 212
zwalnianie blokady, 764
zwracanie referencji, 157
Notatki