Professional Documents
Culture Documents
Bruce Eckel 188153676 Misliti Na Javi
Bruce Eckel 188153676 Misliti Na Javi
na
Javi
Prevod četvrtog izdanja
Bruce Eckel
MindVievv, Inc., p re d s e d n ik
Preveo
Miljenko Šućur
P R E N T IC E
H ALL
M isliti na Javi, prevod četv rto g izdanja
G lavn i u re d n ik O lg a M ila n k o
R e d a k to ri Stela Spasić
S n ežan a B isenić
T ehnički u re d n ik S an ja Tasić
L e k to r i k o re k to r Vesna Đ u k ić
R ealizacija k o ric a N ataša Pavlov
P relom tek sta i o b r a d a slika San ja Tasić
M iiica D ečan sk i
N ataša Pavlov
š ta m p a P u b lik u m , B eograd
M ik ro k n jig a
P. fah 20 -87
11030 B eog rad
tel: 01 1/3 5 4 0 -5 4 4
pi sma@mi k ro k n j i g a . r s
llo p v rig h t • 2 007 M ik ro k n jig a . Sva p rav a z a d rž a n a . N ije d o z v o lje n o d a ije d a n d eo ove knjige b u d e re p ro d u k o v a n
ili e m ito v a n n a bilo koji n a č in , e le k tro n sk i ili m e h a n ič k i, u k lju ć u iu ć i fo to k o p ira n je , s n im a n je ili b ilo koji d ru g i sistem
za beležen je, b ez p r e th o d n e p ism e n e d o z v o le izdavača.
T ra n sla tio n c o p v rig h t - 2007 by M ik ro k n jig a , T h in k in g in Java, F o u r th E d itio n b y B ru ce Eckel, C o p y rig h t <0 2006,
All R ights Reserved. P u b lish e d by a rra n g e m e n t vvith th e o rig in a l p u b lish e r, P earso n F d u c a tio n , Inc., p u b lish in g as P re n tic e
H a ll, In c, a C o m p a n y .
004.438JAVA
001.42:00-4.738.5
EK EJl. Bpvc
M isliti n a Javi / B ru ee Eckel ; p rev o d
4. izd. M iljenko Šueur. - B eograd : Mikro
knjiga, 2007 (B e o g ra d : P ub lik u m ) -
XIII, 1201 s tr . graf. prikazi : 24 cn i
ISBN 978-86-7555-308-3
Razvoj apstrakcije..........................15 P r a v l j e n j e p r o g r a m a n a J a v i . . . .5 2
Vidljivost i m e n a .....................................52
O bjekat im a in te rfe js................... 17
Koriščenje d rugih k o m p o n e n a ta . . . . 52
O bjekat pruža u slu g e................... 19 Rezervisana reć s t a t i c ............................53
Skrivena realizacija........................20 V a š p r v i p r o g r a m n a J a v i ................. 5 4
Ponovno korišćenje realizacije.. .21 Prevodenje i iz v ršav a n je ....................... 56
N asleđivanje...................................22 K o m e n ta ri i u g ra đ e n a
Relaeije !!• i IE-KAO.......................25 d o k u m e n t a c i j a ........................................ 5 7
Virtuelizacija objekata preko D okum entacioni k o m e n ta ri................ 58
p o lim o rfiz m a................................ 26 S in ta k s a .....................................................58
H ijerarhija s jedinstvenim U gradeni H T M L .....................................59
P rim eri o z n a k a ....................................... 60
k o r e n o m ......................................... 29
P rim er d o k u m e n ta c ije ......................... 62
K ontejneri.......................................29
S til p r o g r a m i r a n j a ............................... 6 3
Paramctri/uvani (generieki) tipuvi .. 30
S a ž e t a k ........................................................... 6 3
O brada izuzetaka: postupanje
V e ž b e .............................................................. 6 3
s greškam a.......................................33
vi Misliti na Javi
3: O p e r a t o r i 65 N a r e d b a s w i t c h ................................ 112
S a ž e ta k .................................................... 114
Jednostavnije naredbe
za ispisivanje.................................. 65 5: I n i c i j a l i z a c i j a i č iš ć e n je 115
Korišćenje operatora u Jav i.........66
P rio rite ti.........................................66 G a r a n t o v a n a in ic ija liz a c ija
Dodela v re d n o s ti..........................67 p o m o ć u k o n s t r u k t o r a .................. 115
Pojava pseudonima P r e k l a p a n je m e t o d a ........................ 117
pri pozivanju metode.....................68 Razlikovanje preklopljenih m etoda ..1 1 9
Matematički o p erato ri................. 69 Preklapanje i prosti t i p o v i ................ 120
Unarni operatori minus i plus.........71 Preklapanje p o v ra tn ih v re d n o s ti. . . 123
Osnovni p o jm o v i........................306 P r o n a l a ž e n j e s l i ć n i h i z u z e t a k a . .3 8 2
1 6 :N iz o v i 593 I z b o r r e a l i z a c i j e ..................................... 6 8 3
S tru k tu ra za ispitivanje
Šta to nizove čini p o se b n im .. . .593 p e rfo rm a n s i............................................684
Nizovi su prvorazredni objekti. .595 Perform anse razlićitih L is ta ..............688
Vraćanje niza v re đ n o sti............. 597 O p asn o sti od m ik roporedenja
p e rfo r m a n s i............................................694
Višedimenzionalni n iz o v i.........599
Iz b o rsk u p a
Nizovi i generički tipovi............. 603 (realizacije interfejsa S e t ) .................. 696
Pravljenje podataka Izb or m apa
za testiran je...................................605 (realizacija interfejsa M a p )................ 698
Arrays.fill()................................. 605 U s l u ž n e m e t o d e .................................. 7 01
Generatori podataka..................... 606 U redivanje i p retraživanje lista
Pravljenje nizova od Generatora... 612 (realizacija interfejsa L i s t ) ................ 705
M etode klase A rrays....................616 Kako kolekciju ili m a p u ućiniti
Kopiranje niza..............................617 n e p ro m e n ljiv o m .................................. 706
Poređenje nizova.......................... 618 Sinhronizovanje kolekcije ili m a p e . .708
Poređenje elemenata niza..............619 Čuvanje referen ci ............................... 7 0 9
Uređivanje niza............................ 623 K ontejner W e a k H a s h M a p ................ 712
Pretraživanje uređenog niza.......... 624 Kontejneri Jave 1 . 0 / 1 . 1 ....................7 1 3
Sažetak........................................... 626 V ector i E n u m e r a t i o n .......................713
Klasa H a s h t a b l e .................................. 714
17: D e t a l j n o r a z m a t r a n j e Klasa S ta c k ..............................................714
Klasa B itS e t........................................... 716
k o n te jn e ra 628
Sažetak .........................................................7 1 8
Potpuna taksonomija
kontejnera.....................................628 18: J a v in u la z n o - iz la z n i
2 1 : P a ra le ln o iz v rš a v a n je 885
Uzajam na b lo k a d a ......................978
Nove kom ponente biblioteke . .983
Svi aspekti paralelnog C o u n tD ovvnL atch - brava
izvršavanja.....................................886 sa o d b ro ja v a n je m ................................ 983
Brže iz v ršav a n je .....................................886 C y c lic B a rrie r..........................................986
Poboljšan d izajn k o d a ......................... 889 D e Ia y Q u e u e ............................................ 988
O s n o v e v iš e n itn o g P rio rity B lo c k in g Q u e u e ..................... 991
p r o g r a m i r a n j a ........................................ 8 9 0 K ontroler staklenika napravljen
Definisanje z a d a ta k a ............................890 po m o ču S c h e d u le d E x e c u to ra ......... 994
Klasa T h r e a d ......................................... 891 S e m a f o r ................................................... 997
U p o treb a izvršilaca E x c h a n g e r............................................ 1001
(interfejsa E x e c u to r)............................893 S i m u l a c i j a ..........................................1003
D obijanje p o v ra tn ih v redn osti Sim ulacija šalterskog službenika . . 1003
o d z a d a ta k a ............................................896 Sim ulacija r e s to r a n a ......................... 1008
S p a v a n je ...................................................897 R aspodela p o s la .................................. 1013
P r i o r i t e t ...................................................899 O p t i m iz a c ij a p e r f o r m a n s i . . . .1 0 1 8
P rep u šta n je .............................................. 901 Poređenje tehnologija u zajam no
Servisne n i t i ............................................901 isključivih brava ( m u t e x a ) .............. 1018
V arijante p ro g ra m ira n ja .....................906 K ontejneri bez z a k lju č a v a n ja ......... 1027
T erm inologija..........................................911 O p tim ističk o z a k lju č a v a n je ............1034
Kako se p rid ru žiti postojećoj n i t i . . . 912 R ea d W rite L o c k .................................. 1036
Korisničko o k ruženje koje A k tiv n i o b j e k t i ............................... 1039
brzo re a g u je ............................................913
S a ž e t a k ................................................. 1042
G ru p e n iti................................................ 914
L iteratura za dalje usavršavanje . . . 1044
H vatanje iz u z e ta k a .............................. 915
Deljenje r e s u r s a .......................... 918
N epravilno p ristu p a n je resursim a . . 918
22: Grafička korisnička
R azrešavanje takm ičenja okruženja 1045
za deljene re s u rse ...................................921
A tom ske operacije i tre n u tn a A pleti........................................... 1047
v id ljiv o st...................................................926 Osnove Svvinga..........................1047
A tom ske k l a s e ....................................... 932 Alatka za p rik aziv an je....................... 1050
K ritični o d e ljc i....................................... 933 Pravljenje d ugm eta................... 1051
S inhronizacija s d ru g im
I Ivatanje d o g a d a ja ................... 1052
o b je k tim a ................................................ 939
Lokalno skladište n i t i ......................... 940
Višeredna polja za te k s t...........1054
G a š e n j e z a d a t a k a ............................... 9 4 2
Raspoređivanje elem enata. . . .1056
U krasna b a šta ......................................... 942
R asporedivać 1056
B o r d e r L a y o u t.........
R asporedivać 1057
F lo w L a y o u t..............
G ašenje blo kiran ih z ad a ta k a ..............945
R asporedivać 1058
G r id L a y o u t..............
Prekidanje izv rsav anja......................... 946
R asporedivać G ridB agL avout . . . . 1058
Provera postoji li p r e k id .....................954
A p solutno p o z ic io n ira n je ................ 1059
M e đ u s o b n a s a ra d n ja
R asporedivać B o x I,a y o u t................ 1059
z a d a t a k a ...................................................... 9 5 6 Koji je p ristu p najbolji?..................... 1059
w ait() i n o tity A lI() ..............................957
Model događaja
n o tify () u o d n o su na n o tify A ll(). . . 962
Proizvođači i p o tr o š a č i....................... 965
grafičke biblioteke Svving.........1059
T ipovi dogadaja i p rije m n ik a ......... 1060
Proizvodači, p otrošači i redovi
Praćenje vi.še d o g a d a ja ..................... 1066
za č e k a n je ................................................ 971
Cevi za ulazno/izlazne operacije Primeri Svving kom ponenata. . 1068
izm edu z a d a ta k a .................................. 976 D u g m a d ................................................ IO(->9
Ik o n ic e ................................................... 1071
Sadržaj x iii
A li, k a k o je v r e m e o d m i c a l o I St o s a m je v i Se p r o u č a v a o ,p o C e o s a m d a u v i đ a m d a
je osnovna nam ena Jave drugačija od nam ene svih drugih jezika koje sam sretao.
Program iranje je snalaženje u složenosti: složenost problem a koji želite da rešite dodaje
se na složenost računara na kome se problem rešava. Zbog ove složenosti većina naših pro-
gramskih projekata propadne.- Pa ipak, za gotovo nijedan program ski jezik za koji znam
nije odlučeno da bi njegov glavni cilj trebalo da bude savladavanje složenosti razvoja i ođr-
žavanja program a.1Naravno, mnoge odluke pri stvaranju program skih jezika donete su sa
složenošću na umu, ali u nekom trenutku uvek bi se našle neke druge stvari za koje se sma-
tralo da su neophodne u mešavini. Te druge stvari neizbežno prouzrokuju da program er
koji koristi dati jezik na kraju udari glavom o zid. Na primer, C ++ je m orao da bude kom -
patibilan sa starijim jezikom C (da bi se omogućio lak prelazak C program erim a), a pri
tom i efikasan. I jedno i drugo su veoma korisni ciljevi i um nogom e zaslužni za uspeh je-
zika C++, ali takođe unose dodatnu složenost zbog koje neki projekti ne bivaju završeni
(naravno, možete kriviti program ere i upravu, ali, ako jezik može da pom ogne tako što će
otkrivati vaše greške, zašto to ne bi i radio). Evo još jednog prim era. Visual Basic (VB) je
bio vezan za BASIC, koji baš nije bio proširiv jezik, tako da su sva proširenja nagom ilana u
VB-u proizvela zaista zastrašujuću sintaksu koja se veoma teško održava. Perl je vertikalno
kom patibilan sa Avvkom, Sedom, Grepom i drugim Unixovim alatima koje je trebalo da
zameni, a posledica je bila da je često optuživan kako proizvodi ,,kod sam o za pisanje" (tj.
posle nekoliko meseci ne možete ga više čitati). S druge strane, pri stvaranju jezika kao što
su C++, VB, Perl i Smalltalk, deo napora je usmeren na rešavanje problem a složenosti, pa
oni veoma uspešno izlaze na kraj sa određenom vrstom problema.
Dok sam ućio Javu, čini mi se da je na m ene najviše uticalo to što je Sun m eđu svojim
ciljevima pri projektovanju imao i princip smanjenja složenosti zaprograrnera. Kao da su
rekli: „Važno nam je da ubrzam o i pojednostavim o pravljenje robusnog koda“. U ranim
danim a, ovaj princip je rezultovao kodom koji se nije brzo izvršavao (iako se to s vreme-
nom popravilo), ali je zaista zadivljujuće skratio vreme potrebno za pisanje program a
- za izradu program a na lavi potrebno je upola m anje vrem ena nego za pravijenje odgo-
varajućeg C ++ programa. Ovaj rezultat sam po sebi može da uštedi m nogo vrem ena i
novca, ali lava se tu ne zaustavlja. O na nastavlja da pakuje sve važne i složene zadatke, kao
što su višenitni rad i mrežno program iranje, u svojstva jezika ili bibliotcke koji pojedno-
stavljuju te zadatke. Najzad, latila se nekih zaista veoma složenih problem a kakvi su me-
duplattonnsko programiranje, dinamička prom ena koda, čak i pitanja zaštite —od kojih
se svaki na vašoj lestviei složenosti može svrstati od „prepreka'1do „potpuno nemoguće".
Stoga, uprkos problem ima s perform ansam a koje smo susreli, Java silno obećava: može
od nas da napravi znatno produktivnije programere.
Java na sve načine povećava opseg komunikacije tneđu Ijudima: stvaranjem program a,
tim skim radom na stvaranju program a, izgradnjom korisničkog okruženja kao veze iz-
m eđu program a i l<orisnika, izvršavanjem program a na različitim tipovim a računara i
lakšim pisanjem program a kojima se pristupa preko Interneta.
Mislim da ogrom na količina inform acija koje putuju Internetom nije najvažniji po-
kazatelj značaja kom unikacione revolucije; prava revolucija je u tom e što ćemo moći
m nogo lakše da razgovaramo - pojedinačno, u grupam a, i sa celom planetom . Priča se da
bi naredna revolucija iznedrila svojevrstan globalni um proizašao iz iskustva više ljudi
i njihove m edusobne povezanosti. Java može ali i ne m ora, biti alat koji će podstaći tu
revoluciju - i zbog same takve m ogućnosti, osećam kako je značajno da i drugi nauče ovaj
jezik.
Java SE6
Ova knjiga je bila ogrom an projekat koji je zahtevao mnogo vremena, i pre nego što je
izašla, pojavila se beta verzija Jave SE6 (pod radnim im enom mustartg). lako je lava SF.6
donela nekoiiko manjih izmena koje su poboljšale neke prim ere u knjizi, uglavnom nije
uticala na sadržaj ove kniige; glavna obeležja Jave SE6 su povećanje brzine izvršavanja i
novi elementi biblioteka, koja nisu tem a ove knjige.
Program i navedeni u knjiz.i uspešno su testirani na kandiđatskoj verziji Jave SF.6, pa ne
oćekujem da će ona doneti izmene koje bi uticale na sadržaj ove knjige. Ukoliko u vreme
objavljivanja zvanične verzije Jave SE6 bude važnih iz.mena, njih ćemo uvažiti u izvornom
kodu knjige koji se može preuzeti na adresi www.MiiidVicw.nct.
Na koricama knjige piše da je ona ,,za Javu SE5/6", što znači „napisana za |avu SF.5 i
veoma važne iz.mene koje je ta verzija unela u jezik, ali jednako primenljiva i na Javu SE6“.
Predgovor 3
Četvrto izdanje
Zadovoljstvo pisanja novog izdanja knjige donosi činjenica se stvari m ogu „uraditi kako
treba“, u skladu sa onim što sam saznao od izlaska poslednjeg izdanja. Ćesto su ti uvidi
tipa „Kada ne dobiješ ono što si hteo, dobiješ pouku" a za m ene je to prilika da popravim
ono što je pogrešno ili prosto zamorno. fednako tako, pisanje novog izdanja dovodi do fa-
scinantnih novih zamisli, a stid zbog pogrešaka m nogo je manji od uživanja u otkriču i
m ogućnosti da se zamisli izraze bolje nego pre.
Tu je i izazov da knjigu napišem tako da i vlasnici prethodnih izdanja požele da je ku-
pe. To me tera da poboijšavam ,prerađujem i premeštam sve što mogu da bi knjiga postala
novo i vredno iskustvo za posvećene čitaoce.
Izmene
U ovom izdanju nem a CD-a. Osnovni deo tog CD-a, m ultim edijski sem inar Thinking in
C (koji je za MindVievv napravio Chuck Allison), sada se može preuzeti u obliku Flash
prezentacije. Polaznici tog seminara koji nedovoljno poznaju sintaksu C-a obučavaju se
da ovladaju m aterijalom iz ove knjige. Iako dva poglavlja ove knjige daju pristojan uvod
u sintaksu, ona m ožda neće biti dovoljna osobam a bez adekvatnog predznanja, a prezen-
tacija Thinking in C upravo njima pomaže da dostignu potreban nivo.
Poglavlje Paralelno izvršavanje(ranije„Višenitno izvršavanje11) potpuno je prerađeno da
bi odraziio velike izmene u odgovarajućim bibliotekama Jave SE5, ali još uvek pruža temelj
za osnovne koncepte paralelnog izvršavanja. Bez tog jezgra, teško ćete razumeti sioženije
oblasti paralelnog izvršavanja. Mnogo sam meseci radio na tome, uronjen u taj drugi „para-
lelan“ svet, i na kraju je ispalo da to poglavlje daje ne samo osnovu, nego i naprednije uvide.
Znaćajnim novim m ogućnostim a Jave SE5 posvećena su nova poglavlja, a ostale su
utkane u izmene postojećeg materijala. Pošto ja neprestano proučavam projektne obra-
sce, povećan je i njihov broj u knjizi.
U knjizi je biio m nogo premeštanja, u dobroj meri nadahnutih predavačkom praksom
i sa/.nanjem da bi se moje poimanje onoga što treba da stane u je d n o poglavlje moglo raz-
borito kritikovati. I5io sam sklon nepromišljenom verovanju da tema m ora biti „dovoljno
velika" kako bi zaslužila sopstveno poglavlje. Ali, naročito tokom predavanja projektnih
obrazaca, uvideo sam da polaznici seminara najbolje shvataju kada uvodim jedan po je-
dan obrazac i odm ah uradim odgovarajuću vežbu, čak i ako zbog toga tek nakratko go-
vorim . (Otkrio sam da je taj tempo prijatniji i za m ene kao predavača.) Zato sam u ovoj
verziji knjige pokušao da pođelim poglavlja na teme i da ne brinem koliko su duga. Čini
m i se da je tako bolje.
Uvideo sani i kolika je važnost testiranja programa. Ukoliko nem ate ugrađen sistem za
testiranje, u kojem se testiranjeobavlja svaki put kada generišete program, ne možete znati
koliko je vaš program pouzdan. Da bih izbegao tu nesigurnost, napravio sam sistem za te-
stiranje koji prikazuje i vrednuje rezultate svakog programa. (Sistem je napisan na Pvtho-
nu; naći ćete ga u kodu ove knjige koji se može preuzeti na lokaciji www.MindView.net.)
Testiranje uopšte razm otreno je u dodatku objavljenom na adresi http://M indView.net/
Books/Bctterjavn; tu su objašnjene osnovne veštine kojima bi, po m om mišljenju, svi pro-
gram eri trebalo da vladaju.
4 Misliti na Javi
Pored toga, pročešljao sam sve prim ere u knjizi i zapitao se: „Zašto sam to uradio baš
ovako?" U većini slučajeva nešto sam izm enio i poboljšao, da bi prim eri bili dosledniji i da
bih pokazao ono što sm atram najboljom praksom u pisanju Java koda (koliko se to može
u tekstu uvodnog nivoa). Dizajn i realizacija m nogih postojećih prim era, značajno su iz-
menjeni. Uklonjeni su prim eri koji su izgubili smisao i dodati novi.’
Čitaoci su imali zaista divne kom entare na prva tri izdanja ove knjige što mi je bilo
veoma drago. Tu i tamo, poneko bi im ao i prim edbu i iz nekog razloga, periodično se ja-
vljala ista prim edba „knjiga je preobimna". Po m eni, ako čitaocu smeta samo obim dela,
to i nije tako strašno (prisetim o se prim edbe austrijskog cara o M ocartovom delu: „Pre-
više nota!“, iako se ja ni na koji način ne poredim s M ocartom ). M ogu sam o da pretpo-
stavim kako takvu prim edbu upućuje onaj ko će se tek upoznati sa širinom Jave i ko nije
video ostale knjige o ovom jeziku. Ipak, pokušao sam da u ovom izdanju skratim delove
koji su postali nepotrebni ili barem ne previše značajni. Po praviiu, pokušao sam da uklo-
nim sve što više nije potrebno, ubacim izmene i poboljšam sve što sam mogao. Mislim da
sam to mogao slobodno da uradim , jer je originalni m aterijal i dalje na Internetu (na
adresi www.MindView.net), u obliku prva tri izdanja koje se m ogu besplatno preuzeti, i
dodataka za ovu knjigu.
Iskreno se izvinjavam svima koji još uvek ne mogu da podnesu veličinu ove knjige. Ve-
rovali ili ne, naporno sam radio da bih je smanjio.
ovde uhvaćene i, po svoj prilici, ubijene u tegli za uzorke i smeštene u male izložbene ku-
tije: sve ovo nagoveštava Javinu sposobnost da pronalazi, prikazuje i obuzdava bubice
(što je zbilja jedan od njenih najjačih aduta).
Za ovo izdanje naslikao sam akvarel prikazan na koricama, ispod stilizovane kutije.
Zahvalnice
Prvo, hvala saradnicim a koji su radili sa m nom na sem inaru, u konsultacijam a i pri ost-
varivanju nastavnih projekata. To su: Dave Bartlett, Bill Venners, Chuck Allison, Jeremy
Meyer i Jamie King. Cenim vaše strpljenje, dok i dalje pokušavam da napravim najbolji
model za m eđusobnu saradnju nezavisnih ljudi kao što smo mi.
O dnedavno, bez sum nje zaslugom interneta, povezao sam se sa iznenadujuće velikim
brojem ljudi koji m i pom ažu u poslu, uglavnom radeći u svojim kućnim kancelarijama.
U prošlo vreme, m orao bih da iznajmim prilično veliki kancelarijski prostor gde bih sme-
stio sve te ljude, ali zahvaljujući Mreži, ekspres pošti i telefonu, koristim njihovu pomoć
bez tih dodatnih troškova. Tokom m ojih pokušaja da naučim da se „lepo igram sa ostali-
ma“ svi vi ste bili veom a susretljivi, i nadam se da ću i dalje bolje raditi svoj posao uz tuđu
pomoć. Paula Steuer je neprocenjiva zato što je preuzela m oju ofrlje poslovnu praksu i
postavila je na zdrave temelje (hvala Paula, što mi ne daš mira kada me mrzi da nešto ura-
dim ). Jonathan Wilcox, Esq. pročešljao je struk turu mog preduzeća, prevrnuo svaki ka-
men ispod kojeg se m ožda kriju škorpioni i naterao nas da prođem o kroz postupak
pravno valjanogorganizovanja. Hvala ti na staranju i upornosti. Sharlynn Cobaugh je na-
pravila od sebe stručnjaka za obradu zvuka i postala je bitan deo tim a za pravljenje mul-
timedijskih kurseva, kao i za rešavanje drugih problema. Hvala na upornosti koju
pokazuješ suočena s nepredvidivim računarskim problem im a. Ljudi iz praške firme
Amaio izvukli su m e iz neprilika u nekoliko projekata. Daniel W ill-Harris me je prvi
upoznao s radom preko Interneta, a i glavni je dizajner svih grafićkih rešenja.
Tokom mnogih godina, Gerald VVeinberg je putem svojih konferencija i radioriica
postao moj nezvanični trener i m entor, na čemu mu zahvaljujem.
Ervin Varga mi je izuzetno m nogo pom ogao svojim tehničkim ispravkama 4. izdanja
- iako su drugi pomagali u pojedinim poglavljima i prim erim a, Ervin je bio osnovni
tehnički recenzent cele knjige, a preradio je i vodič s rešenjima za 4. izdanje. Ervin je
pronašao greške i uneo poboljšanja u knjigu koja su nemerljiv doprinos ovom tekstu.
Njegova temeljnost i pažnja koju obraća na pojedinosti začuđujuće su, i on je najbolji re-
cenzent kog sam imao. Hvala ti, Ervine.
Moj blog na lokaciji www.Artiiiia.coin Bila Vennersa bio je izvor pom oći kada sam
tražio tuđa mišljenja. Hvala ćitaocima koji su mi slali kom entare na osnovu kojili sam
razjasnio pojmove - to su James VVatson, Hovvarđ Lovatt, Michael Barker i drugi, poseb-
no oni koji su pomogli s generićkim tipovima.
Hvala Marku VVelshu na njegovoj stalnoj pomoći.
Evan Cofskv mi stalno pruža veliku podršku svojim poznavanjem (napamet!) svih
zam ršenih pojedinosti o podešavanju i održavanju Web servera na Linuxu. On se stara da
MindVievvov server bude podešen i bezbedan.
6 Misliti na Javi
Posebno hvala m om novom prijatelju, kafi, koja mi je ulila gotovo neogranićen entu-
zijazam za ovaj projekat. Kafić Camp4 u gradu Crested Butte u Koloradu bio je stalno
sastajalište ljudi koji su dolazili na MindVievvove seminare, a tokom sem inarskih odm ora
pružao nam je najbolju opskrbu hranom i pićem koju sam ikada dobio. Hvala mom
drugaru Alu Smithu što ga je stvorio i od njega napravio tako zanimljivo i zabavno mesto.
I svim barm enim a kafića koji tako veselo toče piće.
Hvala ljudima u Prentice Hallu na tolerantnosti i ogrom nom strpljenju kojim mi
obezbeđuju sve što tražim.
Određene alatke su se pokazale nezamenljivim tokom procesa razvoja program a i ja
osetim veliku zahvalnost prema njihovim stvaraocima svaki put kada ih upotrebim . Cyg-
win (www.cygwin.com) rešio mi je bezbroj problem a koje W indows neće/ne može, svakog
dana sam m u sve više privržen (da sam ga sam o imao pre 15 godina kada je moj mozak još
bio ,,zalemljen“ za GNU Emacse). IBM-ov Eclipse (www.eclipse.org) zaista je divan dopri-
nos zajedniđ projektanata softvera, pa očekujem da će i ubuduće davati velike rezultate,
pošto se i dalje razvija. (Od kad je to IBM postao kul? M ora da mi je nešto promaklo.) Jet-
Brains IntelliJ Idea nastavlja da krči nove kreativne puteve m eđu razvojnim alatkama.
Na ovoj knjizi počeo sam da koristim Sparxsystemsov Enterprise Architect i on je brzo
postao m oja omiljena UML alatka. U raznim prilikam a dobro je poslužio form ater koda
Jalopy Marca Hunsickera (www.triemax.com), a Marco je bio veoma susretljiv i konfigu-
risao ga prem a mojim specifičnim potrebam a. Katkada je bio koristan i JEdit Slava Pesto-
va ( www.jedit.org) sa svojim softverskim dodacim a, i to je sasvim dobar editor za
početnike na seminarima.
1 naravno, ako to nisam već dovoljno puta rekao na svim mogućim mestim a, za reša-
vanje problem astalnokoristim Pvthon ( www.Pytlion.org). On je delo m o g drugara Guida
Van Rossuma i grupe uvrnutih genija s kojima sam trćao nekoliko divnih dana (Tim e Pe-
tersu, uram io sam miša kojeg si pozajmio i zvanićno ga nazvao „TimBotM iš"). Vi, m om -
ci, treba da nadete neko zdravije mesto za ručak. (Takođe, hvala celoj Pvthon zajednici,
oni su fenom enalna gomila ljudi.)
Mnogi su mi slali ispravke i svima sam dužnik, ali posebnu zahvalnost zaslužuju (za prvo
izdanje): Kevin Raulerson (koji je pronašao obilje sjajnih bubica), Bob Resendes (zaista ne-
verovatan), (ohn Pinto, Joe Dante, Joe Sharp (sva trojica su bili ćudesni), David Combs
(tnnoge gramatičke ispravke i razjašnjenja), dr Robert Stephenson, John Cook, Franklin
Chen, Zev Griner, David Karr, Leander A. Stroschein, Steve Clark, Charles A. Lee, Austin
Maher, Dennis P. Roth, Roque Oliveira, Douglas Dunn, Dejan Ristić, Neil Galarneau, Da-
vid B. Malkovsky, Steve VVilkinson i mnogi drugi. Prot. dr Marc Meurrens uložio je veliki
n ap o rd a u Evropi objavi i učitii dostupnom elektronsku verziju prvog izdanja knjige.
Hvala svitna koji su mi pomogli da ponovo napišem prim ere uz korišćenje biblioteke
Swing (za 2. izđanje) i za svu drugu pomoć. To su Jon Shvarts, Thom as Kirsch, Rahim
Adatia, Rajesh Jain, Ravi M anthena, Banu Rajamani, Jens Brandt, Nitin Shivaram, Mal-
colm Davis i svi koji su tni daii podršku.
U 4. izdanju, Chris G rinđstaff je m nogo pom ogao tokom razvoja odeljka o SVVT-u, a
Sean Neville je za mene napisao prvu verziju odeljka o Flexu.
Predgovor 7
Kad god pomislim da sam najzad sve naučio o program iranju za paraleleno izvrša-
vanje, otvore se neka nova vrata i pokaže se da im a još planina koje m oram preći. Hvala
Brianu Goetzu što mi je pom ogao da savladam sve prepreke u novoj verziji poglavlja Pa-
ralelno izvršavanje i što je otkrio (nadam se!) sve greške.
Nije iznenađenje što mi je poznavanje Delphija pom oglo da razum em Javu, budući da
ta dva jezika imaju puno zajedničkih koncepata i dizajnerskih rešenja. Moji prijatelji koji
se bave Delphijem pom ogli su mi da proniknem u unutrašnjost tog sjajnog programskog
okruženja. To su Marco Cantu (još jedan Italijan: da li dobro poznavanje latinskog dopri-
nosi sklonosti ka program skim jezicima?), Neil Rubenking (koji se bavio jogom, vegeta-
rijanstvom i zenom dok nije-otkrio računare) i, naravno, Zack Urlocker (prvi direktor
projekta Delphi), stari prijatelj s kojim sam proputovao svet. Svi mi sm o dužnici briljant-
nom A ndersu Hejlsbergu, koji se i dalje muči sa C#-om (a taj jezik je bio glavni uzor za
Javu SE5, u šta ćete se uveriti u knjizi).
Moj pronicljivi prijateij Richard Hale Shaw pružio mi je vrlo korisnu podršku (i Kim
takođe). Richard i ja smo proveli više meseci zajedno, držeći seminare, pokušavajući da
osm islim o savršen m etod učenja za polaznike.
Dizajn knjige, dizajn korica i sliku na koricam a uradio je moj priiatelj Daniel Will-
Harris, priznati autor i dizajner ( www.Will-Harris.com), koji se igrao sa samolepljivim
slovima u osnovnoj školi dok je čekao da računari i stono izđavaštvo budu otkriveni, i koji
se žalio na moje gunđanje zbog algebarskih problem a. Ipak, knjigu sam složio sam tako
da sve greške u slaganju idu na moj račun. Pisao sam u M icrosoftovom W ordu XP, a fo-
toslog je priprem ljen u Adobeovom Acrobatu. Desilo se da sam bio u inostranstvu oba
puta kada sam pravio finalnu verziju knjige - prvo izdanje sam slao iz Kejptauna u Južnoj
Africi, a drugo iz Praga (što je moj doprinos eri elektronike). Treće i četvrto sam poslao iz
Cresteđ Butta u Koloradu. Font korišćen na koricam a je ITC Rennie Mackintosh.
Posebno zahvaljujem svojim učiteljima i svim svojim studentim a (koji su istovremeno
i moji učitelji).
Dok sam radio na ovom izdanju, u krilu mi je često ležala mačka Mollv i tako davala
svoju toplu, krznenu podršku.
Listu prijatelja koji su me podržavali ćine izmedu ostalih i: Patty Gast (izvanredna ma-
serka), Andrew Birnstock, Steve Sinofskv, (D Flildebrandt, Tom Keffer, Brian McElhin-
ney, Brinklev Barr, Liill Gates iz časopisa M idnight Engineering, Larry C onstantine i Lucv
Lockvvood, Gene Wang, Dave Mayer, David Intersim one, Chris i Laura Strand, porodica
AImquist, Brad Jerbic, Marilvn Cvitanic, M ark Mabry, porodice Robbins, porodice Mo-
elter (i McMillan), Michael Wilk, Dave Stoner, porodica Cranston, Larry Fogg, Mike Se-
queira, Cary Entsminger, Kevin i Sonda D onovan, Joe Lordi, Dave i Brenda Bartlett, Patti
Gast, Blake, Annette i Jade, porodica Rentschler, porodica Sudek, Dick, Patty i Lee Eckel,
Lvnn i Todd i njihove porodice. I, naravno, moji m ania i tata.
Uvod
„Čoveku je dao jezik, a jezik je stvorio misao, koja jc mera Univerzuma“ —Oslobođeni Pro-
metej, Šeli
Ljudska bića... sasvim su pod vlašću određenog jezika koji je postao sredstvo izražavanja u nji-
hovotn društvu. U zabludi je ko misli da u prilagodavanju pojedinca stvarnosti jezik ne igra
znaćajnu ulogu i da je jezik samo slućajno sredstvo rešavanja specifićnih problema opštenja i
mišljenja. Činjenica je da je „stvarni svet“ u velikoj meri nesvesno izgraden na osnovu jezičkih
navika grupe.
Status lingvistike kao nauke, Edvard Sapir 1929.
Preduslovi
U ovoj knjizi podrazumeva se da koliko-toliko poznajete programiranje: shvatate da je pro-
gram skup naredaba, poznajete princip potprogram a/tunkcije/m akroa, kontrolnih struk-
tura kao što je ,,if“ konstrukcija za petlje kao što je „vvhile" itd. Sve ovo ste mogli naućiti na
raznim mestima, recimo pri program iranju na nekom makro-jeziku ili pri radu sa alatom
kao što je Perl. Bez obzira na to koliko ste u dosadašnjem programiranju ovladali osnovnim
idejama, moći ćete da radite po ovoj knjizi. Naravno, kniiga će biti lakša C programerima,
i još više C ++ program erim a, ali ne otpisujte sebe ako nemate iskustva u ovim jezicima -
samo budite sprem ni da naporno radite. Takode, uz pomoć multimedijalnog seminara
Thinking in Ckoji možete preuzeti s Iokacije mvw.MiudView.net, ovladaćete u potpunosti
osnovama potrebnim za ućenje Jave. Kroz knjigu ću postepeno uvoditi koncepte objektno
orijentisanog program iranja (OOP) i osnovnih Javinih kontrolnih ntehanizama.
Cesta pozivanja na osobine jezika C i C ++ ni.su kom entari poznavaoca, već su pomoć
program erim a da uporede Javu s tim jezicima iz kojih je potekla. Trudiću se da ta po-
ređenja budu jednostavna i objasniću svešto mislim da ne zna neko ko ne koristi C/C++.
Uvod 9
Učenje Jave
O tprilike u isto vreme kada se pojavila moja prva knjiga, Using C++ (Osborne/McGravv-
Hill, 1989), počeo sam da predajem taj jezik. Predavanje program skih jezika postala je
m oja profesija: gledao sam glave koje klimaju, bele pogleđe i zbunjene izraze u publici ši-
rom sveta još od 1987. Kada sam počeo privatno da obučavam m anje grupe Ijudi, tokom
vežbi sam otkrio da mnoga pitanja zbunjuju čak i one koji su se smešili i klimali glavom.
Više godina predsedavao sam na odseku za C ++ na Konferenciji za razvoj softvera (ka-
snije i na odseku za Javu), i prim etio sam kako sm o i ja i drugi govornici sldoni da pro-
sečnoj publici serviramo p reV iše informacija za kratko vreme. Zbog različitih nivoa
publike i načina na koji sam predstavljao materijal, na kraju bih izgubio deo slušalaca.
M ožda previše tražim , ali pošto osećam otpor prem a tradicionalnom načinu predavanja
(a verujem da većina slušalaca takav otpor oseća zbog dosade), pokušao sam da učinim da
m oja predavanja nikom e ne budu dosadna.
Jedno vreme sam pravio dosta raznih prezentacija u relativno kratkom periodu. Tako
sam došao u situaciju da učim „m etodom pokušaja i pogrešaka" (što je dobar način i za
projektovanje program a). Na kraju sam iskoristio sve što sam naučio kao predavač- i na-
pravio kurs koji bih rado držao duže vreme. Sada ovaj kurs držim na javnim i privatnim
sem inarim a o Javi; to je moj glavni uvodni seminar, koji pruža osnovu za naprednije se-
m inare. O tim sem inarim a možete saznati više na adresi www.MindView.net. (Uvodni se-
m inar je dostupan i u obliku CD-a Hands on Java. Podaci o njem u su na istoj Web
lokaciji.)
Povratne informacije koje dobijam na svakom sem inaru pom ažu mi da m enjam i pre-
rađujem materijal sve dok nc zakliučim da je podesan kao sredstvo poučavanja. Ovu knji-
gu ne čine samo beleške sa seminara: u nju sam pokušao da spakujem što više inform acija
i spojio sam ih tako da vas vode od jednog do drugog predmeta. Zamislio sam da ova knji-
ga služi usam ljenom ćitaocu koji se bori s novim program skim jezikom.
Ciljevi
Kao i moja prethodna knjiga, Misliti na jczikit C++, i ova knjiga je pisana u skladu s na-
ćinom na koji Ijudi uče jezik Java. Poglavlje u knjizi zamišljao sam kao jednu dobru lek-
ciju na sem inaru. Povratne intormacije od slušalaca na sem inarim a pom ogle su mi da
otkrijem koji su delovi teži i zahtevaju dodatna objašnjenja. U oblastim a gde sam bio pre-
am biciozan i uvrstio previše novih pojmova odjednom , spoznao sam - kroz proces pri-
kaza materijala - da više novih pojmova zahteva više objašnjavanja, a to lako zbunjuje
slušaoca.
Cilj svakog poglavlja je da savladate jedan pojam , ili m anje grupe povezanih pojmova,
bez obzira na sve ostalo što se tu pominje. Na taj način možete da prihvatite svaki đelić
teorije, u zavisnosti od vašeg dotadašnjeg znanja, pre nego što produžite dalje.
10 Misliti na Javi
Dokumentacija na Webu
Jezik Java i njegove biblioteke iz Sun Microsystemsa (besplatno se preuzim aju s Weba)
im aju đokum entaciju u elektronskom obliku, kojoj možete pristupati koristeći čitač We-
ba; gotovo svaka druga nezavisna distribucija Jave ima takav ili sličan sistem dokum enta-
cije. U većini knjiga o Javi kopira se taj sistem. Stoga takvu dokum entaciju ili već imate,
ili je m ožete preuzeti s Weba, i ukoliko to zaista nije neophodno, u ovoj knjizi je nećemo
ponavljati. O bično m nogo brže možete da nađete opis neke klase pom oću čitača Weba,
nego da ga tražite po knjizi (a dokum entacija na Webu je verovatno ažurnija). Jednostav-
no ću vas uputiti na„dokum entaciju JDK-a“. O vaknjiga će obezbediti dodatne opise klasa
samo kada je neophodno dopuniti dokum entaciju da biste shvatili odredeni prim er.
Vežbe
Prim etio sam da jednostavne vežbe pom ažu studentim a da shvate seminarsko gradivo, pa
dajem nekoliko vežbi na kraju svakog poglavlja.
Veći broj vežbi može relativno lako i u razum nom roku da se uradi u učionici, pod
nadzorom instruktora koji proverava da li su svi studenti usvojili izloženu m ateriju. Neke
vežbe su malo izazovnije, a li nijedna nije nerešiva.
Rešenja izabranih vežbi nalaze se u elektronskom dokum entu pod im enom Thc Thin-
king iu java Annotatcd Solution Guide, koji se može kupiti na lokaciji www.Mind-
Vicw.com.
Javini temelji
Sa ovom knjigom dobijate i besplatan m ultim edijalni sem inar koji možete preuzeti sa
w w w .M in dV iew .L O in . To je sem inar T h i n k in g in C, koji vas uvodi u sintaksu, operatore i
funkcije jezika C na osnovu kojih je Java napravljena. U prethodnim izdanjima ove knjige
na engleskom, to se nalazilo na CD-u Foundations for Java, ali sada se može besplatno
preuzeti.
Prvobitno sam od Chucka Allisona naručio da napravi Thinking in C k ao nezavisan
proi/.vod, aii sam odlućio da ga priložim uz drugo izdanje knjige Thinkingin C++ i drugo
i treće izdanje knjige Thinking in Java, pošto su mi na sem inar stalno dolazili Ijudi bez do-
voljnog poznavanja osnovne sintakse C-a. Izgleda da takva osoba misli: ,,Ja sam pam etan
program er i neću da ućim C, nego C ++ ili Javu, pa ću C preskočiti i odm ah preći na C + + /
Javu“. Po dolasku na seminar, ljuđi polako shvataju da sam imao veoma dobar razlog što
sam poznavanje C-a proglasio za preduslov za pohađanje.
Tehnologije se menjaju, i bilo je bolje preraditi Thinking in C u Flash prezentaciju koja
se može preuzeti sa Interneta, nego prilagati ga na CD-u. Pošto je taj seminar na Inter-
netu, svi mogu dobro da se priprem e prc nego što dođu na moj seminar.
Sem inar Thinking in C i ovoj knjizi pribavlja veću publiku. Iako njena poglavlja Ope-
ralori i Kontrolisanjc izvršavanja obuhvataju osnove Jave koje potiču iz C-a, mrežni semi-
nar postepenije uvodi ćitaoca, a za njega je potrebno još manje program erskog
predznanja nego za knjigu.
12 Misliti na Javi
Izvorni kod
Kompletan izvorni kod iz ove knjige dostupan je kao slobodan softver zaštićen autorskim
pravom, u jednom paketu, na adresi www.MindView.net. Ovo je zvanična Web lokacija za
distribuciju koda i elektronskog izdanja knjige, pa m ožete biti sigurni da ćete tu uvek naći
najnoviju verziju. Kod m ožete deliti i koristiti u učionicam a i u druge obrazovne nam ene.
Osnovni cilj zadržavanja autorskog prava jeste da se poreklo koda ispravno navede i da
se spreči objavljivanje koda u štam panim medijim a bez dozvole. (Ukoliko je poreklo koda
ispravno navedeno, prim eri iz knjige m ogu se navoditi u većini medija.)
U svakoj datoteci sa izvornim kodom naći ćete sledeću napom enu o autorskim pravi-
ma (obično na engleskom):
//:! A u t o r s k a P r a v a . t x t
Ovaj raču n a r s k i iz vorni kod z a š t i ć e n j e a u t o r s k i m p r a v i m a (c)2006 MindView,
Inc.
Sva pra v a zadržana.
3. D o z v o l u za k o r i š ć e n j e I z v o r n o g ko da u š t a m p a n i m m e d i j i m a m o ž e t e pr i b a v i t i
ako se o b r a t i t e na adresu:
Ako m i s l i t e da s te u I z v o r n o m kodu p r o n a š l i g r e š k u , m o l i m v as da i s p r a v k u
pošaljete preko sistema povratnih informacija koji m o ž e t e pr onaći na lo ka ci ji
www.MindView.com.
///:-
Kod možete koristiti u svojim nastavnim projektim a (uključujući i vaše materijale za
prezentaciju), dokle god zadržavate poruku o autorskim pravim a koja se pojavljuje u sva-
koj izvornoj datoteci.
Ova knjiga se zasniva na Javi SE5/6, i na njoj je i testirana. Ako hoćete da naučite nešto
o ranijim varijantama jezika, a to nije obuhvaćeno ovim izdanjem, prvo, drugo i treće iz-
danje knjige (na engleskom) možete besplatno preuzeti s lokacije www.MindView.net.
Greške
Bez obzira na to koliko trikova pisac koristi da otkrije greške, neke se uvek provuku, a nov
čitalac ih često odm ah primeti. Ako otkrijete bilo šta što sm atrate greškom, m olim vas da
iskoristite hipervezu za ovu knjigu na lokaciji www.MindView.net, prijavite grešku i
pošaljete predlog za ispravku. Ceniću vašu pomoć.
Upoznavanje sa objektima
„Prirodu parcelišemo i razvrstavamo u koncepte kojima pripisujemo značenja, uglavnom
zato što se držimo dogovora koji važi u našoj jezičkoj zajednici i kodifikovan je u obrascima
našeg jczika ... uopštc nc možetnogovoriti ukoliko se nepridržavamo organizacije i klasifika-
cije podataka koje taj dogovorpropisuje.“ Benjamin Lee W h o rf ( 1897-1941)
R a č u n a r s k a REVOLUCIj'A je n a s t a l a u m a S in i. N a S i p r o g r a m s k i je z ic i s t o g a t e ž e d a
izgledaju kao ta mašina.
Ali raćunari nisu toliko mašine koliko pojačala um a („točkovi za um “ kako Steve Jobs
voli da kaže) i drugi način izražavanja. Zbog toga ti alati sve m anje liče na mašine, a sve
više na delove našeg uma, i na druge oblike izražavanja, kakvi su pisanje, slikanje, vajanje,
anim iranje i snim anje filmova. O bjektno orijentisano program iranje (OOP) deo je ovog
pom eranja ka korišćenju računara kao sredstva izražavanja.
Ovo poglavlje vas uvodi u osnovne koncepte OOP-a, uključivši i pregled m etoda raz-
voja, uz pretpostavku da imate iskustva s program iranjem , iako to ne m ora biti na jeziku
C. Ako mislite da treba da se još priprem ate za program iranje pre nego što se uhvatite u
koštac sa ovom knjigom, proučite multimedijski sem inar Thinking in C, koji možete
preuzeti na adresi mvw.MitidView.net.
Ovo pogtavlje je i podloga i dodatni materijal. M nogi se ne osećaju prijatno u svetu
ol>iektno orijentisanog program iranja ako pre toga ne razum eju celinu. Stoga je ovde dat
bogat pregled koncepata OOP-a. Drugi ne mogu da shvate glavne principe dok prvo ne
upoznaju barem neke mehanizme. Ako pripadate toj grupi i žetjni ste da otkrijete speci-
fićnosti jezika, slobodno preskoćite ovo poglavlje - u ovom trenutku to vas neće sprečiti
da pišete program e ili naučite jezik. M eđutim, poželećete da se vratite na ovaj deo knjige
da biste dopunili svoje znanje i shvatili zašto su objekti važni i kako da ih koristite pri
pisanju programa.
Razvoj apstrakcije
Svi programski jezici obezbeđuju apstrakoiju. Moglo bi se raspravljati o tom e da li je slo-
ženost problema koje ste u stanju da rešite direktno povezana s vrstom i kvalitetom ap-
strakcije. Pod „vrstom" mislim na to „šta apstrahujete“. Mašinski jezik je mala apstrakcija
mašine na kojoj se programi izvršavaju. Mnogi takozvani ,,proceduralni“ jezici koji su sle-
dili mašinski (kao FORTRAN, RASIC i C) bili su apstrakcija mašinskog jezika. Ovi jezici
su veliki napredak u odnosu na mašinski jezik, ali njihova prim arna apstrakcija ipak za-
hteva od vas da razmišljate iz ugla strukture računara um esto iz ugla probiem a koji reša-
vate. Program er mora da uspostavi vezu izmedu modela tnašine (u „prostoru rešenja“
koji predstavlja mesto gde realizujete rešenje problem a, na prim er, u računaru) i modela
problem a koji se rešava (u „prostoru problema" koji predstavlja mesto gde problem po-
stoji, recimo poslovanje). Napor koji iziskuje ovo preslikavanje i činjenica da je ono ne-
bitno za programski jezik proizvode program e koji se teško pišu i čije je održavanje
skupo, a kao sporedni elekat nastaje celokupna industrija „program skih metoda".
16 Misliti na Javi
Pošto klasa opisuje skup objekata koji imaju identićne karakteristike (elementi s po-
dacima) i jednako se ponašaju (funkcionalnost), klasa ie zaista tip podatka, jer i broj u
form atu pokretnog zareza, na prim er, takođe im a skup karakteristika i ponašanja. Razlika
je u tom e što program er definiše klasu koja odgovara problem u, um esto da bude prinu-
đen da koristi postojeći tip koji predstavlja m em orijsku jedinicu unutar računara. Pro-
gramski jezik proširujete dodavanjem novih tipova podatka, specifičnih za vaše potrebe.
Programski sistem prihvata nove klase, o njim a vodi računa i proverava tipove, kao što
radi i sa ugrađenim tipovima.
O bjektno orijentisan pristup ne ograničava se sam o na pravljenje simulacija. Bez ob-
zira na to da li se slažete sa stavom da je svaki program simulacija sistema koji projektu-
jete, upotrebom tehnika, O O P-a veliki skup problem a lako se može da svesti na
jednostavna rešenja.
Kada se klasa ustanovi, možete da napravite koliko god želite objekata te klase, a zatim
da radite s tim objektim a kao da su elementi u problem a koji pokušavate da rešite. Zaista,
jedan od izazova objektno orijentisanog program iranja jeste ostvarivanje jednoznačnog
preslikavanja izm eđu elemenata u prostoru problem a i objekata u prostoru rešenja.
Kako da naterate objekat da uradi koristan posao? M ora postojati način da postavite
zahtev objektu da nešto uradi, na prim er, da završi transakciju, iscrta nešto na ekranu ili
uključi prekidač. A svaki objekat može da zadovolji samo izvesne zahteve. Zahtevi koje
možete da postavite objektu đefinisani su preko njegovog interfcjsa, koji je određen
tipom . Prost prim er bi mogla biti sijalica:
Ime tipa
Interfejs
S i j a l i c a sj = n ew S i j a l i c a ( ) ;
sj .ukl juci ();
pozovete nevv, čime zahtevate novi objekat tog tipa. Da biste poslali poruku objektu, na-
vedite ime objekta i povežite ga s porukom - zahtevom, razdvajajući ih tačkom. S tačke
gledišta korisnika unapred definisane klase, to je skoro sve što vam treba da biste progra-
m irali sa objektima.
Gornja slika je napravljena u skladu s form atom koji koristi unifikovani jezik za mode-
lovanje (engl. Unified Modeling Languagc, UML). Svaka klasa je predstavljena pravouga-
onikom , ime tipa je u gornjem delu, podaci članovi koje treba opisati nalaze se u srednjem
delu, a u donjem delu pravougaonika su metode (funkcije koje pripadaju tom objektu,
koje prim aju poruke koje šaljete tom objektu). Ćesto se u UML dijagramima prikazuju
sam o im e klase i javne m etode, a srednji deo ne —tako je i na prethodnoj slici. Ako vas za-
nim a sam o ime klase, nem a potrebe da prikazujete ni donji deo.
Jako ćete pojednostaviti sebi život ako objekte budete sm atrali davaocima usluga. To će
koristiti ne sam o vama tokom procesa projektovanja, nego i drugim a koji b u d u pokuša-
vali da razum eju vaš kod ili upotrebe neki od vaših objekata. Ako b u d u mogli da utvrde
vrednost objekta na osnovu usluge koje on pruža, biće im m nogo lakše da ga uklope u
svoje projekte.
Skrivena realizacija
Vrlo je korisno podeliti polje delatnosti na autore klasa (one koji prave nove tipove poda-
taka) i programere klijente4 (korisnike klasa koji te tipove upotrebljavaju u svojim aplika-
cijama). Cilj program era kJijenta je da sakupi klase u „kutiju sa alatom", koju će koristiti
za brzi razvoj aplikacija. Cilj autora klasa je da napravi klasu koja otkriva sam o ono što je
neophodno program eru klijentu, a sve ostalo drži sakriveno. Zašto? P rogram er Idijent ne
može da koristi sakrivene delove, što znači da autor klase m ože da prom eni skriveni deo
kad god hoće, ne razmišljajući da li će se to odraziti na ostale. Skriveni deo obično pred-
stavlja osetljivu unutrašnjost objekta koju lako može da ošteti neobazriv ili neobavešten
program er ldijent, pa sakrivanje realizacije sm anjuje m ogućnost da se pojave greške u
program im a.
U svakom odnosu je važno da sve uključene strane poštuju granice. Kada pravite bi-
blioteku, vi stvarate odnos sa kJijentom koji je takođe program er, ali koji sastavlja aplika-
ciju koristeći vašu biblioteku, da bi, možda, napravio veću biblioteku.
Ako bi svi članovi klase bili dostupni svakome, onda bi program er klijent mogao da
uradi bilo šta s klasom i ne bi postojao način da se nam etnu pravila. Jako biste vi voleli da
program er Jdijent ne radi direktno s nelcim članicama vaše klase, bez kontrole pristupa ne
bi postojao način da to sprečite. Sve bi bilo izloženo javnosti.
Stoga je prvi razlog za uvođenje kontrole pristupa onem ogućavanje program era kli-
jenta da pristupa delovima koje ne sme da dira, a koji su neophodni za interni rad s tipom
podataka. Ti delovi nisu deo interfejsa koji je potreban korisnicim a za rešavanje njihovih
problem a. Ovo je u isto vreme usluga program erim a klijentima, jer lako rnogu da razluče
šta je za njih važno, a o čemu ne treba da misle.
Kontrolu pristupa treba uvesti i da bi se dozvolilo projektantu biblioteke da prom eni
način na koji klasa interno radi, a da ne m ora da brine kako će se to odraziti na progra-
mere klijente. Na primer, možete realizovati neku klasu na jednostavan način, a kasnije
otkriti da je treba ponovo napisati kako bi radila brže. Ako su interfejs i realizacija jasno
razdvojeni i zaštićeni, ovo možete lako da izvedete.
Java koristi tri rezervisane reči da postavi granice un u tar klase: public, private i pro-
tected. Ovi specifikatoripristupaodređuju ko može da koristi definicije koje slede iza njih.
public (javni) znači da je naredni elem ent dostupan svakome. Rezervisana reč private
(privatni) znači da tom elementu ne može da pristupi niko sem autora klase, unutar me
toda tog tipa. private predstavlja zid između autora i program era klijenta. Onaj ko poku
ša da pristupi članici označenoj kao private, izazvaće grešku prilikom prevodenja.
Rezervisana reč protected (zaštićeni) deluje kao private, s tom razlikom što klase nasled-
nice im aju pristup zaštićenim članicama, ali ne i privatnim članicama. O nasleđivanju
ćemo govoriti uskoro.
Java ima i ,,podrazum evani“ pristup koji se koristi ako ne zadate nijedan od navedenih
specifikatora. Ovo se obično naziva paketskipristup, jer klase m ogu da pristupe članicama
drugih klasa u istom paketu, ali izvan tog paketa te iste članice vide se kao privatne.
(Na ovom UML dijagram u kompoziciju označava popunjen romb, koji kazuje da po-
stoji jedan auto. Kad označavam spajanje, obično ću koristiti jednostavniji oblik: sam o li-
niju, bez rom ba.)1
Kompozicija je vrlo fleksibilna. Objekti članovi vaše nove klase obično su privatni, što
ih čini nedostupnim program erim a Idijentima koji koriste klasu. Ovo omogućava da iz-
menite te članove ne remeteći postojeći klijentski kod. Možete m enjati objekte članove i
u vreme izvršavanja da biste dinamički menjali ponašanje svog program a. Nasleđivanje
koje malo dalje opisujemo, nem a ovu fleksibilnost pošto prevodilac m ora da ugradi ogra-
ničenja za prevođenje u klase stvorene nasleđivanjem.
Pošto je nasledivanje veoma bitno, u objektno orijentisanom program iranju često se
veoma naglašava, i nov program er može pom isliti da nasleđivanje treba koristiti svuda.
Kaii rezultat može se dobiti veoma nezgrapan i veoma složen program . Umesto toga, prvo
treba videti da li pri pravljenju novih klasa može da se iskoristi kompozicija, pošto je ona
jednostavnija i fleksibilnija. Ako prim enite ovaj pristup, program će biti čistiji. Kada stek-
nete nešto iskustva, biće vam očigledno kada treba da koristite nasleđivanje.
Nasleđivanje
Sam po sebi, koncept objekta je veoma koristan. On omogućava da podatke i funkcional-
nost grupišete po konceptu, tako da možete pređstaviti odgovarajuću ideju u prostoru
problem a, um esto da budete prinuđeni da koristite izraze računara na kome radite. Kada
se prim eni rezervisana reč class, te ideje su izražene kao osnovne jedinice u ovom pro-
gram skom jeziku.
Bila bi šteta da se nam učite i napravite neku klasu a da onda budete prim orani da pra-
vite p otpuno novu klasu koja im a sličnu funkcionalnost. Bilo bi jednostavnije da uzme-
m o postojeću klasu, da je kloniram o, a zatim dopunjujem o i m enjam o kloniranu klasu.
Ovo se zapravo postiže nasleđivanjem (engl. inheritance), osim kada se originalna klasa
(koja se naziva osnovna klasa ili natklasa ili klasa roditelj) izmeni, a te izmene se odraze i
na ,,klonu“ (koji se naziva izvedena ili nasleđena klasa ili potklasa ili klasa naslednik; engl.
derived class).
(Strelica u ovom UML dijagram u polazi od izvedene klase ka osnovnoj klasi. Kao što
ćete videti, može da postoji više izvedenih klasa.)
Tip ne označava samo ograničenja skupa objekata; on ima odnose i s drugim tipovi-
ma. Dva tipa m ogu imati neke zajedničke karakteristike i jednako se ponašati, ali pri tom
jedan tip može imati još neke dodatne karakteristike i može obrađivati više poruka (ili ih
drugačije obradivati). Kod nasleđivanja, ova sličnost između tipova se izražava preko
koncepta osnovnih i izvedenih tipova. Osnovni tip sadrži sve karakteristike i ponašanja
koja su zajednička za tipove izvedene iz njega. Praveći osnovni tip, izražavate suštinu svo-
jih ideja o nekim objektim a u sistemu. Iz osnovnog tipa izvodite ostale tipove i pokazujete
različite načine realizacije ove suštine.
Na prim er, mašina za recikliranje raspoređuje komadiće otpada. Osnovni tip je ,,ot-
pad “ a svaki pojedini otpadak ima težinu, vrednost itd. i može biti isečen, istopljen ili ra-
stavljen. Iz osnovnog tipa izvodimo posebne vrste otpada s dodatnim karakteristikama
(boca im a boju), ili ponašanjim a (alum inijum ska konzerva može da se zdrobi, čelične
konzerve privlači magnet). Ponašanja mogu biti različita (vrednost papira zavisi od nje-
gove vrste i stanja). Koristeći nasledivanje možete izgracliti hijerarhiju tipova. Ta hijerar-
hija opisuje problem koji pokušavate da rešile preko tipova koji se u problem u pojavljuju.
Drugi prim er je klasičan ,,oblik“ koji može da se koristi u CAD sistemu (engi. Compu-
ter-Aided Design) ili u nekoj kom pjuterskoj igri. Osnovni tip je ,,oblik“ a svaki oblik ima
veličinu, boju, poziciju itd. Svaki oblik može da se iscrta, obriše, pom era, oboji itd. Odav-
de izvodimo (nasledujem o) pojedine vrste oblika - krug, kvadrat, trougao i druge - od
Poglavjje 1: Upoznavanje sa objektima 23
kojih svaki može imati dodatne karakteristike i ponašanje. Neki oblici, na prim er, m ogu
da se okreću. Neka ponašanja m ogu biti drugačija, recimo kada želite da izračunate po-
vršinu oblika. H ijerarhija tipova objedinjuje sličnosti i razlike između oblika.
Predstavljanje rešenja i problem a u istom obliku veoma je korisno jer vam ne treba ve-
liki broj m eđum odela da biste od opisa problem a došli do opisa rešenja. Kod objekata, hi-
jerarhija tipova je osnovni model tako da sa opisa sistema u realnom svetu direktno
prelazite na opis sistema kodom. Jedna od teškoča sa objektno orijentisanim program i-
ranjem jeste ta da je previše jednostavno stići od početka od kraja. Um treniran da traži
složena rešenja često spočetka zbunjuje ova jednostavnost.
Kada iskoristite nasleđivanje iz postojećeg tipa, stvarate novi tip. Taj novi tip ne samo
da sađrži sve članice postojećeg tipa (iako su privatne članice sakrivene i neđostupne),
već, što je mnogo važnije, kopira interfejs osnovne klase. Znači, sve poruke koje možete
da pošaljete objektima osnovne klase, takođe možete poslati i objektim a izvedene klase.
Pošto se tip klase odreduje na osnovu poruka koje joj m ožem o poslati, to znači da je iz-
vedena klasa istog tipa kao i osnovna klasa. U prethodnom prim eru „krug je oblik“. Ekvi-
valencija tipova dobijena nasledivanjem je osnovni korak na p utu ka razum evanju smisla
objektno orijentisanog program iranja.
Pošto i osnovna klasa i izvedena klasa imaju isti osnovni interfejs, m ora da postoji rea-
lizacija koja ide uz taj interfejs. Znači, mora postojati kod koji se izvršava kada objekat
prim i određenu poruku. Ako nasledite klasu i ništa više ne uradite, m etode interfejsa
osnovne klase će preći i u izvedenu klasu. To znači da objekti izvedene klase imaju i isti tip
i isto ponašanje, što i nije previše korisno.
Postoje dva načina da napravite raziiku između svoje nove izvedene klase i osnovne
klase. Prvi je jasan: izvedenoj klasi dodate potpuno nove metode. Te nove m etode nisu
deo interfejsa osnovne klase, što znači da osnovna klasa nije radila sve ono što ste hteli, pa
ste joj zato dodali još m etoda. Ovaj jednostavan način korišćenja nasledivanja ponekad je
savršeno rešenje problem a. M edutim, pažljivo proučite da li su i vašoj osnovnoj klasi po-
trebne te dodatne metode. Ovaj proces otkrivanja i iteracije program a redovna je pojava
u objektno orijentisanom program iranju.
24 Misliti na Javi
Iako nasleđivanje ponekad može da nagovesti (posebno u Javi, gde rezervisana reč
koja označava nasledivanje glasi extends - proširuie) kako ćete dođavati nove m etode in-
terfejsu, to nije uvek istina. Drugi i važniji način da napravite razliku u novoj klasi jeste da
promcnite ponašanje postojeće m etode osnovne klase. To se naziva redefinisanje metode
(engl. overriding).
Relacije JE i JE-KAO
Može se postaviti jedno pitanje o nasledivanju: ,,Da li nasleđivanje treba da redefiniše
samo m etode osnovne klase (i da ne dodaje nove m etode koje ne postoje u osnovnoj kla-
si)?“ To bi značilo da je izvedena klasa potpimo istog tipa kao osnovna klasa, jer ima isto-
vetan interfejs. Kao rezultat, objekat izvedene klase može se svesti na tip objekta osnovne
klase. Ovo je tzv. čista supstitucija, ili princip supstitucije. Unekoliko, ovo je idealan način
prim ene nasleđivanja. U ovom slučaju, m i se često pozivamo na relaciju izm eđu osnovne
i izvedene klasekao na relaciju je, jer m ožem o reći„krug;eoblik“. Pokušaj da utvrdim o da
li m ožem o da uspostavim o relaciju je izm edu klasa, a da to ima smisla, predstavlja svoje-
vrstan test nasleđivanja.
Ponekad m orate da dodate nove elemente u interfejs izvedenog tipa i tim e proširite
postojeći interfejs. Novi tip i dalje m ože biti sveden na osnovni tip, ali supstituđja nije sa-
vršena, jer nove m etode nisu dostupne iz osnovnog tipa. Ovo se može opisati kao relacija
je-kao (moj term in). Novi tip ima interfejs starog tipa ali sadrži i druge m etode, tako da
se ne može reći kako je potpuno isti. Uzmimo za prim er uređaj za klimatizaciju. Pretpo-
stavim o da su po kući razvedene kontrole za hlađenje, odnosno, kuća im a interfejs koji
om ogućava da kontrolišete hlađenje. Zamislite da se uređaj za klimatizaciju pokvari i da
ga zam enite toplotnom pum pom koja može i da greje i da hladi. Toplotna pum pa je kao
uređaj za hlađenje, ali ona može i više. Pošto je kontrolni sistem vaše kuće projektovan
sam o da kontroliše hlađenje, on je ograničen na kom unikaciju s delom za hlađenje novog
objekta. Interfejs novog objekta je proširen, a postojeći sistem ne poznaje ništa drugo
osim originalnog interfejsa.
Cim proučite ovaj plan, postaće jasno da osnovna klasa „sistem za hlađenje" nije do-
voljno opšta i da treba da se preim enuje u „sistem za kontrolu temperature", kako bi m o-
gla da sadrži i grejanje - nakon čega bi princip supstitucije važio. Ovaj dijagram prikazuje
šta u stvarnom svetu može da se desi tokom projektovanja.
Kada upoznate princip supstitucije, lako možete pomisliti da je taj pristup (čista sup-
stitucija) jedini način da se nešto uradi, i zaista jeste dobro da program napravite na taj
način. Ali shvatićete da u interfejs izvedene klase ponekad m orate da dodate nove metode.
Nakon što ispitate problem , trebalo bi da bude prilično očigledno o kojem se od ta dva
slučaja radi.
26 Misliti na Javi
v oid r a d i N e s t o ( O b l i k oblik) {
o b 1 i k.obri si S e ( ) ;
II ...
obli k.i s c r t a j S e ( ) ;
}
ta m etoda se obraća bilo kom O bliku, pa ne zavisi od specifičnog tipa objekta koji se is-
crtava i briše. Ako u nekom drugom delu program a pozovemo funkciju radiN esto():
pozivi m etode radiN esto() autom atski rade ispravno, bez obzira na tačan tip objekta.
28 Misliti na Javi
radiNesto(krug);
Ovde je Krug prosleden metodi koja očekuje Oblik. Pošto Krug jeste Oblik, funkcija
radiNesto() može tako da ga tretira. O dnosno, bilo koju poruku koju m etoda radiNe-
sto() m ože da pošalje Obliku, Krug može da prihvati. Zato je gornji poziv p o tp u n o sigu-
ran i logičan.
Proces pri kome izvedeni tip tretiram o kao osnovni tip, nazivamo svođenje naviše
(engl. npcasting). Ime castje iskorišćeno zato što označava ubacivanje u kalup (engl. ca-
sting into a tnold-ubacivan je u kalup), a up potiče od načina na koji se obično organizuje
dijagram nasleđivanja, sa osnovnim tipom na vrhu i izvedenim klasama koje se lepezasto
šire naniže. Prema tome, konverzija u osnovni tip je penjanje uz dijagram nasleđivania:
svođenje naviše ili upcasting.
U objektno orijentisanom program u uvek postoji svođenje naviše, jer pri tom ne mo-
rate znati tačan tip s kojim radite. Pogledajte funkciju radiN esto():
o b l i k.obri s i S e ( ) ;
II ...
o b l ik . is c r t a j S e f ) ;
O bratite pažnju na to da se nigde ne govori:,, Ako si Krug, radi ovo, ako si Kvadrat radi
onoitd." Tako napisan kod koji proverava sve moguće tipove koje O blik može da pokrije
prljav je i m orate da ga m enjate svaki put kada dodate novu vrstu O blika. U našem slu-
čaju, kažemo: ,,Ti si oblik, ja znam da možeš da se iscrtaš i obrišeš, to i uradi, i sam vodi
računa o detaljima".
Važno je da se naredbe u funkciji rađiN esto() izvršavaju na pravi način. Sam poziv
m etode iscrtajSe() za K rug prouzrokuje izvršavanje drugačijeg koda nego kada poziva-
mo m etodu iscrtajSe() za Kvadrat ili Liniju; ali kada poruku iscrtajSe() pošaljemo ne-
poznatom O bliku, dobija se ispravno ponašanje, zasnovano na stvarnom tipu Oblika.
Ovo je dobra osobina, kao što je ranije pom enuto, jer kada prevodilac prevodi m etodu ra-
diN esto(), on ne zna tip s kojim radi. Stoga bismo obično očekivali da on pozove verziju
m etoda obrisiSe() i iscrtajSe() za osnovnu klasu O blik, a ne za određeni Krug, Kvadrat
Poglavjje 1: Upoznavanje sa objektima 29
ili Liniju. Zbog polim orfizm a se sve ipak ispravno odvija. Prevodilac i sistem za izvršava-
nje vode računa o svim detaljima. Zasađ je dovoljno da znate da polim orfizam funkcio-
niše i kako da pišete program e koristeći taj pristup. Kada pošaljete poru k u objektu,
objekat će uraditi šta treba, čak i kada treba svoditi naviše.
Kontejneri
Po pravilu, ne možete znati koliko objekata će vam biti potrebno za rešavanje nekog
zadatka, niti koliko dugo oni treba da postoje u m emoriji. Ne znate ni kako da uskladištite
te objekte. Ako pre izvršavanja program a ne znate ni broj objekata ni njihov vek trajanja,
kako da odredite količinu memorijskog prostora za njihovo skladištenje?
Rešenje većine problem a u objektno orijentisanom program iranju ponekad deluje
neozbiljno: stvoriti još jedan tip objekta. Novi tip objekta koji rešava pom enuti problem
čuva reference na druge objekte. Naravno, to isto možete da uradite i pom oću nizova, koji
postoje u većini jezika. Ali taj novi objekat, obično pod im enom kontejner (naziva se i fco-
lekcija, ali pošto Javine biblioteke koriste taj term in u drugom smislu, ova knjiga će se
30 Misliti na Javi
držati im ena ,,kontejner“), širiće se po potrebi da bi prim io sve što u njega stavite. Stoga
ne m orate da znate koliko ćete objekata čuvati u kontejneru. Samo napravite kontejnerski
objekat i prepustite njemu da se stara o detaljima.
Srećom, dobar OOP jezik sadrži skup kontejnera kao deo paketa. U jeziku C ++ on je
deo standardne C ++ biblioteke i često se naziva standardna biblioteka šablona (engl.
Standard Template Library, STL). Smalltalk ima prilično dobar skup kontejnera. I Java
ima m nogo kontejnera u svojoj standardnoj biblioteci. U nekim bibliotekama jedan ili
dva generička kontejnera dovoljni su za sve potrebe, dok u drugim bibliotekama (na pri-
m er u Javi) postoje različiti tipovi kontejnera za različite potrebe: više različitih klasa
spiskova (tipova Lista) za čuvanje sekvenci, tipova M apa (koje neki nazivaju asocijativni
tiizovi) zapridruživanje jednih objekata drugim a, tipova skupova (Set) za čuvanje po jed-
nog prim erka raznih tipova objekata i druge kom ponente m eđu kojima su redovi če-
kanja, stabla, stekovi itd.
Sa stanovišta program a, za vas je važan kontejner s kojim možete da radite i rešite pro-
blem. Ako kontejner jednog tipa zadovoljava vaše potrebe, nem a razloga da uvodite druge.
Izbor treba da vam bude ponuđen iz dva razloga. Prvo, kontejneri obezbeđuju različite ti-
pove interfejsa i načina rada. Stek ima interfejs i način rada različit od reda čekanja, koji se
razlikuje od skupa ili liste. Za rešenje vašeg problem a jedan od njih je obično bolji od osta-
lih. Drugo, različiti kontejneri obavljaju iste operacije s različitom efikasnošću. Na primer,
postoje dve osnovne vrste lista: ArrayList i LinkedList. I jedna i druga su jednostavne se-
kvence koje mogu imati identične interfejse i, spolja gledano, načine rada. No, postoji bit-
na razlika u trajanju i zahtevima odredenih operacija nad njima. Prim era radi, nasum ično
pristupanje elementima kontejnera ArrayList je operacija s konstantnim vremenom izvr-
šavanja; bez obzira na to koji element izaberemo, potrebno je isto vreme. Međutim, pom e-
ranje kroz listu LinkedList do nasum ično izabranog elementa je komplikovanije i treba
više vremena da se pristupi elementim a što su oni dalje od početka liste. S druge strane,
ako želite da ubacite element usred sekvence, to je mnogo jednostavnije i brže u iisti Lin-
kedList, nego u ArrayList. Ove i druge operacije nisu jednako efikasne, što zavisi od struk-
ture koja se nalazi u osnovi pojedine sekvence. U fazi pisanja program a, možete krenuti od
kontejnera LinkedList a kada budete popravljali perform anse, prebacite se na kontejner
ArrayList. Zahvaljujući apstrakciji preko interfejsa List, prebacivanje iz jedne strukture 11
drugu imaće minim alan uticaj na vaš kod.
objekat u kontejner, ona se svodi naviše na O bject i gubi identitet. Kada je izvadite iz kon-
tejnera, dobijate referencu na Object, a ne referencu na tip koji ste stavili unutra. Kako onda
da tu referencu vratite u nešto što ima konkretan tip objekta koji ste stavili u kontejner?
Ovde se ponovo koristi eksplicitna konverzija (pretvaranje) tipova, ali ovog p u ta se ne
penjete uz hijerarhiju nasleđivanja do opštijeg tipa, već se spuštate niz hijerarhiju do od-
ređenijeg tipa. Ovaj naćin eksplicitne konverzije naziva se svođenje naniže (engl. dovvnca-
sting). Pri svođenju naviše, na primer, znate da je K rug tipa O blik, pa možete slobodno
da izvršite svođenje naviše; m eđutim , neki O bject nije obavezno K rug ili O blik, pa svo-
đenje naniže nije baš sigurno, osim ako znam o s čim im am o posla.
Ovo i nije previše opasno: ako izvršite pogrešno svođenje naniže, javlja se greška pri iz-
vršavanju - izuzetak (engl. exception), o kome uskoro govorimo. Kada iz kontejnera uzi-
m ate reference, m orate obezbediti neki naćin da zapam tite na šta se te reference odnose
kako biste mogli da izvršite pravilno svođenje naniže.
Svođenje naniže i provere pri izvršavanju zahtevaju dodatno vreme za izvršavanje pro-
gram a i dodatni napor za program era. Zar ne bi bilo logično da nekako napravim o kon-
tejner tako da on zna koje tipove čuva, čime se isključuje potreba za svođenjem naniže
kao i m oguće greške. Ovo se rešava pom oću parametrizovanih tipova, a to su klase koje
prevodilac autom atski može da prilagodi tako da rade sa određenim tipom . Na prim er,
param etrizovani kontejner prevodilac bi mogao da prilagodi tako da prihvata i vraća
sam o klasu Oblik.
ledna od velikih prom ena koje je donela Java SE5 jesu param etrizovani tipovi, koje u
Javi nazivamo generički tipovi. Prepoznaćete ih po uglastim zagradam a u n u tar kojih se
navode; recimo, ovako se može napraviti ArrayList koji sadrži Oblik:
A r r a y L i s t < 0 b l i k > oblici = new A r r a y L i s t < 0 b i i k > ( );
Možda imate i neki drugi sistem za zapisivanje podataka o avionim a o kojima ne treba
neposredno voditi računa. M ožda je to evidencija o planovim a leta malih aviona koji na-
puštaju aerodrom . Znači, trebao bi vam drugi kontejner za male avione i kad god napra-
vite objekat aviona, ako je to mali avion, stavili biste ga takođe i u ovaj drugi kontejner.
Zatim bi neki pozadinski proces obavljao operacije nad objektim a iz tog kontejnera kada
računar ne radi ništa drugo.
Sada je problem nešto teži: kako uopšte možete znati kada da uništite objekte? Kada za-
vršite sa objektom, nekom drugom delu sistema m ožda je još uvek potreban. Isti problem
se može pojaviti i postati vrlo složen u m nogim drugim situacijam a i u program skim si-
stemim a (kao što je C++) u kojima m orate izričito da obrišete objekat kada s njim završite.
Gde se nalaze podaci iz objekta i kako se kontroliše trajanje objekta? C ++ zauzima stav
da je najvažnija efikasnost, pa program eru prepušta izbor. Da bi se postigla maksimalna
brzina izvršavanja, čuvanje i trajanje m ogu biti određeni prilikom pisanja program a, tako
što se objekti stavljaju na stek (oni se ponekad nazivaju i automatske ili vidljive- engl. sco-
ped -pro m enljive) ili u statičku oblast za čuvanje. Ta mesta im aju viso k p rio ritet i prostor
se u njim a brzo zauzima i oslobađa, pa je kontrola nad njim a veoma značajna u nekim si-
tuacijama. M eđutim , tim e žrtvujete fleksibilnost jer m orate da znate tačan broj, trajanje
i tip objekata prilikom pisanja program a. Ako pokušavate da rešavate opštiji problem , na
prim er projektovanje pom oću računara (CAD), upravljanje skladištem ili kontrolu va-
zdušnog saobraćaja, ova m etoda je previše restriktivna.
Drugi pristup je dinamičko stvaranje objekata u dinamičkoj oblasti memorije (engl.
heap). Pri ovom pristupu, sve dok ne počne izvršavanje, ne znate koliko vam objekata tre-
ba, koliko će trajati, niti kog su tipa. Sve se to određuie kada program već radi (ovakav na-
čin pravljenja objekata zove se dinam ički). Ako vam zatreba novi objekat, napravite ga u
dinamičkoj memoriji, u trenutku kada vam je zatrebao. Pošto se oblašću za čuvanje upra-
vlja dinamički, prilikom izvršavanja, zauzimanje memorijskog prostora traje znatno duže
od odvajanja prostora na steku. Odvajanje prostora na steku često se postiže samo asem
blerskom naredbom da se pokazivač steka pom eri naniže i da se vrati nazad. Vreme za koje
se odvoji prostor u dinamičkoj m em oriji, zavisi od dizajna m ehanizm a za skladištenje.
Dinamički pristup se zasniva na najčešće opravdanoj pretpostavci da su objekti slože-
ni, tako da dodatni režijski troškovi za nalaženje prostora i njegovo oslobađanje neće bit-
no uticati na stvaranje objekata. Veća fleksibilnost dinam ičkog pristupa je neophodna za
rešavanje opštijih program skih problem a.
Java isključivo koristi drugi pristup.' Svaki p u t kada želite da stvorite objekat, koristite
rezervisanu reč new da biste napravili dinam ički prim erak tog objekta.
Nameće se još i pitanje trajanja objekata. U jezicima koji dozvoljavaju da se objekti stva-
raju na steku, prevodilac određuje koliko dugo objekti traju i može automatski da ih uništi.
Međutim, kada objekat stvorite dinamički, prevodilac nema informacije o njegovom ži-
votnom veku. U jeziku kao što je C ++ m orate programski da odredite kada ćeteda uništite
objekat, što može dovesti do„curenja" m em orije ako se to ne uradi ispravno (to je čest pro-
blem u program im a pisanim na C + + -u). Java obezbeđuje tzv. sakupljač smeća (engl. gar-
bage collector) koji automatski otkriva koji se objekat više ne upotrebljava i uništava ga.
Sakupljanje smeća je veoma korisno, jer sm anjuje broj stavki na koje m orate da mislite i
količinu koda koji m orate da napišete. Još je bitnije što sakupljanje smeća obezbeđuje
m nogo viši nivo zaštite od podm uklih problem a sa „curenjem" m em orije (koje je mnoge
projekte pisane na jeziku C++ oborOo na kolena).
U Javi, sakupljač smeća vodi računa o problem u oslobađanja m em orije'(iako u to ne
spadaju drugi aspekti čišćenja objekta). Sakupljač smeća ,,zna“ kada se objekat više ne ko-
risti i autom atski oslobađa m em oriju koju je zauzim ao taj objekat. To, uz činjenicu da su
svi objekti izvedeni iz jedne osnovne klase O bject, kao i da postoji samo jedan način za
pravljenje objekata - dinam ički - čini proces program iranja u Javi m nogo jednostavnijim
od program iranja na jeziku C ++. M nogo je m anje odluka koje treba da donesete i pre-
preka koje m orate da prevaziđete.
Paralelni rad
Osnovni koncept u računarskom program iranju jeste obrada više od jednog zadatka isto-
vremeno. Mnogi programski poslovi zahtevaju da program može da zaustavi rad, poza-
bavi se nekim drugim problem om a zatim se vrati glavnom procesu. Problem u se
pristupalo na više načina. Na početku, program eri koji su poznavali računar do najsit-
nijih pojedinosti, pisali su prekidne servisne rutine, a suspenzija glavnog procesa je inici-
rana preko hardverskog prekida. Iako je sve to dobro radilo, bilo je teško i neprenosivo, jer
je prebacivanje program a na novi tip računara bilo sporo i skupo.
Ponekad su za obradu zadataka koji se m oraju odm ah izvršiti prekidi rada neophodni,
ali postoji velika klasa poslova u kojoj problem pokušavam o da izdelimo na više delova
koji se izvršavaju zasebno i paralelno, da bi ceo program brže reagovao. Delovi koji se za-
sebno izvršavaju unutar program a nazivaju se niti (engl. thread), a ceo koncept paralelni
rad (engl. concurrency). Tipičan p rim er paralelnog rada je korisničko okruženje. Zbog
podele program a na niti, korisnik m ože da pritisne dugm e i da dobije brz odziv, umesto
da bude prinuđen da čeka dok program ne završi tren u tn i zadatak.
N itim a se obično raspodeljuje vreme jednog (i jedinog) procesora. M eđutim , ako ope-
rativni sistem podržava višeprocesorski rad, svaka nit može biti dodeljena različitim pro-
cesorima, i tada one zaista m ogu da rade paralelno. Jedna od korisnih osobina paralelnog
rada na nivou jezika jeste sledeća: program er ne m ora da brine postoji li jedan ili više pro-
cesora. Program je logički podeljen na niti i ako računar ima više od jednog procesora,
program će se brže izvršavati, bez ikakvih posebnih prilagođavanja.
Nakon svega ovoga, paralelni rad zvuči prilično jednostavno. Postoji ipak jedna zač-
koljica: deljeni resursi. Ako se sim ultano izvršava više niti koje očekuju da pristupe istom
resursu, pojaviće se problem. Na prim er, dva procesa ne mogu istovrem eno da šalju po-
datke istom štampaču. Da bi se rešio problem , resursi koji mogu biti deljeni, kao što je
štam pač, m oraju biti zaključani dok se koriste. Znači, nit zaključa resurs, završi zadatak,
a zatim otključa resurs, posle čega neko drugi može da ga koristi.
Paralelni rad je ugrađen u Javu, a Java SE5 ga d odatno podržava svojom bililiotekom.
Java i Internet
Ako je Java zapravo samo još jedan računarski program ski jezik, m ožete se zapitati zašto
je toliko važna i zašto se predstavlja kao revolucionarni korak u računarskom programi
ranju. Odgovor nije odmah očigledan, ukoliko se posm atra iz tradicionalne program er-
ske perspektive. Iako je Java veoma korisna za rešavanje uobičajenih zasebnih
program skih problem a, još je važnije to što pom oću nje možete rešiti i program ske pro-
bleme koji se tiču Weba.
Šta je Web?
Isprva Web može da deluje pom alo tajanstveno, uz celu priču o „krstarenju“, ,,prisustvu“
i „Iičnim prezentacijama“. Korisno je malo se udaljiti i sagledati šta Web zaista jeste, ali za
to m orate da razumete sisteme klijent/server, još jedan aspekt računarske obrade koji je
pun zbunjujućih tema.
Poglavlje I : Upoznavanje sa objektima 35
Klijent/server obrada
O snovna ideja sistema klijent/server jeste postojanje centralnog skladišta inform acija -
neke vrste podataka, obično u bazi podataka - koje hoćete da šaljete, po zahtevu, grupi
ljudi ili računara. U konceptu klijent/server ključno je to što je skladište inform acija cen-
tralizovano tako da informacije m ogu da se m enjaju i da se te prom ene prenose svim ko-
risnicima informacija. Skladište inform acija, softver koji šalje informacije i računar
(računari) gde se softver i inform acije nalaze, nazivaju se server. Softver koji se nalazi na
korisničkom računaru, sarađuje sa serverom, preuzim a informacije, obrađuje ih i prika-
zuje na udaljenom računaru, naziva se klijent.
Osnovni koncept klijent/server obrade, znači, nije previše složen. Problemi se javljaju
jer im ate jedan server koji pokušava da opsluži više klijenata u isto vreme. Uglavnom se ko-
risti sistem za upravljanje bazam a podataka, tako da program er ,,uravnotežava“ raspored
podataka u tabelama da bi postigao optim alno korišćenje. Sistemi često dozvoljavaju kli-
jentim a i da dodaju informacije na server. To znači da m orate obezbediti da novi podaci
jednog klijenta ne „pregaze'" nove podatke drugog klijenta, ili da se ti podaci ne izgube u
procesu dodavanja u bazu. (Ovo se zove obrada transakcija - engl. transaction processitig).
Kada klijentski softver treba izmeniti, on m ora da se prevede, očisti od grešaka i instalira na
klijentskim računarim a, što je u stvarnosti znatno komplikovanije i skuplje nego što mi-
slite. Posebno je problem atično podržati više tipova računara i operativnih sistema. Ko-
načno, tu je i veoma bitno pitanje perform ansi: stotine klijenata mogu da postavljaju
zahteve vašem serveru u bilo kom trenutku, pa je i najmanje zakašnjenje problematično.
Da bi se kašnjenje svelo na najm anju m eru, program eri naporno rade da rasterete proce-
sne zadatke, prebacujući deo obrade na klijentski računar, a ponekad i na druge računare
na serverskoj strani, koristeći takozvane posrednike (engl. middleware). (Posrednici se ta-
kođe koriste da bi se olakšalo održavanje.)
Realizacija tako jednostavnog postupka - slanja informacija - toliko je slojevita i slo-
žena da ceo problem đeluje beznadežno zagonetno. I pored toga je veoma bitna: klijent/
server obrada nosi otprilike polovinu svih program skih aktivnosti. O na je zadužena za
sve, počev od uzimanja narudžbina, transakcija s kreditnim karticama, pa do slanja ra-
znih vrsta podataka - berzanskih, naučnih, državnih; šta god vam padne na pam et. U
prošlosti sm o se suočavali sa individualnim rešenjima individualnih problema; svaki put
sm o smišljali novo rešenje. Bilo je teško osmisliti ih, teško su se upotrebljavala i korisnik
ie m orao da uči nov interfejs za svako od tih rešenja. Ceo klijent/server problem trebalo
bi sveobuhvatno da se reši.
stranica sa servera. Želeii su punu klijent/server kompatibilnost kako bi klijent bio u mo-
gućnosti da vraća podatke mogućnost na server, na primer, da pretražuje baze na serveru,
dodaje nove informacije na server, ili da ostavi narudžbinu (što je sve zahtevalo posebne
bezbednosne mere). Bili smo svedoci tih prom ena u razvoju Weba.
Čitač Weba je označio veliki korak napred - uveo je m ogućnost da se delići inform acija
neizm enjeni prikazuju na bilo kom tipu računara. Ti čitači su ipak bili prilično prim itivni
i ubrzo su se zaglibili u zahtevima koji su im ispostavljani. Oni nisu bili previše interak-
tivni; zagušivali su server i sam Internet jer ste, svaki p u t kada je trebalo uraditi nešto što
zahteva program iranje, m orali da vraćate informacije serveru na obradu. Moglo je da
prođe više sekundi ili m inuta dok ne otkrijete da ste nešto pogrešno otkucali. Pošto je či-
tač bio predviđen samo za pregled, nije m ogao da izvrši ni najjednostavnije zadatke ob-
rade. (S druge strane, bio je po tp un o bezbedan, jer na vašem računaru nije mogao da
izvrši nijedan program , dakle ni da m u potencijalno donese grešku ili virus.)
Prim enjeno je više pristupa rešenju ovog problema. Za početak, poboljšani su grafički
standardi da bi omogućili bolju animaciju i video prikaz u čitačima. Drugi deo problema
mogao je biti rešen samo ugrađivanjem u čitač mogućnosti za pokretanje program a na kli-
jentskoj strani. To se naziva programiranje s klijentskestrane (engl. client-sideprogramming).
grafika.) Pored toga, bez sum nje ste stekli neposredno iskustvo s proverom ispravnosti
podataka na ulaznom obrascu. Pritisnete dugm e za slanje na stranici, server pokrene CGI
program koji otkrije grešku, form atira HTML stranicu obaveštavajući vas o grešci, a za-
tim vam vrati tu stranicu. O nda vi m orate da se vratite na p reth o d n u stranicu i pokušate
ponovo. Ovo ne samo da je sporo već je i zam orno.
Rešenje je program iranje s klijentske strane. Većina stonih računara na kojima rade či-
tači Weba sposobni su da urade ogrom an posao, a s prvobitnim statičkim HTM L pristu-
p om sam o su stajali i besposleno čekali da server isporuči sledeću stranicu. Program iranje
s klijentske strane znači da čitač Weba radi sav posao koji može da obavi, korisnik m nogo
brže dobija rezultat a osećaj zajedništva p ri radu s Web stranicom je potpuniji.
Rasprave o program iranju s klijentske strane malo se razlikuju od rasprava o progra-
m iranju uopšte. Param etri su gotovo isti, ali je platform a drugačija: čitač Weba je sličan
ograničenom operativnom sistemu. Na kraju, opet m orate da program irate, i zato pro-
gram iranje s klijentske strane povlači vrtoglav niz problem a i rešenja. U daljem tekstu da-
jem o pregled pitanja i pristupa pri program iranju klijentske strane.
Dodaci
Razvoj dodataka (engl. plug-ins) predstavlja jedan od najdužih koraka napred u progra-
m iranju s klijentske strane. Program er dodaje novu funkcionalnost čitaču tako što kori-
snik u potrebnom trenutku preuzme deo koda koji se ugradi na odgovarajuće m esto u
čitaču. Taj kod govori čitaču: ,,Od sada možeš da obavljaš i ovu novu aktivnost“. (D odatak
treba da preuzm ete sam o jednom .) Preko dodataka su čitačima pridodata brza i m oćna
proširenja, ali pisanje dodataka nije jednostavan zadatak i nije nešto što biste želeli da ra-
dite u okviru izgradnje odredene stranice. Vrednost đodataka za program iranje s klijent-
ske strane jeste to što oni omogućavaju stručnjaku da pravi nova proširenja i da ih dodaje
čitaču bez odobrenja proizvođača čitača. Stoga, dodaci predstavljaju „zadnja vrata“ koja
om ogućavaju stvaranje novih jezika za program iranje s klijentske strane (iako nisu svi je-
zici realizovani kao dodaoi).
Skript-jezici
Dodaci su prouzrokovali razvoj skript-jezika za čitače. Pomoću skript-jezika ugradujete
izvorni kod program a za klijentsku stranu direktno u HTML stranicu, a dodatak koji tu-
mači taj jezik autom atski se aktivira pri prikazivanju HTML stranice. Skript-jezici se
obično prilično lako shvataju i pošto se pišu u delu HTML stranice, učitavaju se veoma
brzo, jednim pristupom serveru na kome se nalazi ta stranica. Nedostatak je to što je vaš
kod izložen i svako može da ga vidi (i ukrade). U principu, ipak ne pravite neke sofistici-
rane stvari pom oću skript-jezika, pa ovaj nedostatak i nije neki problem .
Postoji jedan skript-jezik koji većina čitača Weba podržava i bez ikakvog dodatka - to
je JavaSoript (koji ima tek prolaznu sličnost s Javom, i m oraćete ga učiti zasebno). Tako je
nazvan sam o da bi prigrabio deo Javinog marketinškog uzleta. Nažalost, većina čitača
Weba je svoju podršku za JavaScript realizovala na svoj jedinstveni način, drugačije od
ostalih čitača, pa čak i od ostalih svojih verzija. Nešto je popravila standardizacija Java-
Scripta u obliku ECMAScipta, ali je raznim čitačima trebalo m nogo vrem ena da dođu do
38 Misliti na Javi
tog nivoa (tom e je doprineo i M icrosoft, gurajući svoj VBScript koji pomalo liči na Java-
Script). Da bi program m ogao da se izvršava na svim čitačim a,po pravilu ga m orate pisati
koristeći najm anji zajednički imenilac svih postojećih verzija JavaScripta. Programska
obrada grešaka, kao i otkrivanje i otldanjanje grešaka u toku pisanja program a, mogu se
opisati sam o kao mučenje. Dokaz tili poteškoća je činjenica da je na JavaScriptu tek ne-
davno napisan zaista složen program (to je Googleov GMail), što je zahtevalo veliki trud
i stručnost.
Ovim se naglašava da su skript-jezici koji se koriste u nutar čitača Weba predviđeni da
reše određene vrste problem a, prvenstveno da se naprave bogatija i interaktivnija grafička
korisnička okruženja. Skript-jezik m ože da reši i do 80 procenata problem a koje srećemo
pri program iranju s klijentske strane. Vaši problem i verovatno spadaju u tih 80 procena-
ta, a kako skript-jezici om ogućuju lakši i brži rad, trebalo bi da razmislite o njim a pre
nego što se upustite u m nogo zapetljanija rešenja, kao što je program iranje na Javi.
Java
Ako skript-jezici mogu da razreše 80 procenata problem a pri klijentskom program iranju,
šta je s preostalih 20 procenata „zaista teških stvari“? Java je popularno rešenje za tih 20%.
Pre svega, to je m oćan program ski jezik, siguran, m eđuplatform ski i internacionalan.
Java se neprekidno širi: dodaju se nove m ogućnosti jezika i biblioteke koje mogu elegant-
no da reše problem e teške i u tradicionalnim program skim jezicima, kao što su istovre-
meni rad, pristup bazama, m režno program iranje i distribuirana obrada. Java dopušta
program iranje s klijentske strane preko apleta i pom oću lava Web Starta.
Aplet je mali program koji može da se izvršava samo unutar ćitača VVeba. Aplet se
autom atski preuzim a kao deo Web stranice (kao što se, na prim er, automatski preuzima
grafika). Kada se aplet aktivira, on izvršava program . Deo njegove pogodnosti je to što
omogućava da autom atski distribuirate klijentski softver sa servera tek u trenutku kada je
korisniku klijentski softver potreban, a ne ranije. Korisnik bespogovorno dobija najnovi-
ju verziju klijentskog softvera i to bez složene ponovne instalacije. Zbog načina na koji je
Java projektovana, program er treba da napravi samo jedan jedini program, a taj program
autom atski radi na svim računarim a koji imaju čitače s podrškom za Javu. (Ovo, bez ika-
kve brige, obuhvata ogrom nu većinu računara.) Pošto je Java potpun programski jezik,
možete uraditi sav posao koji je m oguć na klijentskoj strani, pre i posle postavljanja za-
hteva serveru. Na prim er, nem a potrebe da šaljete obrazac sa zahtevom preko Interneta
kako biste otkrili da li ste pogrešili pri upisivanju datum a ili nekog drugog param etra; vaš
računar može brzo da iscrtava grafike na osnovu podataka, um esto da čeka da ih server is-
crta i vrati sliku. Pored trenutnog poboljšanja brzine i odziva, sm anjuju se ukupni mrežni
saobraćaj i opterećenje servera i sprečava usporavanje celog Interneta.
Druge mogućnosti
Pošteno rečeno, Java apleti nisu opravdali velika početna očekivanja. Kada se Java tek po-
javila, najviše se govorilo upravo o apletim a, jer je trebalo da oni najzad omoguće ozbiljno
program iranje na klijentskoj strani, povećaju brzinu odziva i smanje protok podataka
potreban Internet aplikacijama. Predviđale su se ogrom ne mogućnosti.
Poglavlje 1: Upoznavanje sa objektima 39
Na Webu se zaista m ogu naći i veoma pam etni apleti, ali do sveopšteg prelaska na aple-
te nije došlo. Verovatno je najveći problem bila veličina Javinog izvršnog okruženja (Java
Runtim e Environm ent, JRE) od 10 MB, koje je trebalo preuzeti s Weba i instalirati, što je
uplašilo prosečnog korisnika. Sudbinu im je m ožda zapečatila činjenica da je Microsoft
odlučio da JRE ne isporučuje kao deo svog Internet Explorera. U svakom slučaju, Java
apleti se nisu proširili posvuda.
Bez obzira na to, u nekim situacijama Java aplete i Java Web Start aplikacije još uvek
vredi imati. Ukoliko upravljate računarim a korisnika, recimo unutar preduzeća, bilo bi
p am etno da distribuciju i ažuriranje klijentskih aplikacija obavljate pom oću ovih tehno-
logija, jer ćete tim e uštedeti znatnu količinu vrem ena, tru d a i novca, naročito ako m orate
često da ažurirate njihov softver.
U poglavlju Grafička korisnička okruženja upoznaćem o Flex, novu Adobeovu tehno-
logiju koja obećava - u njom e se prave Flash apleti. Pošto više od 98 procenata svih čitača
Weba im a Flash Player (na W indowsu, Linuxu i M acu), m ožem o sm atrati da je on usvo-
jen standard. Flash Player se instalira i ažurira lako i brzo. Jezik ActionScript je napravljen
na osnovu ECMAScripta i stoga je prilično poznat, ali Flex omogućava program iranje bez
brige o specifičnostima raznih čitača - zato je daleko privlačniji od JavaScripta. Pri pro-
gram iranju na klijentskoj strani, ovu m ogućnost vredi razm otriti.
.NETi C#
Neko vrem e je glavni konkurent Java apleta bio Microsoftov ActiveX, iako samo na W in-
dovvs računarim a. O tad je M icrosoft napravio prvog pravog konkurenta celoj Javi u liku
platform e .NET i program skog jezika C#. Platform a .NET je približno isto što i Javina vir-
tuelna mašina (Java Virtual Machine, JVM) plus Java biblioteke. JVM je softverska plat-
form a na kojoj se izvršavaju java program i, a C# ima velike sličnosti s Javom. Bez sum nje,
radi se o dosad najboljem Microsoftovom proizvodu na području program skih jezika i
program skih okruženja. Naravno, Microsoft je imao veliku prednost jer je mogao da uči
na tuđim greškama, ali je bogami naučio. Prvi put od svog nastanka, Java je dobila pravu
konkurenciju. Zato su projektanti Jave u Sunu dobro pogledali C#, dobro razmislili o
tom e zašto bi program eri hteli da pređu na C#, i odgovorili: načinili su fundam entalna
poboljšanja Jave - Javu SE5.
T renutno je najveća siabost .NET-a važno pitanje da li će Microsoft dozvoliti njegovo
potpm io prenošenje na đruge platforme. Microsoft tvrdi da se to može napraviti bez pro-
blema, i već postoji delimična realizacija .NET-a koja radi na Linuxu (projekat Mono,
www.gomono.com), ali dok se ne napravi potpuna realizacija iz koje M icrosoft nije ništa
izbacio, .NET je rizično sm atrati rešenjem za sve platforme.
Internet i intranet
Wcl> je najopštije rešenje problem a klijent/server, pa je logično da istu tehnologiju
upotrebite i da biste rešili podgrupu tog problem a, posebno klasični problem klijent/ser-
ver um itar preduzeća. Pri tradicionalnom pristupu klijent/server, postoji problem zbog
raznih tipova računara kao i problem zbog teškog instaliranja novog klijentskog softvera.
Oba problem a dobro su rešena pom oću čitača Weba i program iranja s klijentske strane.
40 Misliti na Javi
Kada se tehnologija Weba koristi za inform acionu m režu ograničenu na određeno pre-
duzeće, to nazivamo intranet. Intranet obezbeđuje m nogo veću sigurnost nego Internet
jer pristup serverima u preduzeću možete fizički da kontrolišete. Izgleda da korisnici,
kadajednom shvate osnovni princip rada čitača, m nogo lakše izlaze na kraj s različitim iz-
gledima stranica i apleta, pa brže uče nove sisteme.
Problem s bezbednošću svrstava nas u jednu od grupa koje se obrazuju, čini se, po
autom atizm u, u svetu klijent/server program iranja. Ako se vaš program izvršava na In-
ternetu, ne znate na kojoj platform i će on raditi i želite da budete sasvim sigurni da ne ši-
rite neispravan kod. Treba vam nešto nezavisno od platform e i bezbeđno, kao što su
skript-jezik ili Java.
Ako radite na intranetu, pred vas se postavljaju drugačija ograničenja. Nije neuobiča-
jeno da svi računari budu platform e Intel/W indows. Na intranetu ste sami odgovorni za
kvalitet svog program a i možete da otklanjate greške kako se koja otkriva. Pored toga,
možda već imate dosta nasleđenog koda koji ste koristili za tradicionalniji klijent/server
pristup, pri čemu m orate fizički da instalirate ldijentske program e svaki p u t kada radite
na poboljšanju. Vreme protraćeno na instaliranje poboljšanja pruža razlog više da pređe-
m o na čitače gde su poboljšanja nevidljiva i autom atska. (Ovaj problem rešava i Java Web
Start.) Ako imate posla s takvim intranetom , najrazboritiji pristup je da krenete najkra-
ćim putem koji vam omogućava da koristite postojeću bazu koda, um esto da ponovo pi-
šete program e na novom jeziku.
Kada se suočite sa ovim zbunjujućim nizom rešenia za program iranje s klijentske stra-
ne, najbolji plan napada je analiza isplativosti. Razm otrite ograničenja koja vaš problem
nameće i najkraći put do rešenja. Pošto je program iranje s Jdijentske strane ipak progra-
miranje, uvek je dobro odabrati pristup koji omogućava najbrži razvoj u određenoj situa-
ciji. Ovo je hrabar stav koji vas priprem a za neizbežne susrete s problem im a u razvoju
programa.
stranice prelaze na Javu zbog tehnologije servleta i iz njih izvedenih JSP strana, najviše
zato što se tim e isključuju problem i pri radu sa čitačima različitih m ogučnosti. Progra-
miranje sa scrverske strane razm otreno je u knjizi Thitikingin Enterprise Java, koju može-
te nači na adresi www.MindView.net.
Uprkos tom e što se o Javi priča sam o u vezi sa Internetom , ona je programski jezik opšte
nam ene pom oću kogae možete rešiti bilo koji tip problem a, kao i pom oću drugih jezika.
Tu Javina snaga nije samo u prenosivosti, već i u lakoći program iranja, robusnosti, velikoj
standardnoj biblioteci i brojnim bibliotekama drugih proizvođača koje se stalno razvijaju.
Sažetak
Znate kako izgleda proceduralni program : definiciie podataka i pozivi funkcija. Da biste
pronašli svrhu takvog program a, m orate malo da se pom učite i pregledate pozive funk-
cija i koncepte niskog nivoa kako biste stvorili m odel u glavi. Zbog toga nam treba po-
sredno predstavljanje kada projektujem o proceduralni program : sami za sebe, ti
program i m ogu da zbunjuju jer su načini izražavanja usm ereni više ka računaru nego ka
problem u koji rešavamo.
Pošto OOP dodaje m noge nove ideje onim a koje nalazite u proceduralnim jezicima,
možda mislite da će Java program biti m nogo komplikovaniji nego ekvivalentni C pro-
gram. Bićete prijatno iznenađeni: dobro napisan Java program je uglavnom daleko jed-
nostavniji i m nogo razumljiviji od ekvivalentnog C program a. Imaćete definicije objekata
koji predstavljaju ideje u vašem prostoru problem a (um esto pitanja računarskog prikaza)
i poruke poslate tim objektima koje predstavljaju aktivnosti u tom prostoru. D obra stra-
na u objektno orijentisanom program iranju jeste ta što je uz dobro projektovan program
lako razumeti kod pri čitanju. O bično ima i znatno m anje koda, jer ćete m noge svoje pro-
bleme rešiti ponovnim korišćenjem postojećeg koda iz biblioteka.
O O P i Java ne m oraju da budu za svakoga. Važno je da procenite svoje potrebe i odlu-
čite da li će ih lava optim alno zadovoljiti ili bi bilo bolje da radite s nekim drugim pro-
gramskim sistemom (uključujući onaj koji tren u tno koristite). Ako znate da će vaše
potrebe biti usko specijalizovane u bliskoj budućnosti i ako imate specifična ograničenja
koja Java m ožda ne može da zadovolji, onda ispitajte ostale raspoložive m ogućnosti. Kon-
kretno, preporučujem da razm otrite Python; posetite www.Python.org. Ako ipak izabere-
te Javu, barem ćete znati koje ste m ogućnosti imali i zašto ste baš nju izabrali.
Sve je objekat
,,Da govorimo drugačiji jezik, percipirali bismo tiešto drugačiji svet.“
Ludwig Witgenstein (1889-1951)
JAVA I C ++ SU H IB R ID N IJE Z IC I ALI SU PROJEKTANTI JAVE SMATRALI DA HIBRID IZA CIJA NIJE
toliko bitna kao u C++-U. U h ibridnom jeziku može se koristiti više program skih stilova;
C++ je hibridan da bi obezbedio povratnu kom patibilnost s jezikom C. Pošto je C++
nadskup jezika C, on sadrži i m noge nepoželjne nasleđene osobine koje previše kompli-
kuju neke aspekte C++-a.
Jezik Java je napravljen za one koji žele da se bave samo objektno orijentisanim pro-
gram iranjem . Pre nego što se uopšte upustite u program iranje, m orate početi da raz-
mišljate objektno-orijentisano (osim ako već ne razmišljate tako). Ovaj početni napor će
vam se isplatiti jer ćete biti sposobni da program irate na jeziku koji se uči i koristi jedno-
stavnije od mnogih drugih O O P jezika. U ovom poglavlju razm atraćem o osnovne delove
Java program a i naučićemo da je u Javi (gotovo) sve objekat.
O v d e m o že d o ć i d o n esu g lasica. Im a lju d i koji k ažu : „ O čito , to jc pokazivač", ali tak av iskaz već sadrži
p re tp o s ta v k e o realizaciji. Po svojoj s in ta k si, Java refe re n c e su m n o g o s ro d n ije C + + re fe re n c a m a nego
p o k a ziv ač im a . U p rv o m iz d a n ju ove k n jig e o d a b ra o sa m n o v te rm in „ id e n tifik a to r (en g l. hnm U c)“ jcr
iz m e đ u re fe re n c i u C + + - u i re fe re n c i u Javi p o s to je b itn e razlike. Izlazio sa m iz C + + - a i n isa m želeo
d a z b u n ju je m p ro g ra m e re u C + + - u , za k oje sa m s m a tra o d a će b iti n a jb ro jn iji k o risn ic i Jave. Pre
n e g o što sam p rip re m io d ru g o iz d a n je, v id e o sa m d a je češće k o rišć en te rm in re fe re n c a, p o š to svako
ko p relazi sa C + + - a m o ra d a savlada m n o g c d ru g e p o jm o v e p o rc d te rm in o lo g ije re fe re n c i, još jc d a n
p o ja m neće p rev iše s m e ta ti. M e d u tim , neki se n e slažu č ak ni sa te rm in o m re fe re n c a. U je d n o j knjizi
tv rd i se kak o je „ p o tp u n o p o g re š n o rcći d a Java p o d rž a v a p ro s le d iv a n je p re k o rc fe re n c i11je r su id e n -
tifik ato ri o b je k a ta u Javi (p o to m a u to r u ) u s /iw /„ o b je k ti re fe re n c i“. A p o š to se svc (k a k o d aljc tv rd i)
u stva ri p ro s le d u je p o v re d n o s ti, vi n e o b a v lja te p ro s le d iv a n je p re k o rc fc rc n c i već „ p ro s le đ u jc te o b -
je k te re fe rc n c i p o v re d n o s ti“. M o že se d is k u to v a ti o p re c iz n o sti ta k o z a m rše n ih o b ja šn je n ja , ali sm a
tra m d a m o j p ris tu p p o je d n o sta v lju je s h v a ta n je k o n c e p ta , bcz ik akvih g u b ita k a (p a , te o re tič a ri jezika
m o g u d a tv rd e d a vas Iažem , ali ja ć u reći d a o b e z b e d u je m o d g o v a ra ju ć u a p s tra k c iju ).
Poglavjje 2: Sve je objekat 43
Daljinski upravljač može da postoji sam za sebe, bez televizora. O dnosno, to što imate
referencu, ne znači da obavezno im ate i objekat povezan s njom . Ako želite da čuvate reč
ili rečenicu, treba da napravite referencu na objekat ldase String:
String s;
Ali, ovim ste napravili satno referencu, ne i sam objekat. Ako pokušate u ovom trenut-
ku da pošaljete poruku preko s, dobičete grešku pošto s u stvari nije povezano ni sa čim
(nem a televizora). Zato je sigurnije da uvek inicijalizujete referencu kada je pravite:
String s = "asdf";
Ovde se koristi posebna osobina Jave: znakovni niz se m ože inicijalizovati tekstom pod
navodnicim a. Obično, za objekte m orate da koristite opštiji tip inicijalizacije.
String s = n ew S t r i n g ("asdf");
Ovo ne samo da znači: „Napravi mi nov objekat klase S trin g “, več daje i inform aciju o
tom e kako da se taj objekat napravi, tako što se navodi početni znakovni niz.
Pored tipa String Java ima obilje drugih unapred definisanih tipova. M nogo je važnije
da možete napraviti svoje tipove. U stvari, to je osnovna aktivnost u program iranju na
Javi i o tome čete učiti u ovoj knjizi.
3. Dinamička memorija: To je oblast m em orije opšte nam ene (takođe u radnoj me-
m oriji) gde obitavaju svi objekti. D obra strana dinamičke m em orije jeste to što, za
razliku od steka, prevodilac ne m ora da zna koliko dugo dodeljeno skladište m ora
da ostane u dinamičkoj m em oriji. Stoga im am o veliku fleksibilnost pri korišćenju
dinam ičke memorije. Kad god vam zatreba neki objekat, napišite kod za njegovo
pravljenje koristeći new, i skladište će se napraviti u dinamičkoj memoriji. Narav-
no, m orate platiti ovu fleksibilnost: skladište u dinamičkoj m em oriji duže se pravi
nego skladište na steku (kad biste uopšte mogli da napravite objekte na steku u Javi,
kao što možete u C + + -u).
4. Konstantno skladište: K onstantne vrednosti često se um eću direktno u kod pro-
gram a što je bezbedno jer se one nikad ne m ogu prom eniti. Ponekad se konstante
izdvojeno grupišu kako bi opciono mogle da se stave u ROM m em oriju (m em orija
samo za čitanje; engl. read-only memory), što se radi u računarim a ugrađenim u
druge uređaje.2
5. Skladišta van radne memorije: Ako su podaci p otpuno izvan program a, oni mogu
da postoje i kada se program ne izvršava, izvan kontrole program a. Dva osnovna
prim era za ovo su objekti u tokovima podataka (engl. streamed objects), što su
objekti pretvoreni u tok bajtova, obično da bi se poslali na drugu m ašinu, i trajni
objekti (engl. persistent objects), što su objekti stavljeni na disk da bi sačuvali svoje
stanje čak i kada se program završi. U ovim skladištima teško je pretvoriti objekte u
nešto što može da postoji na drugom m edijum u, a što može da postane regularni
m em orijski objekat kada je potrebno. Java obezbeđuje podršku za laku trajnost
(engl. lightvveight persistencc), a m ehanizm i kao što su JDBC i Hibernate obez-
beđuju sofisticiraniju podršku za skladištenje i preuzim anje inform acija o objekti-
ma u bazama podataka.
boolean - - - Boolean
char 16 Unicode 0 Unicode 2'6-l Character
byte 8 -128 + 127 Byte
short 16 -2'5 +2I5-I Short
int 32 -23' +23'-l Integer
long 64 -263 +263-l Long
float 32 IEEE754 IEEE754 Float
double 64 IEEE754 IEEE754 Double
void - - - Void
Svi num erički tipovi su označeni (sadrže i pozitivne i negativne vrednosti), stoga ne
tražite neoznačene tipove.
Veličina tipa boolean nije izričito definisana; zadato je samo da može da im a vrednosti
true ili false.
Om otačkc klase za proste tipove podataka omogućavaju da u dinamičkoj m em oriji
napravite objekat koji predstavlja određen prost tip. Na primer:
char c = 'x 1;
C h a r a c t e r ch = n e w C h a r a c t e r ( c ) ;
C h a r a c t e r ch = n e w C h a r a c t e r ( 'x ');
C h a r a c t e r ch = 'x ';
char c = ch;
Nizovi u Javi
Praktično svi program ski jezici podržavaju neku vrstu nizova. Korišćenje nizova u C -u i
C + + - U rizično je jer su ti nizovi samo blokovi m em orije. Ako program pristupi nizu iz-
van njegovog m em orijskog bloka ili koristi m em oriju pre inicijalizacije (što su česte greš-
ke u program iranju), doći će do nepredvidljivih situacija.
Jedan od osnovnih ciljeva Jave je sigurnost; u njoj se uopšte ne pojavljuju m nogi pro-
blemi koji muče program ere u C-u i C + + -u. U Javi je niz garantovano inicijalizovan i ne
može m u se pristupiti van njegovog opsega. Opseg se proverava po cenu male količine
dodatnog utroška m em orije u svakom nizu, kao i uz proveru indeksa pri izvršavanju, ali
se pretpostavlja da su sigurnost i povećana produktivnost vredni toga (a Java katkada
može da optim izuje te operacije).
Kada pravite niz objekata, vi, u stvari, pravite niz referenci, i svaka od tih referenci
autom atski se inicijalizuje na posebnu vrednost. Za tu vrednost koristi se rezervisana reč
null. Kada Java naiđe na null, ona prepoznaje da dotična referenca ne pokazuje na obje-
kat. Pre nego što je upotrebite, svakoj referenci m orate da dodclite objekat, i ako pokušate
da koristite referencu koja još uvek ima vrednost null, problem će biti prijavljen pri izvr-
šavanju. Na taj način su tipične greške s nizovima predupređene 11 Javi.
Možete da napravite i niz prostih tipova. Ponovo, prevodilac garantuje ispravnu inici-
jalizaciju jer m em oriju za taj niz puni nulama.
Nizovi će biti detaljnije objašnjeni u narednim poglavljima.
Oblast važenja
Većina proceduralnih jezika počiva na oblasti važcnjn (engl. scope). O na određuje vidlji-
vost i životni vek imena definisanih u n u tar te oblasti. U C-u, C + + -u i Javi oblast važenja
je određena vitičastim zagradam a { }. Na prim er:
Poglavjje 2: Sveje objekat 47
{
int X = 12;
/ / d o s t u p n a j e s am o p r o m e r l j i v a x
{
int q = 96;
// d o s t u p n e su i x i q
/ / d o s t u p n a j e sa mo x
/ / q j e " i zvan ob l a s t i važenja"
}
Promenljiva definisana unutar oblasti važenja dostupna je samo do kraja te oblasti.
Sav tekst nakon ’/ / ’ pa do kraja reda predstavlja komentar.
Uvlačenje pom aže da se program pisan u Javi lakše čita. Pošto je Java jezik slobodne
forme, dodatni razmaci, tabulatori i novi redovi ne utiču na rezultujući program.
U Javi ne tnožete da uradite sledeće, iako je to dozvoljeno u C-u i C++-u:
{
int x = 12;
{
int x = 96; // n e p r o p i s n o
}
}
Prevodilac će ol)javiti da je prom enljiva x već definisana. Stoga mogućnost C-a i C++-
a da sakrije promenljive u većoj oblasti nije dozvoljena, je rsu projektanti Jave mislili da to
vodi do program a koji zbunjuju.
{
S t r i n g s = n ew S t r i n g ("neki znakov ni niz");
} // kraj oblasti važenja
referenca s će nestati na kraju oblasti važenja. M eđutim, objekat klase String na koji je s
ukazivala još uvek zauzima mem oriju. U ovom deliću koda neina načina da pristupite
objektu nakon oblasti važenja, jer je jedina referenca na njega nedostupna izvan oblasti
važenja. Dalje u knjizi videćete kako reference na objekte mogu da se prosleđuju i um no-
žavaju celim tokom programa.
Pošto se objekti napravljeni pom oću operatora new zadržavaju dokle god su vam po-
trebni, ispostavlja se da mnoštvo problem a u program iranju u C + + -u ne postoji u Javi. U
C + + -u se m orate pobrinuti ne sam o za to da objekti postoje đolde god su vam potrebni,
nego ih m orate i uništiti kada s njim a završite.
48 Misliti na Javi
Zbog toga se postavlja važno pitanje. Ako Java ostavlja objekte da leže unaokolo, šta ih
sprečava da prepune m em oriju i zaustave program? Upravo bi se ta vrsta problem a poja-
vila u C ++-u. Na ovom mestu deluje malo magije. Java ima sakupljač smeća (engl. garbage
collector); on vodi računa o svim objektima koji su napravljeni operatorom new i otkriva
one na koje više nema referenci. Zatim oslobađa m em oriju koju su zauzimali ti objekti, pa
ta m em orija može da se koristi za nove objekte. To znači da nikada ne m orate sami da se
brinete o oslobađanju memorije. Samo napravite objekte, a kada vam više nisu potrebni,
oni će sami otići. Ovo eliminiše jednu klasu program skih problem a, takozvano curenje
memorije (engl. memory leak), pri kome program er zaboravi da oslobodi m em oriju.
Ovo uvodi nov tip, iako se telo klase sastoji samo od kom entara (zvezdica i kosa crta i
ono što je izm eđu tih znakova, o čem u će biti reči đalje u ovom poglavlju), pa malo šta
možete s njom da uradite. M eđutim, rnožete da napravite objekat ovog tipa koristeći new:
NekoI m e T i p a a = new N e k o I m e T i p a ( ) ;
Ali klasi ne možete reći da mnogo toga uradi (tj. ne možete joj poslati neke važne po-
ruke) sve dok ne definišete neke njene metode.
Polja i metode
Kada definišete lclasu (a sve što radite u Javi jeste definisanje klasa, pravljenje objekata tih
klasa i slanje poruka tim objektim a), u nju možete staviti dve vrste elemenata: polja (koja
se ponekad nazivaju i podaci članovi) i metode (koje se katkada nazivaju fimkcije članice).
Polje je neki od prostih tipova ili objekat bilo kog tipa, s kojim možete da se povežete pu-
tem njegove reference. Reference na objekte m orate da indjalizujete (koristeći new, kao
što ste videli ranije), da biste ih povezali sa stvarnim objektima.
Svaki objekat ima zasebno skladište za svoja polja; obično se polja ne dele između obje-
kata iste klase. Evo prim era klase s nekim poljima:
cla s s S a m o P o d a c i {
int i ;
d o u b l e d;
b o o l e a n b;
I
Poglavlje 2 : Sve je objekat 49
Ova klasa ne radi ništa, sem što čuva neke podatke. Ali ovako možete da napravite objekat:
Poljima možete dodeliti vrednosti, ali prvo m orate da znate kako da se obratite članu
nekog objekta. To se postiže tako što se navede im e reference na taj objekat, zatim sledi
tačka pa im e člana u nu tar objekta:
r e f e r e n c a N a O b j e k a t .polj e
Na prim er:
podaci .i = 4 7 ;
p o d a c i . d = 1.1;
p o d a c i . b = false;
Vaš objekat može da sadrži i druge objekte koji sadrže podatke. Samo nastavite da ,,na-
dovezujete tačke“.
Na prim er:
m o j A v i o n . l e v i R e z e r v o a r . k a p a c i t e t = 100;
Klasa SamoPodaci ne može skoro ništa da uradi osim da čuva podatke, jer nem a me-
tode. Da biste shvatili metode, prvo m orate da naučite šta su argumenti i povratne vred-
nosti (ubrzo će biti opisani).
boole<an talse
char "\u0000" (nullj
byte |byte)0
short (short)O
mt 0
long 0L
float O.Of
double O.Od
Dobro obratite pažnju na to da Java jamči dodelu podrazum evanih vrednosti samo
kada se promenljiva koristi kao članica klase. To obezbeđuje da prom enljive članice pro-
stog tipa uvek budu inicijalizovane (to C ++ ne radi), čime se sm anjuje izvor grešaka.
Ipak, ova početna vrednost ne m ora da bude ispravna niti odgovarajuća za program koji
pišete. Najbolje je da uvek izričito inicijalizujete svoje promenljive.
50 Misliti na Javi
O va garan cija se ne o d n o si na lokalne pro m en ljiv e - o n e koje nisu polja klase. Stoga,
ako u n u ta r definicije m eto d e im ate:
in t x;
P ovratni tip opisuje tip vred n o sti koja se vraća kao rezu ltat m eto d e koju ste pozvali. Li-
sta a rg u m en ata zadaje tipove i im ena inform acija koje želite da p rosledite m etodi. Im e m e-
tode i lista a rg u m en ata (koji zajedno čine njen potpis) jed in stv en o id entifikuju m etodu.
M etode u Javi m o g u biti nap rav ljen e sam o kao deo klase. M etoda m ože da se poziva
sam o za neki o b je k a t,’ i taj objek at m o ra da b u d e sp o so b an da izvrši poziv. Ako po k u šate
da pozovete p ro g re šn u m e to d u objekta, d obićete p o ru k u o grešci p rilik o m prevođenja.
M eto du objekta pozivate tako što navedete im e olijekta iza koga je tačka, zatim im e m e-
tod e i n jenu listu a rg u m en ata, recim o ovako:
Na p rim er, p re tp o stav im o da im ate m eto d u f() koja nem a arg u m e n te i vraća v red n o st
tip a in t. Z atim , ako im ate o b jek at pod im en o m a koji im a m eto d u f(), m ožete napisati
sledeće:
in t x = a . f ( ) ;
sta tic m etode, o kojim a ćete uskoro učiti, rnogu hiti pozvauezd klnsii, bez objekta.
Poglavlje 2: Sveje objekat 51
Lista argumenata
Lista arg u m en ata m etode o dreduje koje inform acije pro sled u jete m etodi. Kao što p re tp o -
stavljate, te inform acije - kao i sve d ru g o u Javi - im aju fo rm u objekata. Stoga u listi argu-
m en ata m o ra te da navedete tipove objekata koji se p ro sleđ u ju i im en a koja će se ko ristiti za
svaki od njih. Kao i u d ru g im situacijam a u Javi gde se čini da rad ite sa o b jektim a, vi, u
stvari, prosleđujete reference.4 Tip reference m o ra da b u d e ispravan. A ko a rg u m e n t treba
da b u d e objekat klase String, o nda m o rate da prosledite objek at tip a String ili će prevodi-
lac prijaviti grešku.
R az m o trim o m e to d u čiji arg u m en t je objek at tip a String. Sledi definicija m eto d e koja
m o ra biti postavljena u n u ta r definicije ldase d a bi bila prevedena:
in t s k la d is te (S trin g s) {
return s .le n g th () * 2;
}
Kada je po vratni tip void, rezervisana reč re tu r n se koristi sam o da bi se nap u stila m e-
toda i stoga je n ep o treb n a kada se d o đ e do kraja m etode. Iz m eto d e se m ožete vratiti iz bilo
koje tačke, ali ako ste naveli p ovratni tip koji nije void, prevodilac će vas, bez o bzira na m e-
sto sa kog vraćate, naterati (p o ru k am a o greškam a) da v ratite odgovarajući tip vred n o sti.
U ovom tre n u tk u m ožda izgleda da je p ro g ram sam o gom ila o bjekata sa m e to d a m a čiji
sli arg u m en ti dru gi objekti i koji šalju po ru ke tim d ru g im o bjektim a. To se uglavnom i de-
šava, ali u nared n o m poglavlju naučićete kako da urad ite posao niskog nivoa, d o n o šen jem
o dluk a u n u ta r m etode. Ako u ovom poglavlju savladate slanje p o ru k a - biće dovoljno.
1 Uz uobičajeno izuzim anje ranijc pom en utih prostih tipova podataka boolean, char, byte, short, int,
iong, float i double. I’o pravilu, vi prosleđtijete objekte, što znači da prosleđujete reference na objekte.
52 Misliti na Javi
Vidljivost imena
K ontrola im en a je p ro b le m u svim p ro g ra m sk im jezicim a. Ako k o ristite im e u je d n o m
delu p ro g ra m a, a d ru g i p ro g ra m e r k o risti isto im e u d ru g o m delu, kako da razlikujete
je d n o im e o d d ru g o g i sprečite d a se „ su d a re “? O vo je p o seb n o ozbiljan p ro b lem u C -u jer
p ro g ram često sadrži m o re im en a n e p o d e sn ih za rad . C + + klase (n a k o jim a su zasnovane
Java klase) funkcije u g n ežđ u ju u n u ta r Idasa, pa zato i ne m o g u da se su dare sa im enim a
funkcija u g n ežđ en im u n u ta r d ru g ih ldasa. M eđ u tim , u C + + -u se i dalje koriste globalni
p o d a ci i g lobalne funkcije p a je su d a ra n je još uvek m oguće. D a bi se rešio taj p ro blem , u
C + + je p o m o ć u d o d a tn ih rezervisan ih reči uveden itnenski prostor (engl. namespace).
Z ahvaljujući n ov o m p ristu p u , Java je sve to m ogla da izbegne. Za p ravljenje je d n o -
zn ačn ih im en a za biblioteke, p ro je k ta n ti Jave žele d a ko ristite im e vašeg In te rn e t d o m ena
u o b rn u to m p o retk u , p o što su ta im en a sig u rn o jedinstvena. Pošto je im e m og d om en a
MindView.net, m oja u slu žn a b ib lio tek a za o tld an jan je n ed o statak a dobila bi im e
net.m indview.utility.foibles. Iza o b rn u to g im en a d o m e n a , ostale tačke treba da predsta-
vljaju p o d d irek to riju m e.
U Javi 1.0 i Javi 1.1, oznake d o m e n a com, edu, org, net itd. po konvenciji su pisane ve-
likim slovim a, pa bi se b ib liotek a zvala: NET.mindview.utility.foibles. Tokom razvoja
Jave 2 o tk riv en o je da to izaziva pro b lem e, pa se sada celo im e paketa piše m alim slovim a.
O vaj m eh an izam znači da sve vaše dato teke au to m atsk i žive u svojim im enskim pro-
sto rim a i da svaka klasa u n u ta r d ato tek e g aran to v an o im a jed in stv en iden tifikator - o
to m e se jezik b rin e u m esto vas.
import j a v a . u t i l, A r r a y L is t ;
kako b iste saopštili p rev o d io cu da želite d a k o ristite Javinu klasu ArrayList. M e đ u tim , p a-
ket util sadrži više kJasa i m o žete poželeti da ko ristite nekoliko n jih a d a ih p ri to m izričito
ne deklarišete. To se lako p ostiže o zn ak o m * za slo b o d an izbor:
import j a v a . u t i l
Skupove klasa češće ćete uvoziti na ovaj n ačin nego p o jed in ačn o .
c la s s S ta tic T e s t {
s t a t i c i n t i = 4 7;
}
Cak i ako posle ovoga n ap rav ite dva objekta klase StaticTest, i dalje će posto jati sam o
je d a n p rim e ra k skladišta za p ro m en ljiv u StaticTest.i. O ba objekta će deliti istu p ro m e n -
Ijivu i.
N aravno, pošto sta tic m etode nc zalitcvaju da ijedan objekat bude napravljen pre nego što će se kori-
stiti, one ne m ogu iliivkliu) da pristupe nestatičnim članicam a ili m etodam a. Pošto nestatični članovi
i m etode m oraju biti povezani sa odgovarajućim objektom , iz statičnih m etoda m ožete pristupati ne-
statičnim članovim a sam o preko nekog im en ovanogobjekta.
54 Misliti na Javi
S ta tic T e s t s t l = new S t a t i c T e s t ( ) ;
S ta tic T e s t st2 = new S t a t ic T e s t O ;
U ovom tre n u tk u i stl.i i st2.i im aju istu v red n o st, 47, p o što se o d n o se na isti deo ine-
m orije.
Postoje dva način a za p ristu p statičn o j p ro m en ljiv o j. Kao što je po k azan o u p re th o d -
n o m p rim e ru , m o žete joj p ristu p a ti p rek o objekta, n a p rim e r st2.i. M ožete joj p ristu p a ti
i d ire k tn o preko im en a klase, što ne m o žete d a u rad ite sa n e statič n o m članicom .
StaticT e st.i+ + ;
class MozeSeUvecati (
s t a t ic void u ve c a j() { S ta tic T e s t.i+ + ; )
}
Pošto je uvecaj() statična m eto d a, m o žete je pozvati d ire k tn o p rek o njene klase:
MozeSeUvecati. u ve caj( ) ;
Iako rezervisana reč static, p rim e n je n a na polje, defin itiv n o m enja način na koji se
p o d a tak stvara (po jed an za svaku klasu u o d n o su na po jedan nestatičan za svaki obje-
kat), kada se p rim en i na m eto d u , p ro m e n e nisu tako velike. M etode se označavaju rezer
visan o m rečju static kako bi se m ogle pozvati iako nije naprav ljen objekat. O vo je veom a
važno, kao što ćem o videti, za đ efinisanje m eto d e main() koja je p o četn a tačka za pokre-
tan je svih aplikacija.
// Zdravo.java
import ja v a . u t i l
im e klase i im e datoteke su isti. Kada pravite nezavisan p ro g ra m kao što je ovaj, jeđ n a od
klasa m ora da ima isto im e kao datoteka. (Prevodilac se b u n i ako tako ne uradite.) Ta klasa
m ora da sadrži m etodu pod im en o m main() sa sledećim p o tp iso m i p o v ratn im tipom :
Prevodilac za Javu i Suiiova dokum t’ntacija stalno se m enjaju i najbolje ih je preuzim ati neposredno
od Suna. Ako je sam i preuzm etc, dobićete najnoviiu verziju.
56 Misliti na Javi
pu b lic c la s s ShowProperties {
public s t a t ic void m a in (S trin g [] args) {
Syste m .g etPro p erties() .1 is t(S y s te m .o u t);
System .out.pri n tl n(System .getProp erty("user.nam e")) ;
Sy ste m .o u t.p rin tln (
System .getProperty( " j a v a . 1i b ra ry .p a th ")) ;
)
} ///:-
Prvi red u m eto d i m a in () p rikazu je sva svojstva (engl. properties) sistem a na kojem
izvršavate p ro g ram , dakle daje p o d atk e o o k ru ž en ju . M etoda list() šalje te rezu ltate svom
a rg u m e n tu S y stem .o u t. V idećete u n astavku knjige da ih m ožete poslati i na d ru g a m esta,
recim o u neku d ato tek u . M ožete zatražiti i o d re đ e n o svojstvo - u ovom slučaju, korisnić-
ko im e (engl. user nam e) i p u ta n ju (engl. path) do Javine biblioteke (engl. library). (M alo
kasnije o b jasnićem o n eo b ićn e k o m en tare na p o četk u i na kraju.)
Prevođenje i izvršavanje
Da biste preveli i po k ren u li ovaj p ro g ram , kao i sve d rug e u ovoj knjizi, prvo m o ra te da
im ate razvojno o k ružen je za Javu. Postoji više nezavisnih razvojnih o k ru žen ja, ali p retp o -
stavićem o d a koristite b esp latn o razvojno ok ru žen je JDK, k o m p an ije Sun M icrosystem s.
Ako koristite neki dru g i razvojni sistem ,' m o raćete da pogledate d o k u m en ta ciju za taj si-
stem d a biste saznali kako da p rev o d ite i pokrećete p ro g ram e.
Povežite se na In te rn et i p o gledajte W eb lokaciju http://iava.suii.coin. Tam o ćete naći
in form acije i veze koje će vas voditi kroz proces p reu zim an ja i in staliran ja )D K -a za vašu
p latfo rm u .
O bičn o je to IBM -ov prevodilac jikes, koji je zn atn o brži od Sunovog javac (iako razlika nije velika
kada p o m o ću Anta pravite grupe datoteka). Postoje i progranii otvorenog koda za pravljenje Java
prevodilaca, izvršnih okruženja i biblioteka.
Poglav[je 2: Sve je objekat 57
ja va c H ello D ate.java
ja v a HelloDate
/* Ovo j e komentar
* koji se n a s ta v lja
* u v iš e redova
*/
Dokumentacioni komentari
V erovatno da je najveći p ro b lem p ri d o k u m e n to v a n ju koda bilo o d ržavanje d o k u m e n ta -
cije. A ko su k o d i d o k u m en tacija razdvojeni, po staje n ezg o d n o m en jati d o k u m en tac iju
svaki p u t kada p ro m e n ite kod. Rešenje izgleda jed n o stav n o : povežite k o d i d o k u m e n ta c i-
ju . N ajlakše ćete to u ra d iti kad sve stavite u istu d ato tek u . D a biste zaokružili p o stu p ak ,
p o tre b n a je p o seb n a sintaksa za d o k u m e n ta c io n e k o m en tare, kao i alat kojim ćete te ko-
m e n ta re izdvojiti i preb aciti ih u k o ristan oblik. To je u rad ila Java.
Alat za izdvajanje k o m en tara naziva se Javadoc i deo je instalacije JDK-a. O n koristi neke
o d tehnologija Java prevodioca da p o traži posebne oznake k o m en tara u p ro g ram u . Izdvaja
označene inform acije i izvlači im e klase ili m etode koja ide uz taj kom entar. Na ovaj način
m ožete sa m in im aln o m količinom rad a generisati p risto jn u doku m en taciju p rogram a.
R ezultat ovog p o stu p k a je H T M L d atotek a ko ju m ožete da p regledate p o m o ć u čitača
W eba. Tako Javadoc om ogućava d a na p ra v ite i održav ate sam o je d n u izv o rn u d ato te k u i
a u to m atsk i generišete k o risn u d o k u m en taciju . Z ahvaljujući p ro g ra m u Javadoc im am o
je d n o stav an sta n d a rd za pravljenje đ o k u m en tacije, pa m o žem o da o čekujem o ili čak za-
h tev am o d o k u m en ta c iju iz svih Java biblioteka.
Uza sve, m o žete n ap isati sopstvene Javadoc id en tifikato re (engl. handlers), doclete,
ukoliko k o m en tare koje je izdvojio Javadoc hoćete p o seb n o da o b rad ite (recim o, da ih
ispišete u d rug ačijem fo rm a tu ). O d ocletim a čitajte u d o d a tk u na h ttp ://M in d V iew .n et/
Books/Betterjava.
Sledi uvod u Javadoc i pregled njegovih obeležja. P o tp u n opis naći ćete u d o k u m e n ta -
ciji o JD K-u. Kada je raspakujete, p o tra žite p o d d ire k to riju m „tooldocs" ili p ritisn ite istoi-
m en u hipervezu.
Sintaksa
Sve k o m and e p rog ram a Javadocnalaze se isključivo u n u ta r k o m entara /**. K om entar se za-
vršava sa */ kao i obično. Postoje dva osnovna načina za korišćenje ovog sistem a: ugrađeni
H TM L ili korišćenje d o k u m en tacio n ih oznaka (engl. doc tags). Samostojeće dokum entacio-
n eo zn a k e su ko m an d e koje p o čin ju sa @ i koje se nalaze na početku reda kom entara. (Zane-
m aruje se vodeća zvezdica.) Nesam ostalnc dokum entacionc oznake m ogu biti napisane bilo
gde u n u ta r Javadoc k o m en tara i takode počinju sa @, ali se nalaze izm eđu vitičastih zagrada.
Postoje tri tipa d o k u m e n ta c io n ih k o m en tara koji o d govaraju elem en tu k om e kom en-
ta r p reth o di: klasi, polju ili m etodi. K om en tar klase stoji o d m ah ispred d e fin iđ je klase;
k o m e n ta r polja se pojavljuje o d m a h ispred definicije polja, a k o m e n ta r m etode se javlja
n e p o sred n o ispred definicije m etode. O vo je jed n o stav an prim er:
Ugrađeni HTML
Javadoc prep isu je H TM L oznake iz k o m en tara u H T M L d o k u m e n t koji generiše. To
o m og u ćava p o tp u n o korišćenje H TM L-a; m eđ u tim , o sn ov n i cilj je da se o m o g u ć i fo rm a-
tira n je koda, kao što je:
// : object/Documentation2.java
j
* <pre>
*System .out. p rin tln(n ew Date( ) ) ;
* </pre>
*/
///: '
p u b lic c lass Documentation2 {}
//: object/Documentation3.java
/**
* Možete u b aciti <em>čak</em> i li s t u :
* <ol>
* <li> Prva tačka
* <1i> Druga tačka
* <1i> Treća tačka
* </ol>
*/
pu b lic c lass Documentation3 {}
Primeri oznaka
Evo p rim e ra n ekih Javadoc ozn ak a koje se m o g u stavljati u d o k u m e n ta c iju koda. Pre nego
što p o m o ć u Javadoca p o k u šate d a u ra d ite bilo šta ozbiljno, treb alo bi da p ro čitate n jem u
posvećen odeljak u d o k u m e n ta ciji JD K -a i tam o vid ite sve m oguće način e u p o tre b e
Javadoca.
@see:
O va o zn ak a služi za u p u ćiv an je na d o k u m e n ta c iju u d ru g im klasam a. Javadoc će generi-
sati HTML sa o zn ak am a @see kao hip erv ezam a po vezanim s d ru g o m d o k u m en tacijo m .
O blici su:
@see imeklase
Psee potpuno-opisano-imeklase
@see potpuno-opisano-imeklase#ime-metode
{©docRoot}
D aje relativ n u p u ta n ju d o koren sko g d irek to riju m a d o k u m en tacije. Koristi se za izričito
h iperpovezivanje sa stra n ic a m a u stablu do k u m en tacije.
{@inheritDoc}
Tekući d o k u m en tac io n i k o m e n ta r n asleđuje d o k u m en tac iju najbliže o sn o v n e klase ove
klase.
@version
N jen oblik je:
gde je informacija-o-verziji bilo koja b itn a inform acija koju treba uključiti. Kada sc in-
d ik a to r -version navede na k o m a n d n o j liniji p ro g ram a javadoc, inform acija o verziji će
biti p ro sleđ en a u gen erisan u H T M L d o k u m en taciju .
@author
N jen oblik je:
@since
O va oznaka o m og u ćava da naznačite verziju klase koja je počela da k oristi o d re đ e n u m o -
gućnost. V idećete da se pojavljuje u Java H T M L d o k u m en taciji da naznači koja je verzija
JD K -a korišćena.
©param
K oristi se za d o k u m e n to v a n je m e to d a u obliku:
gde je ime-parametra id en tifik ato r u listi p a ra m e ta ra m eto d e, a opis tekst koji se m ože
p ro te g n u ti na nekoliko redova. S m atra se da je opis završen kada se naiđe n a n o v u d o k u -
m e n ta c io n u o zn aku . M ožete im ati koliko ho ćete ovih oznaka, najčešće p o je d n u za svaki
param etar.
@return
Koristi se za d o k u m e n to v a n je m eto d a, a piše se u obliku:
Pretu rn opis
@throws
O izuzecim a g o v o rim o u poglavlju O brada grcšaka pom oću izuzctaka. U kratko, to su
objekti koji m og u biti ,,bačeni“ iz m eto d e u slučaju greške. Iako sam o je d an objekat izu-
zetka m ože d a se pojavi kada pozovete m eto d u , o d ređ en a m eto d a m ože da proizvede više
razlićitih tipova izuzetaka a svi m o raju biti u n a p red naznačeni. Stoga oznaka za izuzetak
im a sledeći oblik:
gde p o tp u n o -o p is a n o -im e k la s e nedvosm isleno daje iine klase izuzetka koja je negde de-
finisana, a o p is (koji m ože da se p ro teg n e na nekoliko redova) pokazuje zbog čega odre-
đeni tip izuzetka m ože da se pojavi pri pozivu inetođe.
62 Misliti na Javi
©deprecated
O vo se koristi da naznači m o g u ćn osti koje su zastarele. O znaka @deprecated pokazuje da
više ne treba da koristite tu m ogućnost, p o što će uskoro v erovatno biti u klonjena. Prevodi-
lac će vas upozoriti ako koristite m e to d u označenu sa @deprecated. U Javi SE5, @depreca-
ted je zam enjena anotacijom @Deprecated (anotacijam a je posvećeno poglavlje Anotacije).
Primer dokumentacije
P onovo dajem o prvi p ro g ra m n a Javi, ovog p u ta s d o d a tn im d o k u m e n ta c io n im k o m en -
tarim a:
U prvom redu datoteke koristi se m oja tehnika: stavlja se //: kao posebne oznake za red
s kom entarom koji sadrži ime izvorne datoteke. Taj red sadrži inform aciju o p utanji do da-
toteke (object označava ovo poglavlje), iza koje sledi ime datoteke. Poslednji red takode se
završava kom entarom ( / / / : - ) koji označava kraj listinga izvornog koda i om ogućava da se
taj kod autom atski ažurira u tekstu ove knjige (nakon što ga prevodilac p ro v eri) i da se izvrši.
O znaka /* Ispis: naznačuje početak izlaza koji će ovaj p ro g ram generisati. U ovom obli-
ku, (55% nratch) naznačuje sistem u za testiranje da se izlaz priličn o razlikuje od jed n o g do
drugog izvršavanja i da treba da očekuje korelaciju od sam o 55 pro cen ata sa ovde prikaza-
nim izlazom. O d onih p rim era u ovoj knjizi koji im aju neki izlaz, većina će sadržati ovaj
oblik izlaza, pa ćete m oći da izvršite svaki pro g ram i da proverite da li m u je izlaz ispravan.
Poglavlje 2: Sve je objekat 63
Stil programiranja
P rem a stilu o p isano m u knjizi Codc C onventions fo r the Java Program m ingLatiguage8 prvo
slovo im en a klase treba da b u d e veliko. A ko se im e klase sastoji o d nekoliko reči, o n e se
pišu zaje dn o (tj. ne koristite d o n je crte d a razdvojite im en a ), i prvo slovo svake u g rad en e
reči je veliko, na p rim er:
c lass SveBojeDuge { // . . .
Tako n ap isano im e klase liči n a grbe kam ile. Za skoro sve ostalo: m eto d e, p olja (p ro -
m enljive članice) i im ena referenci n a objekte, usvojeni stil je isti kao i za klase, osim što
se p rv o slovo identifikatora piše m alo. N a p rim er:
c lass SveBojeDuge {
in t c e o B ro jK o jiP re d s ta v lja B o je ;
void promeniNijansuBoje ( in t novaNijansa) {
//
}
/ / •••
}
M islite na to kako k o risn ik tak o đ e m o ra da kuca sva ova đugačka im ena, stoga im ajte
m ilosti.
U Java kodu iz Sunovih b iblioteka k oristi se isti stil p isanja o tv o ren ih i zatv o ren ih vi-
tičastih zagrada p o p u t stila u p o treb ljen o g u ovoj knjizi.
Sažetak
Cilj ovog poglavlja je da nau čite tek toliko Jave koliko je dov o ljn o da biste napisali je d n o -
stavan p ro g ram . Stekli ste i uvid u jezik i njegove o sn o v n e pojm ove. Do sada su ipak svi
p rim eri bili u znaku „uradi ovo, zatim u rad i o n o , a p o to m nešto treće“. U n a re d n a dva p o-
glavlja upoznaćete o sno v n e o p e rato re koji se u p o treb ljav aju u p ro g ra m ira n ju na Javi i
naučićete da upravljate tok o m p ro g ram a.
Vežbe
U b ud uće će vežbe biti raspodeljene po celom tek stu poglavlja, ali u ovom su sve vežbe sta-
vljene na kraj, pošto ste tek naučili da pišete najjed n o stav n ije p ro g ram e.
Iza red n o g broja vežbe nalazi se broj u zag rad am a, koji u op seg u od 1 do 10 pokazuje
njenu težinu.
Rešenja o d a b ra n ih vežbi nalaze se u elek tro n sk o m d o k u m e n tu The T hinking in Java
A nno tatcd Solution Guide, koji se m ože k u p iti na adresi w w w .M indV iew .net.
Vežba 1: (2) N apišite klasu koja će sad ržati neinicijalizovana p o lja tip a int i char, i ispišite
njihove v red n o sti kako biste se uverili da ih Java p o d ra z u m e v a n o inicijalizuje.
Vežba 2: (1) N akon p rim e ra Zdravo.java u ov om poglavlju, n a p ra v ite p ro g ra m „zdravo,
svete“ koji p rikazu je d a tu n a re d b u na ek ran u . P o treb n a vam je sam o je d n a m e to d a u klasi
(m a in koja se izvršava kada se p ro g ra m p o k re n e). Setite se da tre b a d a o stan e statična i da
uključite listu a rg u m en ata, iako je nećete koristiti. Prevedite p ro g ra m k o m a n d o m javac i
p o k ren ite ga k o m a n d o m java. A ko k o ristite razvojno o k ru ž en je koje je d ru g ačije od
JD K -a, n au čite kako d a prevedete i izvršite p ro g ra m e u to m o k ru ž e n ju .
Vežba3: (1) P ro n a đ ite delove k o d a koji o b u h v ataju klasu NekoImeTipa i p re tv o rite ih u
p ro g ra m koji m ožete d a p revedete i izvršite.
Vežba 4: (1) P retvo rite deliće k od a koji o b u h v ataju klasu SamoPodaci u p ro g ra m koji
m ožete d a prevedete i izvršite.
Vežba 5: (1) Izm enite p re th o d n o vežbanje tako d a v red n o sti p o d a ta k a u klasi SamoPoda-
ci b u d u d o d eljen e i ispisane iz m eto d e main().
Vežba 6: (2) N apišite p ro g ra m koji o b u h v ata i poziva m e to d u skladiste(), d efm isan u kao
deo k o da u ovom poglavlju.
Vežba 7: (1) Pretvorite MozeSeUvecati delove k o d a u p ro g ra m koji radi.
Vežba 8: (3) N apišite p ro g ra m koji po k azu je da postoji sam o je d a n p rim e ra k o d ređ en o g
statičnog polja u klasi, bez o b zira na to koliko objekata te klase n apravite.
Vežba 9: (2) N apišite p ro g ra m koji po k azu je da a u to m atsk o pakovanje fu n k cio n iše za sve
pro ste tipove i njihove o m otače.
V ežba 10: (2) N apišite p ro g ra m koji ispisuje tri a rg u m e n ta p re u ze ta s k o m a n d n e linije.
Da biste to uradili, pozabavite se ind ek sim a u nizu objekta klase S trin g s k o m a d n e linije.
Vežba 11: (1) Pretvorite p rim er SveB ojeD uge u pro g ram koji m ožete da prevedete i izvršite.
Vežba 12: (2) P ro n a đ ite kođ za d ru g u verziju p ro g ram a Z d rav o .ja v a, koji je jednostavan
p rim e r d o k u m e n ta c io n ih k o m e n ta ra. P rop ustite tu d ato tek u k ro z Jav ad o c i pogledajte
rezultate p o m o ć u čitača W eba.
Vežba 13: (1) P ro p u stite kroz Jav ad o c dato teke D o k u m en tacija 1.java, D o k u m en taci-
ja2.java i D okum entacija3 .jav a, i p ro v erite rezultate p o m o ć u čitača W eba.
V ežba 14: (1) D od ajte H T M L listu d o k u m en taciji u p re th o d n o m vežbanju.
Vežba 15: (1) U zm ite p ro g ra tn iz vežbe 2 i d o d ajte m u d o k u m e n ta c io n e k o m en tare. Iz-
dvojite te d o k u m e n ta c io n e k o m e n ta re u H TM L daiOteku koristeći Jav ad o c i pregledajte
ih p o m o ć u čitača W eba.
V ežba 16: (1) U poglavlju Inicijnlizacijn i čišćenje p ro n ađ ite p rim e r P re ld a p a n je .ja v a i do-
dajte m u d o k u m e n ta c io n e k o m en tare . Izdvojite te d o k u m e n ta c io n e k o m e n ta re u H TM L
d ato te k u koristeći Jav ad o c i p regledajte ih p o m o ću čitača W eba.
Operatori
N a n ajnižem nivou, u Javi se s podacim a radi pom oću operatora.
Priori teti
P rioriteti o p erato ra definišu kako će izraz biti izraču n at kada se u n jem u javlja vi.še ope-
rato ra. Java im a specifična p ravila koja o d re đ u ju p o red ak izračunavanja. Najlakše se
p am ti pravilo da se m n o žen je i deljenje vrše p re sab iranja i o d u zim an ja. P ro gram eri često
zaborave ostala pravila o p rio rite tim a , p a stoga nije na o d m e t da koristite zagrade za iz-
ričito navođenje p o retk a izračun av an ja. Na p rim er, p ogledajte n ared b e (1) i (2):
//: o p e r a t o r i/ P r io r it e t i.java
public c lass P r io r i t e t i {
public s t a t ic void main ( S t r i ng[] args) {
in t x = 1, y = 2, z = 3;
in t a = x + y - 2/2 + z; // (1)
in t b = x + (y - 2)/(2 + z ); // (2)
System .o u t.p rin t1 n ("a = " + a + " b = " + b ) ;
I
} /* Is p is :
a = 5 b = 1
* ///:-
Poglav[je 3: Operatori 67
Te n ared b e izgledaju gotovo jed n ak o , ali iz izlaza vidite d a zbog u p o treb e zagrada u (2)
im aju sasvim različita značenja.
O b ra tite pažn ju na u p o tre b u o p e ra to ra + u n ared b i System.out.println(). U to m k o n -
tekstu , + znači „nadovezivanje zn ak o v n ih nizova“ i, ako treb a, „konverzija zn ak o v n ih ni-
zova“. Kada p revodilac n aiđ e na String + ne-String, p ok u šaće d a k o n v ertu je ne-String u
String. Kao što vid ite iz izlaza, a i b je u sp ešn o konv erto vao iz tip a int u String.
Dodela vrednosti
V red n ost se dodeljuje o p e ra to ro m = . O n znači „u zm i v red n o st s d esn e stra n e koja se često
naziva dvrcdnost (engl. rvalue), i kopiraj ie n a levu stra n u , koja se često naziva Ivrednost
(engl. lvalue)“. D vrednost je bilo koja k o n sta n ta, p ro m en ljiv a ili izraz koji m ože d a se iz-
račun a, ali Ivrednost m o ra da b u d e p o seb n a im en o v an a pro m en ljiv a. (O d n o sn o , m o ra
p o sto jati fizički p ro sto r za sm eštan je te v red n o sti.) N a p rim er, m o žete d odeliti k o n sta n t-
n u v red n o st prom enljivoj
a = 4;
ali k o n sta n tn o j v red n o sti ne m o žete n išta da d o d elite - o n a n e m ože b iti Ivrednost.
(N e m o žete n apisati 4 = a;.)
D odela v red n osti prosto g tip a je p riličn o očigledna. Pošto p ro sti tipovi čuvaju stvarnu
v red no st, a ne referencu na objekat, kada d o d elite v red n o st p rom enljivoj pro sto g tipa, vi
k o p irate sadržaj s jed n o g m esta na d ru g o . Na p rim er, ako napišete a = b za proste tipove,
o n d a se sađržaj p rom enljive b ko p ira u sadržaj pro m en ljiv e a. Ako n akon toga nastavite
da m en jate a, ta izm ena, narav n o , neće uticati na b. Kao p ro g ram er, u većini situacija ovo
i očekujete.
M eđ u tim , kada d odelju jete v re đ n o sti o b jek tim a, stvari se m en jaju . D ok rad ite sa
ob jek to m , vi radite s referencom , a kada d o d elju jete „jedan objekat d ru g o m “, zapravo ko-
pirate referencu s je d n o g rnesta na drugo. To znači da će, kada napišete c = d za objekte,
i c i d pokazivati na objekat na koji je p rv o b itn o pokazivao d. Evo p rim e ra koji će pokazati
to ponašanje:
c lass Rezervoar {
in t nivo;
}
Klasa Rezervoar je je d n o sta v n a i njena dva p rim e rk a (rl i r2) n ap rav ljen a su u m eto d i
m ain(). Polju nivo u svakom p rim e rk u klase Rezervoar d od eljen e su različite v rednosti,
zatim je v red n o st r2 d o d eljen a p rim e rk u rl i p o to m je r l p ro m en jen . U m n o g im p ro -
g ram sk im jezicim a očekivali b iste da su r l i r2 nezavisni sve vrem e, ali, p o što ste d odelji-
vali reference, kada se izm eni o b jek at rl, m enja se i objekat r2, zato što i r l i r2 sađrže istu
referencu koja pokazuje na isti objekat. (P rv o b itn a referenca koja se nalazila u rl i koja je
pokazivala na objekat koji je čuvao v red n o st 9, izm en jen a je p rilik o m đodele i efektivno
izgubljena; n jen objekat će p o čistiti sakupljač sm eća).
O vaj fenom en često se naziva pojavap scu d o n im a (engl. aliasing), i on je u o snovi način
na koji Java rad i sa o b jek tim a. Ali šta ako nećete da se u ovom slučaju pojavi pseu d o n im ?
M ožete da precizirate d o d elu i d a napišete:
r l.n iv o = r2 .n ivo ;
cla s s Slovo {
char c;
Poglavlje 3: Operatori 69
y .c = ' z ' ;
Matematički operatori
O sn ov ni m atem atičk i o p e ra to ri su isti kao i u većini p ro g ram sk ih jezika: sabiranje (+ ),
o d u z im an je (-), deljenje (/), m n o žen je (*) i m o d u lo (% ), koji daje o statak p ri celobroj-
n o m deljen ju. Pri celo b ro jn o m deljenju odseca se realni deo, a ne zaok ru žu je rezuitat.
Java tako d e koristi skraćeni C /C + + zapis za isto v rem en o ob avljanje o p eracije i dođele.
To se naznačava na sledeći način: iza o p erato ra sledi znak jedn ako sti, i d o sled n o se m ože
p rim e n iti na sve o p era to re u jeziku (kada to im a sm isla). Na p rim e r: da biste d o dali 4 p ro -
m enljivoj x i rezultat dodelili x, pišite: x + = 4.
O vaj p rim e r p okazuje korišćenje m atem atičk ih op erato ra:
p r in t("u /= v : + u );
) /* Is p is :
j : 59
k : 56
j + k : 115
j - k : 3
k / j : 0
k * j : 3304
k % j : 56
j %= k : 3
Poglavlje 3: Operatori 71
v : 0.5309454
w : 0.0534122
v + w : 0.5843576
v - w : 0.47753322
v * w : 0.028358952
v / w : 9.940527
u += v : 10.471473
u -= v : 9.940527
u *= v : 5.2778773
u /= v : 9.940527
* ///:-
x = -a ;
x = a * -b;
x = a * (-b );
Broj 4 / se sm atrao „čarobnim brojem " na koledžu koji sani pohađao, pa m i se to urezalo u sećanje.
72 Misliti na Javi
Jedno o d objašnjenja za p o rek lo im en a C + + , koje znači „jed an k o rak dalje o d C -a“, leži
u o p e ra to ru uvećanja. U vrem e n a sta n k a Jave, Bill Joy (jedan o d a u to ra Jave) rekao je da
je „Java = C + + —“ (C p lus p lu s m in u s m in u s), nagoveštavajući kako je Java jezik C + + iz
kojeg su izbaćeni n e p o tre b n i teški delovi, pa je zbog toga m n o g o jednostavnija. Što b u -
dete više napred ovali kroz ovu k njigu, videćete da su m n o g i delovi jednostavniji, ali im a
d ru g ih stvari zbog kojih Java ipak nije m n o g o jed n o stav n ija o d C + + -a .
Operatori poređenja
R ezultat o p e ra to ra p o ređ en ja je logička v re d n o st (boolean). O n i o cen ju ju relaciju izm e-
d u v re d n o sti dva o p era n d a. Izraz sa o p e ra to rim a p o ređ e n ja daje v re d n o st true ako je re-
lacija tačn a i false ako je relacija n etač n a. O p e ra to ri p o ređ en ja su m an je o d (< ), veće od
(> ), m an je ili jed n ak o (< = ), veće ili jed n ak o (> = ), jed n a k o (= = ) i različito (!=). Jednakost
i različitost se m ogu p rim e n iti n a sve proste tip o v e p o d atak a , ali se ostala p o ređ en ja ne
m o g u p rim e n iti na tip boolean. O b jekti tip a boolean m o g u im ati sam o v red n o sti true ili
false, p a ne bi im alo sm isla tra ž iti koji o d njih je veći, a koji m anji.
N aredba S y s te m .o u t.p rin tln ( n l = = n 2) ispisuje rezu ltat logičkog p o ređ en ja koje se
u n u ta r nje nalazi. Njegov rezultat bi sig u rn o treb alo da b u d e tru e, a o n o g sledećeg false,
pošto su v redno sti oba objekta klase In te g e r iste. Ali iako je sadržina o b jekata ista, refe-
rence su različite, a o p e rato ri = = i != p o red e reference objekata, a ne n jihovu sadržinu.
Stoga je rezultat prvog p oređ en ja false, a d ru g o g tru e . N aravno, ovo isprva iznenaduje.
Šta ako želite da p o red ite ekvivalenciju stv arn ih sadržaja objekata? U to m slučaju m o-
rate da koristite poseb nu m eto d u eq u a ls() koja p o stoji u svim objek tim a (ta m eto d a se ne
koristi za proste tipove, za koje ionako d o b ro fu n k cio n išu = = i !=). Evo kako se o n a koristi:
74 Misliti na Javi
R ezultat će sada biti kao što i očekujete. Ah, ali to ipak nije baš tako jed n o stav n o . Ako
sam i n ap rav ite klasu, na p rim er:
c lass Vrednost {
i nt i ;
}
public class MetodaEquals2 {
p ublic s t a t ic void main (S t r in g [] args) {
Vrednost v l = new V red n o st();
Vrednost v2 = new VrednostO ;
v l . i = v2 .i = 100;
System .out.pri n t l n ( v l . equ als(v 2 )) ;
}
} /* Is p is :
fa l se
* ///:-
ponovo ćete biti zbunjeni: rezultat je false, zato što je p o d ra z u m e v an o p o n ašan je m etode
equals() da poredi reference. Stoga n e ć e ted o b iti željeno p o n ašan je osim ako ne rcdefinišeie
(engl. override) m etodu equals() u vašoj novoj klasi. N ažalost, o redefm isanju nećete učiti
sve do poglavlja Ponovno korišćenje klasa, a o p rav iln o m načinu definisanja m etode equ-
als() do poglavlja Detaljno razm atranje kontejnera, ali u m eđ u v re m e n u sam o pazite na na-
čin na koji se m etoda equals() p onaša, jer će vas to m ožda sačuvati od problem a.
Većina klasa iz Javine biblioteke realizuje m eto d u equals() tako da o n a p oredi sadržaj
objekata u m esto njihovih referenci.
Vežba 5: (2) N apravite klasu Pas koja sadrži dve prom enljive tipa String: ime i kaže. U
m etod i main() n apravite dva objekta tip a Pas sa im en im a Fleki (koji k aže:„R afi“) i Buva
(koji kaže: ,,Vafi“ ). Z atim p rik ažite njihova im ena i šta kažu.
Vežba 6: (3) Posle vežbe 5, napišite n o v u referencu objekta tip a Pas i d o d elite je Flekije-
vom objektu. Z atim obavite p o red en je koristeći = = i equals() za sve reterence.
Poglavlje 3: Operatori 75
Logički operatori
Logički o p e ra to ri konju n k cija (&&), d isjunkcija (II) i negacija (!) daju v re d n o sti true ili
false tipa boolean. U ovom p rim e ru k oriste se o p e ra to ri p o re đ e n ja i logički o p erato ri:
// Tip in t u Ja v i ne može da se
// k o ris ti kao lo g ič k i tip
//! p r i n t ( " i && j je " + ( i && j ) ) ;
//! p r i n t ( " i I I j je " + ( i | ( j ) ) ;
//! p r i n t ( " ! i je " + ! i ) ;
p r in t("(i < 10) && (j < 10) je "
+ ( (i < 10) && (j < 10)) ) ;
p r in t (" (i < 10) j | (j < 10) je "
+ ( (i <10) || ( j < 10)) ) ;
}
} /* Is p is :
i = 58
j = 55
i > j je true
i < j je fa ls e
i >= j je true
i <= j je fa ls e
i == j je fa ls e
i != j je true
(i < 10) && (j < 10) je fa ls e
(i < 10) || (j < 10) je fa ls e
* ///:-
O p erato re konjunkcije, disjunkcije i negacije m ožete p rim e n iti sam o na v red n o sti tip a
b o o le an . Za logičke uslove ne m ožete da koristite ostale (nelogičke) tipove, kao u jezici-
m a C i C + + . O vakve neuspele pokušaje m ožete v ideti u k o m e n ta rim a sa o zn ak o m //!
76 Misliti na Javi
Nepotpuno izračunavanje
K ada rad ite s logičkim o p e ra to rim a , naići ćete na fen o m en p o d nazivom „ n e p o tp u n o iz-
raču n av an je“. To znači da će izraz b iti izraču n av an sam o do tren u tk a kada tačn o st ili ne-
ta čn o st celog izraza m ože n ed v o sm islen o da se odredi. Z ato p reostali delovi logičkog
izraza u opšte neće b iti izraču n ati. Evo p rim era koji p o kazuje n ep o tp u n o izračunavanje:
Svaki test vrši p o ređ en je sa a rg u m e n to m i vraća v re d n o sti true ili false. T akođe ispi-
suje i p o ru k u da je bio pozvan. Testovi se pozivaju preko sledećeg izraza:
P riro d n o je da pom islite kako će sva tri testa biti izvršena, ali izlaz pokazuje drugačije:
Prvi test daje rezultat tr u e pa se izračunavanje izraza nastavlja. M eđutim , drug i test daje re-
zu ltat false. Pošto to znači da će i rezultat celog izraza sigurno biti false, zašto nastavljati iz-
računavanje izraza? To m ože da potraje. Baš zbog toga se koristi n e p o tp u n o izračunavanje:
ako ne treba do kraja izračunavati sve delove logičkog izraza, p ro g ram će m ožda raditi brže.
Literali
Kada u p ro g ra m u doslov n o n avodite v ređ no sti, prevodilac o b ičn o tačn o zna kojim tip o m
da ih p redstavi. P o nek ad se m ože javiti n ed ou m ica. U to m slučaju m o rate da n avo dite
p rev o d io ca d o d a tn im in fo rm ac ijam a u oblik u znakova p rid ru ž e n ih v red n o sti literala.
Sledeći p rim e r po k azu je te znakove:
c : 1111111111111111
b: 1111111
s : 111111111111111
* ///■ -
Slovo iza literala zadaje njegov tip. Veliko ili m alo L označava tip long (m eđ u tim , m alo
1 zb un juje zato što liči n a jed in icu ). Veliko ili m alo F označava tip float. Veliko ili m alo D
označava tip double.
Sve celo b ro jn e tipove p o d atak a m oguče je zadavati u h ek sad ecim aln o m obliku (sa
o sn o vom 16), n azn ačav an jem v o d ečim 0x ili 0X n ak o n čega slede sim boli 0 -9 i a -f, bilo
m alim ili velikim slovim a. A ko p o k u šate da inicijalizujete p ro m en ljiv u v red n o šču većom
od one koju m ože d a čuva (bez o b zira na n u m eričk i oblik v red n o sti), prevodilac če pri-
javiti grešku. U g o rn jem p rim e ru o b ra tite p ažn ju na m aksim alne m oguće heksadecim al-
ne v red n o sti za tipove char, byte i short. Ako ih p rekoračite, prevodilac če ih auto m atsk i
p retv o riti u int i jav iti kako treb a izvršiti sužavanje eksplicitnom konverzijom za d a tu d o-
delu. (E ksplicitne konverzije su d efin isan e u nastavku poglavlja.) Tada ćete znati da ste
prekoračili dozvo ljen u v red n o st.
O ktalni ob lik (sa o sn o v o m 8) nazn ačav a se v odećom n u lo m u zapisu broja, koju slede
cifre 0 -7 .
U C -u, C + + -u i Javi ne postoji prikaz b in arn ih brojeva kao literala. M eđutim , pri rad u s
heksadecim alnim i o k taln im zapisom rezultate je podesno prikazati u b in a rn o m obliku. To
se lako postiže m eto d am a static toBinaryString() iz klasa Integer i Long. Im ajte u vidu da
se m anji tipovi autom atski k o nvertuju u int kada se proslede u Integer.toBinaryString().
Vežba 8: (2) Pokažite da hek sad ecim aln i i o k taln i zapis rade s brojevim a tipa long. Za pri-
kazivanje rezultata u p o treb ite Long.toBinaryString().
Eksponencijalni zapis
Za ekspon en cijaln i zapis realnog b ro ja k oristi se notacija koju sam o duvek sm atrao
o b esh ra b ru ju ć o m .
U nauci i inžen jerstv u ’e’ o značava o sno vu p riro d n o g logaritm a, koja otprilike iznosi
2,718. (Preciznija v re d n o st tip a d o u b le je d o stu p n a u Javi kao M ath.E .) O n a se k o risti u
ek sp o n en cijaln im izrazim a kao što su 1,39 x e'43, što znači 1,39 x 2,718‘43. T vorci FOR-
T R A N -a odlučili su d a če e značiti „deset na step en “, što je ču dn o, je r se FO RTRAN u p o -
trebljava u nauci i inženjerstvu, i svako je m ogao pom isliti d a će tvorci o b ra titi p a žn ju na
takvu d vo sm islen ost.2 Bilo kako bilo, ovaj običaj je nastavljen u C -u , C + + -u i sada u Javi.
Stoga, ako ste se navikli na e kao na o sn ov u p riro d n o g logaritm a, o b ra tite p a žn ju kad a u
Javi v id ite izraz kao što je 1,39 e-43f; o n o značava 1,39 x 10~43.
P rateći znak n e m o ra te da k o ristite kada prevodilac m ože sam da o d red i odgovarajući
tip. Kada napišete:
long n3 = 200;
nem a dvosm islenosti, pa je L iza bro ja 200 iziišno. M eđ utim , kada napišete:
John K irkham jc napisao: „R ačunarim a sam počeo da se bavim 1962. koristeći FORTRAN II na m aši-
ni IBM 1620. U to vrem e, tokom šezdesetih i početkom sedam desetih, FORTRAN je u p o tp u n o sti
pisan velikim slovim a. To potičc verovatno od toga što su stari uređaji za u no s koristili 5-bitni Bau-
d o to v kod koji nije p o d r/av ao m ala slova. Slovo E je u eksponencijalnoj notaciji uvek pisano kao ve-
liko i nikada nije bilo m ešano sa osnovom p riro d n o g logaritm a, e, koja se uvek piše m alim slovom .
H je označavalo eksponent, obićno 10, za korišćeni brojni sistem. U to vrem e i oktalni form at je bio
široko rasp ro stranjen m edu p rogram erim a. Da sam naišao na oktalni broj u eksponencijalnoj nota-
ciji, sm a trao bih da je osnova 8. Sećam se da sam p rvi p ut video eksponencijalni zapis napisan s
m alim e u kasnim sedam desetim i sm atrao sam ga zbunjujućim . Froblem se pojavio kada su m ala
slova p ro d rla u FORTRAN, ne na saniom poćetku. Mi sm o, u stvari, imali funkcije koje sm o koristili
ako nam je zaista bila potrebna osnova p riro d n o g logaritm a, ali su sve bile pisane velikim slovim a.“
80 Misliti na Javi
daje n u lu sam o ako su oba ulazna bita jednaka nuli. Isključiva disjunkcija nad bitovim a ili
XOR ( A) daje jedinicu kao v red n o st izlaznog bita, ako je jed an ili d ru g i ulazni bit je d n a k je -
dinici, ali ne i oba. Negacija nad bitovim a (~ , koja se takođe naziva i operatorprvogkom ple-
m enta) jeste u n a rn i o p erato r; im a sam o jed an arg u m en t. (Svi ostali o p erato ri n ad bitovim a
su b in a rn i o p erato ri - im aju dva o p eran d a.) Negacija n ad b ito v im a daje negaciju ulaznog
bita - jed inicu ako je ulazni b it je d n ak nuli i n u lu ako je ulazni b it jed n ak jedinici.
Za o p erato re n ad b ito v im a i logičke o p e rato re k o riste se isti sim boli, p a će v am biti
lakše da se setite značenja ako se d o setite sledećeg: p o što su bito v i ,,m ali“, za o p erato re nad
b ito v im a koristi se sam o p o jed an znak.
O p e ra to ri n a d b ito v im a m o g u da se k o m b in u ju sa z n ak o m = i tim e u jed in e operaciju
sa dodeljivanjem : dozvoljeni su &=, 1= i A=. (Pošto je ~ u n a rn i o p erato r, o n ne m ože da
se k o m b in u je sa zn ak o m =.)
T ip boolean se tre tira kao je d n o b itn a v red n o st, pa je situacija nešto d rugačija. N ad
njim m ožete da p rim e n ite k o n ju n k ciju , d isju n k ciju i isključivu d isju n k ciju n ad bitovim a,
ali ne i negaciju nad b ito v im a (verovatno da bi se izbeglo m ešanje s logičkom negacijom ).
Za tip boolean o p e rato ri n ad b ito v im a im aju isti efekat kao i logički o p e ra to ri, osim što
se n e javlja n e p o tp u n o izračunavanje. O p e ra to r isključive disju n k cije n ad b ito v im a nem a
ekvivalentan logički op erato r. Z ato ovaj o p e ra to r predstavlja jed in i način da n a dve vred-
nosti tipa boolean p rim e n ite o p eraciju XOR. Na pro m en ljiv e tip a b o o le a n ne m ožete
p rim en jiv ati o p erato re p o m e ran ja koje ćem o u p rav o opisati.
Vežba 10: (3) N apišite p ro g ram s dve b in a rn e k o n stan te koje im aju n aizm en ičn e jedinice
i nule, s tim što je prvoj na n ajm an je zn ačajn o m m estu nula, a d ru g o j jed in ica. (U putstvo:
to će vam biti najlakše s hek sad ecim aln im k o n sta n tam a). Z ad ajte ta dva b ro ja kao arg u -
m en te svih o p e ra to ra n ad bito v im a na sve m o g u će naćine, a rezu ltate prikažite m eto d o m
Integer.toBinaryString().
Operatori pomeranja
O p erato ri p o m e ran ja (engl. shift) takođe rade s bitovim a. O ni m o g u da se k oriste isklju-
čivo s p ro stim , celobrojnim tip o v im a. O p e ra to r p o m era n ja ulevo (< < ) kao rezultat daje
o p e ra n d s leve stra n e o p e rato ra, p o m e ren ulevo za broj bitova naveden n.ikon o p erato ra
(niži bitovi p o p u n jav aju se n u lan ia). O p e ra to r o zn ačen o g p o m e ran ja u d esn o (> > ) kao
rezultat daje o p e ra n d s Ieve stra n e o p e ra to ra , p o m eren u d esn o za bro j bitova naveden na-
kon o p erato ra . O zn ačen o p o m eran je ud esn o > > k oristi produžavanje uz očiivanje znaka
(engl. sign extension)\ ako je v red n o st p ozitivna, viši bitovi se p o p u n jav aju nu lo m ; ako je
v red n o st negativna, viši bitovi se p o p u n jav aju jedinicom . U Javu je takođe d o d a to i neo-
značen o p o m e ran je u d esn o » > koje k oristi produžavanje nz dodavanje nula (engl. zero
extension): bez o bzira na znak, viši bitovi se p o p u n jav aju n u lo m . Ovaj o p e ra to r ne postoji
u C -u ili C++-U.
Ako p o m e ra te v red n osti tip a char, byte ili short, on e će biti p ro širen e na int pre nego
što se izvrši p o m eran je i rezu ltat će biti tip a int. Koristi se sam o p et nižih b itova vrednosti
s desne stra n e o p erato ra. O vo vas sprečava da izvršite p o m eran je za više m esta nego što
ima bitova u ru ita r p ro m en ljiv e tip a int. Ako o perišete n ad v red n o šću tipa long, dobićete
Poglavlje 3: Operatori 81
rezu ltat tipa Iong. Biće k orišćeno sam o šest nižih b ito v a v re d n o sti s desne stra n e o p era-
to ra, pa ne m ožete da izvršite p o m e ra n je za više m esta nego što im a b itova u n u ta r p ro -
m enljive tip a long.
P om eranje m ože da se k o m b in u je sa z n ak o m jed n ak o sti ( « = ili » = ili » > = ) .
L vrednost se zam enjuje v red n o šću lv red no st p o m e re n o m za d v re d n o st m esta. Kada se
n eozn ačen o p o m eran je u d esn o k o m b in u je sa d o d e lo m , po sto ji p ro blem . A ko ovu o p era-
ciju p rim e n ite na vredn o sti tip a b y te ili short, nećete d o b iti p rav iln e rezultate. U m esto
toga, on e se p ro širu ju na int, p o m e ra ju ud esn o, ali se sk raću ju p riiik o m d odele, pa u tim
slučajevim a kao rezultat d obijate —1. To pok azuje n a red n i p rim er:
//: operatori/NeoznacenoPomeranjeUdesno.java
// Test za neoznačeno pomeranje udesno.
import s t a t ic n e t.m in d v ie w .u til. P r in t . * ;
long 1 = s lu c a ja n . nextlong( ) ;
long m = s lu c a ja n . nex tLo n g();
p rin tB in a ry L o n g ("- lL ", -1L);
prin tB in aryLo n g (" +1L“ , +1L);
long 11 = 9223372036854775807L;
pri ntBi naryLong( " n a jv e ć i", 11);
long 11n = -9223372036854775808L;
p rin tB i naryLong("najmanj i " , 11 n ) ;
pri ntBi naryLong ( " 1 " , 1 );
p rin tB in aryLo n g ("~ 1 " , -1);
p rin tBin aryLong( " - 1 " , -1);
prin tB in aryLon g ("m ", m);
p rin tB in a ryLo n g ("l & m", 1 & m);
p rin tB in a ryLo n g ("l | m", 1 | m);
p rin tB in aryLo n g ("l " m", 1 ^ m);
p r intBinaryLong("1 « 5 ", 1 « 5 );
Poglavlje 3: Operatori 83
* // /= -
84 Misliti na Javi
return i * 100;
el se
return i * 10;
}
p ub lic s t a t ic void m a in (S trin g [] args) {
p r in t ( t e r n a r n i( 9 ) ) ;
p r in t ( t e r n a r n i(1 0 ));
p r in t ( s t a n d a r d n iIf E ls e ( 9 ) ) ;
p r in t ( s t a n d a r d n iIf E ls e ( lO ) ) ;
}
} /* Is p is :
900
100
900
100
* ///:-
V idite d a je k o d m eto d e ternarni() sažetiji od o noga što biste m o rali pisati d a te rn a r-
nog o p e ra to ra nem a, kao u m eto d i standardniIfElse(). M eđ u tim , standardniIfElse() se
lakše čita i brže piše. Stoga d o b ro razm islite p re nego što u p o treb ite te rn a rn i o p e ra to r -
po pravilu, to je u m esn o kada pro m en ljiv o j dodelju jete je d n u od dve v rednosti.
while (x = y) {
/ / ••••
}
Kao što vidite, eksplicitnu konverziju je m oguće izvršiti na nu m eričk o j vred n o sti, kao
i na p rom enljivoj. Im ajte u vidu da je đozvoljena i izlišna eksplicitna konverzija. Na p ri-
m er, prevodilac, kad god treb a, a u to m atsk i prevodi v red n o st tip a int u tip long; ipak, su-
višna konverzija se dozvoljava, je r tim e m ožete d a ie naglasite ili da kod u činite jasnijim .
U d ru g im situacijam a, eksplicitna konverzija m ože b iti n e o p h o d n a da bi kod uop šte m o -
gao biti preveden.
U C -u i C++-U , a u to m atsk a konverzija m ože izazvati m alo glavobolie. U Javi je a u to -
m atska konverzija b ezb ed n a osim kada vršite takozvanu snžavajuću konverziju (tj. kada
prelazite sa tipa p o d atak a koji m ože da čuva više inform acija na onaj koji čuva m anje), pri
čem u rizikujete da izgubite inform acije. U to m siučaju prevođilac vas prisiljava da vršite
eksplicitn u konverziju, obaveštavajući vas na taj način da ,,to m ože biti op asn o - a ako
ipak to želite, m oraćete izrićito da tražite konverziju“. Proiirujuću konverzijn ne treba iz-
rićito zahtevati jer novi tip m ože da ćuva z n a tn o više in fo rm acija nego stari, pa se infor-
m acije nikad ne gube.
Java dozvoljava da izvršite e k sp liđ tn u konverziju iz bilo kog pro sto g tipa u bilo koji
dru gi prost tip, izuzev tipa boolean koji u o p šte ne m ožete k o nvertovati. Klase takođe ne
dozvoljavaju eksplicitnu konverziju. Da biste konvertovali jed n u klasu u d ru g u , za to
m o ra da postoji posebna m etoda. (Kasnije u knjizi videćete i da se m ože vršiti eksplicitna
konverzija objekata u o k v iru porodice tipova; Hrast m ože biti eksplicitno k o nvertovan u
Drvo i o b rn u to , ali ne i u stran i tip kao što je Stena.)
88 Misliti na Javi
Odsecanje i zaokruživanje
K ada obavljate sužavajuću konverziju, m o ra te p aziti na odsecan je i zaokru živ an je. P rim e-
ra rad i, ako realan bro j tip a float izričito p retv o rite u ceo bro j tip a int, šta u ra d i Java? Na
p rim er, broj 29.7 p retv arate u int - hoćete li d o b iti 30 ili 29? O d g o v o r n a to p ita n je nave-
d e n je u sledećem p rim e ru :
Dakle, odgovor je da se p rilik o m eksplicitne konverzije tipa float ili double u ceo broj
(tip int), decim ale uvek odsecaju. U koliko h oćete da rezultat b u d e z ao k ru žen , u p o tre b ite
m e to d u round() iz b iblioteke java.lang.Math:
Pošto round() p rip ad a biblioteci java.Iang, nije p o tre b n a p o seb n a n ared b a iinport za
n jen o uvoženje i korišćenje.
Unapređenje tipova
Ako vršite bilo koju m atem atičk u operaciju i operaciju nad b itov im a s p ro stim tipo vim a
p o datak a, koji su m anji o d int (tj., char, byte ili short), videćete d a će te v red n o sti b iti u n a-
p ređ en e u tip int pre nego što se operacije izvrše, a rezultat će tak ođ e biti tipa int. Stoga,
ako tu v red n o st želite da p onovo dodelite m an jem tip u , rn orate da koristite eksplicitnu
konverziju. (A pošto vršite d o d elu m an jem tip u , m o gu se izgubiti inform acije.) U prin ci-
p u , najveći tip p o datak a u izrazu o d ređ u je veličinu rezu ltata tog izraza; ako p o m n o ž ite flo-
at i double, rezultat će b iti tipa double; ako saberete int i long, rezultat će biti tip a long.
X > » = 1;
x &= y;
x "= y ;
x |= y;
// E k s p lic itn a konverzija:
//! boolean bl = (boolean)x;
byte b = (b yte )x ;
short s = (s h o rt)x ;
in t i = ( in t ) x ;
long 1 = (lon g )x ;
flo a t f = ( f lo a t ) x ;
double d = (double)x;
)
void byteTest(byte x, byte y) {
92 Misliti na Javi
Sažetak
Ako im ate iskustva s bilo kojim jezik o m čija je sintaksa slična C -ovoj, videli ste kako su Ja-
vini o p e rato ri toliko slični o n im a iz vam a zn a n ih jezika da ih gotovo ni ne treb a učiti.
U koliko vam je poglavlje bilo tesko, p ogledajte m u ltim ed ijsk u prezentaciju T hinking in C,
koja je d o stu p n a na adresi www.M iin1Vicw.iici.
Rešenja odabranih vežbi data su u elektronskom dokumentu Thinking in lava Annotatcd Solution
Guide, koji se može kupiti na lokaciji tvww.MindView.rwt.
Kontrolisanje izvršavanja
Kao i svesno biće, i program tnora da upravlja svojim okruženjem i da bira šta će da radi.
U lavi birate pom oću naredaba za kontrolisanje toka programa.
JAVA KORISTI SVE NAREDBE JEZIKA C ZA KONTROLU IZVRŠAVANJA, STOGA ČE VAM, AKO STE
p ro g ram ira li na C -u ili C ++-U , veći deo ovoga što sledi biti p o zn at. M n ogi p ro ced u raln i
p ro g ram sk i jezici im aju neku v rstu n ared b e za k o n tro lu i m eđ u jezicim a često posto je
sličnosti. M eđ u rezervisane reči za k o n tro lu izvršavanja u Javi sp ad aju if-else, while, do-
while, for, return, break i n are d b u izbora switch. Java, m eđ u tim , ne p o d ržava p riličn o
o p a sn u n a re d b u goto (koja i dalje m ože b iti najefikasniji način za rešavanje o d re đ e n e vr-
ste p ro b lem a ). I dalje m ožete da n ap rav ite skok koji je nalik na goto, ali je m n o g o više
o g ran ičen o d tip ičn e nared b e goto.
Logičke vrednosti
Sve uslovne n ared b e koriste tačn o st i n etačn o st uslovnih izraza da bi odredile to k izvrša-
vanja. P rim er uslovnog izraza je a = = b. Tu se k o risti o p e ra to r p o ređ en ja = = d a se utvrdi
da li je v red n o st a jed n ak a v red n osti b. Ovaj izraz vraća true ili false. I bilo koji o p e ra to r
p o ređ en ja iz p re th o d n o g poglavlja m ože se ko ristiti za fo rm iran je uslovnog izraza. O b ra-
tite p ažn ju na to da Java ne dozvoljava da kao logički tip (boolean) koristite broj, iako je
to dozvoljeno u C -u i C ++-U (gde je tačn o sve različito o d nule, a n etač n o je nu ia). Ako
želite da k o ristite nelogički tip u logičkom testu, na p rim e r if(a), prvo ga m o rate pretvo-
riti u v re d n o st tipa boolean p o m o ću usiovnog izraza, na p rim e r if(a != 0).
Naredba if-else
N aredba if-else je najosnovniji način na koji se k o ntroliše to k p ro g ram a. D eo else nije
obavezan, pa stoga if m ožete da koristite u dva oblika:
i f (1ogi č k i- i zraz)
naredba
i f (1ogi č k i- i zraz)
naredba
el se
naredba
Logički izraz m o ra da vraća rezultat tipa boolean. Naredba je jed n o stav n a nared b a
koja se završava z nakom tačka i zarez, ili složena n ared b a, o d n o sn o g ru p a naredaba u n u -
ta r vitičastih zagrada. Svaki p u t kada se koristi term in naredba, p o d razu m ev a se da ona
m ože biti jednostavna ili složena.
100 Misliti na Javi
Kao p rim e r za if-else, sledi m eto d a test() koja saopštava da li je p retp o stav ljen a vred-
n ost veća, m an ja ili je d n ak a ciljnoj v red n o sti:
//: c o n tr o l/ If E ls e .ja v a
import s t a t ic n e t.m in d view .u ti1 .P r in t
p ub lic c lass If E ls e {
s t a t ic in t re z u lta t = 0;
s t a t ic void t e s t ( in t vrednost, in t c i l j ) {
if(v re d n o s t > c i l j )
re z u lta t = +1;
e lse if(v re d n o s t < c i l j )
re z u lta t = -1;
el se
re z u lta t = 0; // Po k lo p ile su se
}
p ublic s t a t ic void m a in (S trin g [] args) {
te s t(1 0 , 5 );
p ri n t ( r e z u l t a t ) ;
t e s t(5 , 10);
p rin t(re z u lta t);
t e s t(5 , 5 );
pri n t ( r e z u l t a t ) ;
}
} /* Is p is :
1
-1
0
* ///= -
Negde na sredini koda m eto d e test() v idite kom bin aciju else if, što nije nova rezervi-
sana reč, nego jed an else iza kojega je nova n ared b a if.
Iako je Java jezik „slo b o d n o g o blika“, kao C i C + + , uvlaćenje tela n ared b e za k o n tro lu
izvršavanja je korisn o kako bi čitalac m ogao Iakše da o dredi gde o n o počinje i gde se za-
vršava.
Petlje
Petlje se k o n tro lišu rezervisanim rečim a w h ile, d o -w h ile i for, koje se pon ek ad nazivaju i
iteracione narcdbe. Naredba se ponav lja sve d o k logički-izraz za k o n tro lu ne dobije vred-
n o st false. O blik petlje w h ile je sledeći:
Sledi je d n o stav an p rim e r koji generiše slučajne brojeve sve d o k o d red en i uslov ne
b u d e ispunjen.
M eto da uslov() koristi sta tičn u m eto d u random() iz b iblioteke Math, koja generiše
v re d n o st tip a double izm eđ u 0 i 1 (u k lju ču ju či 0, ali ne i 1). Prom enljivoj rezultat vred-
no st daje o p e ra to r p o ređ en ja <, ćiji je rezu ltat tip a boolean. U koliko o d šta m p ate vred-
n o st tip a boolean, au to m atsk i ćete d o b iti odgo v araju ći znakovni niz tru e ili false. Uslovni
izraz za while glasi: „Ponavljaj n ared b e u telu petlje sve d o k m e to d a uslov() vraća true“.
Petlja do-while
O blik petlje do-w hile je:
do
naredba
w h ile ( lo g ić k i- iz r a z ) ;
ledina razlika izm eđu while i do-while jeste ta da se n ared b a u petlji do-while uvek iz-
vrši b arem je d n o m , ćak i ako izraz im a v red n o st false i prvi p u t. Ako je uslovni izraz ne-
tačan pre ulaska u petlju vvhile, n ared b a se neće izvršiti n ijed n o m . U praksi, do-while se
rede javlja od vvhile.
Petlja for
Petlja for je verovatno najčešći oblik ponavljanja (ciklusa). O na vrši inicijalizaciju pre pr-
vog izvršavanja ciklusa. Z atim isp itu je uslov ( logički-izraz) i ako je o n isp u n jen , izvršava
narcdbu, a p o to m na kraju svakog ciklusa izvršava korak i p o n o v o ispituje logički-izraz.
O b lik petlje for je:
fo r ( i n i c i j a l iz a c ij a ; 1o g ič k i- iz ra z ; korak)
naredba
102 Misliti na Javi
Bilo koji o d izraza inicijalizacija, logički-izraz ili korak m ože b iti prazan . Logički izraz
se prov erava p re svakog izvršavanja n aredbe. Č im izraz d obije v red n o st false, petlja se
p rek ida i izvršavanje se nastavlja p rv o m n a red b o m n ak o n petlje for. Na kraju svakog ci-
klusa (posle svakog izvršavanja n a red b e), izvršava se korak.
Petlje for se o b ičn o k oriste za nabrajanje:
* '/ / / :-
Operator zarez
R anije u ovom poglavlju naveo sam da se operator zarez (n e razdelnik zarez koji služi d a
odvoji definicije, kao i arg u m e n te m eto d e) k oristi sam o u k o n tro ln im izrazim a petlje for.
Kako p rilik o m inicijalizacije, tako i p ri izvršavanju koraka k o n tro ln o g izraza, m ožete na-
vesti više n a red ab a razdvojenih zarezim a i o n e će biti izvršene sekvencijalno. P o m o ću
o p e ra to ra zarez m ožete definisati više prom enljiv ih u n u ta r n ared b e for, ali o n e m o ra ju
b iti istog tipa:
// : c o n tro l/ O peratorZarez.java
p u b lic c la s s OperatorZarez {
public s t a t ic void m a in (S trin g [] args) {
f o r ( i n t i = 1, j = i + 10; i < 5;
i++, j = i * 2 ) {
S y s te m .o u t.p rin tln ("i = " + i + " j = " + j ) ;
)
)
} /* Is p is :
i= 1 j= 11
i= 2 j= 4
i= 3 j= 6
i= 4 j= 8
* ///:-
Foreach sintaksa
Java SE5 uvodi novu i sažetiju sintaksu naredbe for koja se u p o trebljava za nizove i kontej-
nere (o kojim a ćete više saznati u poglavljim a N izovi i Detaljno razm atranje kontejnera).
N ju često nazivaju foreach sintaksa, jer u njoj ne m o rate sam i da pravite celobrojnu p ro-
m enljivu za bro jan je prolaza kroz sekvencu stavki - foreach au to m atsk i pravi sve stavke
u m esto vas.
104 Misliti na Javi
Na p rim er, p retp o stav im o da im ate n iz b ro jev a tip a float i d a ho ćete da izaberete svaki
elem ent to g niza:
fo r (f lo a t x : f ) {
Kao što ćete videti u poglavlju Č iivanje objekata, foreach sintaksa ra d i sa svakim ob-
je k to m za koji se zn a p o re d a k n jegovih član o v a (koji p rip a d a klasi Iterable).
M noge for n ared b e o b u h v ataju p ro lazak k ro z sekvencu celo b ro jn ih v re d n o sti, recim o
ovako:
U ovo m slučaju foreach sintaksa n e b i rad ila, sem ukoliko u n a p re d n e n a p ra v ite n iz int
brojeva. D a bih to pojeđn o stav io , u b ib lio tek u net.mindview.util.Range stavio sam m e-
to d u range() koja au to m atsk i g eneriše o d g o v araju ći niz. N a m era m i je bila d a se range()
ko risti kao static uvoz:
Foreach sintaksa štedi nešto v rem en a prilik o m pisan ja koda, ali je važnije to što je takav
k od m nogo čitljiviji - kazuje šta hoćete (d o b iti svaki elem en t n iza), a ne kako to postižete
(„Pravim ovaj indeks d a b ih m ogao izabrati svaki elem en t niza“ )- U ovoj knjizi će se
foreach sintaksa upotrebljavati gde god je to m oguće.
U petlji fo r v red n o st i nikada ne d ođe d o 100 jer n a red b a b re a k p rek in e p etlju kada i
p o sta n e 74. N aredba b re a k se na ovaj način o b ičn o ko risti satno kada nije u n a p re d p o -
znato kada će se stvoriti uslov za izlazak. N aredba c o n tin u e nastavlja izvršavanje o d v rh a
petlje (stoga uvećava i) kad god i nije deljivo sa 9. Kada jeste, ta v re d n o st se ispisuje.
U dru g o j petlji fo r p rik azan o je kako se u p o treb ljav a foreach sin tak sa i vidi se da o n a
daje jed n ak e rezultate.
108 Misliti na Javi
Posleđnji d eo prik azu je b e sk o n ačn u p etlju while, koja b i se, teorijski, izvršavala n ep re-
kidno. M eđ u tim , u n u ta r p etlje se nalazi n are d b a break p o m o ć u koje se izlazi iz petlje. Po-
red toga, p rim etiće te d a n a re d b a continue v raća izvršavanje na v rh petlje i d a ne izvršava
o sta ta k tela petlje n a k o n sebe. (Stoga se ispisivanje u dru go j petlji javlja sam o k ad a je
v re d n o st i deljiva sa 10.) N a izlazu se ispisuje 0 je r je v red n o st izraza 0 % 9 jed n ak a 0.
D rugi oblik besk o n ačn e p etlje je for(;;). P revodilac tu m a č i while(true) i for(;;) n a isti
način, p a je izb o r isključivo p ita n je p ro g ram e rsk o g ukusa.
Vežba 7; (1) P repravite vežbu 1 tak o d a p ro g ra m završava ra d p o m o ć u rezervisane reči
break, k ad a v re d n o st d o đ e d o 99. Pokušajte d a u m esto toga k oristite return.
Čuveni goto
R ezervisana reč goto p o sto ji u p ro g ram sk im jezicim a o d sam og početka. Z aista, goto je
nastao kao posledica k o n tro le p ro g ra m a u m ašin sk o m jeziku: ,A ko je isp u n jen uslov A,
skoči tam o , u su p ro tn o m , skoči o v am o “. K ad p ro čitate asem blerski ko d koji na k raju ge-
neriše p rak tičn o bilo koji p revodilac, p rim etiće te d a se k o n tro la p ro g ram a sastoji o d više
skokova. (P revodilac Jave p rav i sop stveni „asem blerski k o d “, ali njega izvršava v irtueln a
m ašina Jave (JV M ), a n e hard verski procesor.)
N aredba g o to je skok n a niv o u izvornog koda i to ju je iznelo na loš glas. Ako će p ro -
g ram stalno skakati s je d n e tačke n a d ru g u , zar ne po stoji način da se ko d reorganizuje
tako da to k k o n tro le n e b u d e toliko p u n skokova? N aredba g o to je pala u p rav u nem ilost
n ako n objavljivanja čuv en o g članka E dsgera D ijkstre „G oto se sm atra štetnim " i od tada
su n ap ad i na nju postali p o p u la ra n sp o rt.
Kao što je takvoj situaciji svojstveno, srednji p u t je najbolji. Problem nije u korišćenju
n ared b e g o to, već u n jen o m prečesto m ko rišćen ju - u retkim situ acijam a g o to je najbolji
način za s tru k tu rira n je to k a izvršavanja.
Iako je reč g o to rezervisana u Javi, o n a se u jeziku ne koristi; Java nem a n ared b u goto.
Ipak, o na im a nešto što liči na skok, pov ezan o sa rezervisanim rečim a b re a k i c o n tin u e .
To nije skok, već više način da se izađe iz petlje. O n se često povezuje s n a red b o m g o to
zato što koristi isti m eh an izam : o zn ak u (engl. label).
O znaka je id en tifik ato r iza koga sledi dvotačka, na p rim er:
oznakal:
U Javi se oznaka k o risti satno n e p o sred n o p re iteracione naredbe. I to znači baš nep o-
sred n o - ne valja stavljati bilo koje d ru g e n ared b e izm eđu oznake i petlje. O zn aku treba
staviti p re petlje sam o ako u n u ta r te petlje želite da ugnezdite jo š jed n u petlju ili n ared bu
izbora (sw itch ), o kojoj ćete u skoro saznati šta treba. R ezervisane reči b re a k i c o n tin u e
o b ičn o prek id aju sam o tek u ću p etlju , ali kada se k oriste sa oznak om , o n e će p rek in u ti sve
petlje d o nivoa gde se nalazi oznaka:
oznakal:
sp o ljn a - p e tlja {
u n u tra šn ja - p etlja {
/ / •••
Poglavlje 4: Kontrolisanje izvršavanja 109
break; // (1)
/ / •••
continue; // (2)
/ / •••
continue oznakal; // (3)
/ / •••
break oznakal; // (4)
}
}
f o r ( i n t k = 0; k < 5; k++) {
i f ( k == 3) {
p r in t("n a s ta v i u n u tra š n ju ");
continue u nutrasnja;
}
}
}
}
// Ovde ne možete da pozovete
// označene naredbe break i continue
}
}/ * Is p is :
i =0
n astavi unutrašnju
i = 1
nastavi unutrašnju
i =2
nastavi
i =3
preki ni
i =4
nastavi unutrašnju
i = 5
nastavi unutrašnju
i = 6
nastavi unutrašnju
i = 7
nastavi spoljašnju
i =8
p rekini spoljašnju
* ///:-
spoljasnja:
while(true) {
p r i n t ( " S p o lj a š n j a p e t l j a w h ile " ) ;
w h ile (tr u e ) {
i++;
print("i = 11 + i);
if(i “ 1) {
printC'nastavi");
continue;
)
i f (1 == 3) {
print("nastavi spolja šn ju ");
continue spoljasnja;
}
if(i == 5) {
pri nt("preki n i " ) ;
break;
}
if(i == 7) {
p rin t("p re k in i s p o lja š n ju ");
break s p o lja s n ja ;
}
}
}
}
} / * I s p is :
Spoljašnja p e t l j a while
i = 1
nastavi
i = 2
i = 3
nastavi spo lja šnju
Spoljašnja p e t l j a while
i = 4
i = 5
preki ni
Spoljašnja p e t l j a while
i = 6
i = 7
p re k in i spoljašnju
* ///:-
V ažno je da zap am tite kako se o zn ak a u Javi jed in o k o risti kad im ate u g n ežđ en e petlje
i želite da n ap rav ite break ili continue u više nivoa u gnežđivanja.
U svom član k u „ G o to se sm atra štetn im “, D ijkstra je p o se b n o im ao zam e rk i na u p o tre -
b u oznaka, a ne sam e nared b e goto. P rim etio je da broj „b u b ica“ raste s b ro jem oznaka u
p ro g ram u i da oznake i goto otežavaju analizu p ro g ram a. O b ra tite p ažn ju n a to d a oznake
u Javi ne pate o d to g p ro b lem a, p o što je m esto n a k o m e m o g u da stoje o g ran ičen o i ne
m o g u da se koriste za o p šti p ren o s k ontrole. Treba p rim e titi i d a je ovo slučaj kada neka
osobin a jezikai postaje korisnija kada se ograniče njene m o g u čn o sti.
Naredba switch
N ared ba sw itc h se n ek ad naziva i naredba izbora. N ared b a sw itc h b ira iz m e đ u nekoliko
delova koda u zavisnosti o d v re d n o sti d ato g izraza. N jen o p šti ob lik je:
switch (izraz) {
case vrednostl : naredba; break;
case vrednost2 : naredba; break;
case vrednost3 : naredba; break;
case vrednost4 : naredba; break;
case vrednost5 : naredba; break;
/ / ••
default: naredba;
}
Izraz m o ra im ati c elo b ro jn u ili logičku v red n o st. N ared b a switch p o red i rezultat izra-
za i svake vrednosti. Ako p ro n a đ e ekv ivalen tnu v red n o st, o d g o v araju ća naredba (jed n a ili
više njih, vitičaste zagrade nisu p o tre b n e) biće izvršena. A ko ne p ro n a đ e ekvivalentnu
v red n o st, biće izvršena naredba označena kao default.
P rim etićete u p reth o d n o j definiciji da se svaki blok case završava sa break, čim e se to k
izvršavanja p rem ešta na kraj tela nared b e switch. O vo je uob ičajen naćin fo rm iran ja na-
redbe switch, ali je break o pcioni. Ako nedostaje, izvršavaće se sledeće case n aredbe sve
d o k se ne naiđe na break. Iako o b ičn o ne želite da se to dešava, isk u sn o m p ro g ram e ru ovaj
p ristu p m ože biti koristan . O b ratite pažnju na to da p o sled n ja nared b a, posle default,
nem a break jer se izvršavanje nastavlja baš tam o gde bi se io n ak o odvijalo nakon break.
M ožete, bez ikakvih posledica, da stavite break na kraj bloka default ako sm atra te da je to
važno zbog stila.
N aredba switch o m ogućuje čistu realizaciju izbora izm eđu više različitih puteva izvrša-
vanja, ali je za nju p o treb an izraz koji im a v rednost tipova kao što su int ili char. Sa n ared-
b o m switch ne m o žetek ao izrazza izbor koristiti, na p rim er,z n ak o v n i niz ili broj u fo rm atu
po k retnog zareza. Za tipove koji nisu celobrojni ili logički m o ra te da koristite niz naredaba
if. Na kraju sledećeg poglavlja, videćete da to ograničenje olakšava enum, nova rezervisana
reč koja se u Javi koristi od verzije SE5, jer enum lepo radi sa n a re d b o m switch.
Poglavlje 4 : Kontrolisanje izvršavanja 1 13
'/ / / = -
m eto d a Random.nextInt() daje slučajan ceo bro j izm eđ u 0 i 25, koji se d o d aje n u m e rič -
koj (ASCII) vred n o sti slova 'a'. To znači d a se 'a' a u to m atsk i p retv ara u tip int da b i se iz-
vršilo sabiranje.
D a bi se pro m en ljiv a c tip a int štam p ala k ao znak, m o ra b iti vraćen a u tip char ekspli-
c itn o m konverzijom ; inače b i se ispisivali brojevi.
Vežba 8: (2) N apišite n a red b u switch k oja ispisuje p o ru k u za svaki o d slučajeva (case), a
switch stavite u n u ta r petlje for koja isprobava svaki slučaj. Stavite n are d b u break n akon
svakog slučaja i testirajte p ro g ram , a zatim izbacite nared b e break i v idite šta se dešava.
Vežba9: (2) Fibonačijev n iz c ine b rojevi 1 , 1 ,2, 3, 5, 8 ,1 3 , 21, 34 itd., gde je svaki bro j (o d
trećeg nadalje) je d n ak zb iru p re th o d n a dva. N apišite m eto d u koja p rim a ceo bro j k ao ar-
g u m e n t i prikazuje toliko F ibonačijevih brojeva počev o d prvog, n pr. ako p o k ren ete java
Fibonacci 5 (gde je Fibonacci im e klase) izlaz će biti: 1, 1, 2, 3, 5.
Vežba 10: (5) Vampirski broj im a p a ran broj cifara i dobija se m n o ž e n je m p ara brojeva
koji im aju p o pola o rig in aln ih cifara proizvoda. Cifre se uzim aju iz p o lazn o g broja p ro i-
zvoljnim redosledom . N isu dozvoljeni parovi završnih nula. P rim eri:
1260 = 21 * 60
1827 = 21 * 87
2187 = 27 * 81
N apišite p ro g ra m koji pronalazi sve četvorocifrene v am pirske brojeve. (P redložio D an
F orhan.)
Sažetak
O vim poglavljem se završava proučavanje osnovnih osobina koje se javljaju u većini pro-
gram skih jezika: računanje, p rio ritet operatora, eksplicitna konverzija tipova, uslovi i petlje.
Sada ste sprem ni za korake koji će vas približiti svetu objektno orijentisanog pro g ram iran ja.
N aredno poglavlje će ob rad iti važne tem e: inicijalizaciju i čišćenje objekata te veom a b itn o
sakrivanje realizacije.
R ešenja n ek ih vežbi d ata su u e le k tro n sk o m d o k u m e n tu Thc Thinking in Java Annotalecl Solution
Guide, a m o g u se k u p iti na lo k a đ ji www.MindVicw.com.
Inicijalizacija i čišćenje
S napretkom računarske revolucije, ,,nesigurno“ program iranje je postalo jeda n od glavnih
razloga za visoku cenu programiranja.
class Kamen {
Kamen() { / / Ovo j e konstruktor
System.out.print("Kamen");
}
} /* Ispis:
Kamen Kamen Kamen Kamen Kamen Kamen Kamen Kamen Kamen Kamen
* ///:-
new Kamen();
zauzim a se m em o rija i poziva k o n stru k to r. G ara n tu je se da će objek at b iti p rav iln o inici-
jalizovan p re nego što b u d e u p o treb ljen .
O b ra tite p ažn ju n a to d a se stil p isan ja p o k o m e se prv o slovo im en a svih m eto d a piše
m alim slovom ne o d n o si n a k o n stru k to re , je r im e k o n stru k to ra m o ra da se tačno pok lapa
sa im en o m klase.
K o n stru k to r koji ne p rim a a rg u m e n te naziva se po d ra zu m eva n i konstruktor. U većem
d elu literature o Javi koju je izdao Sun, n jih nazivaju „k o n stru k to ri bez arg u m en ata" (engl.
no-arg constructors). T erm in „p o d razu m ev an i k o n stru k to r“ upotrebljava se dugi niz godi-
n a, p a ću ga i ja koristiti. Ali kao i svaka m eto d a , k o n stru k to r m ože da im a arg u m e n te koji
om o g ućav aju d a o d red ite kako će o b jek at biti naprav ljen . P reth o d n i p rim e r lako m ože da
se prep rav i tako da k o n stru k to r im a jed an arg u m en t:
class Kamen2 {
Kamen2(int i ) {
System.out.print(''Kamen " + i + " “ ) ;
}
}
Ako je Drvo(int) jed in i k o n stru k to r, prevodilac neće dozvoliti da n ap rav ite objekat
Drvo ni na koji d ru g i način.
Poglavlje 5: Inicijalizacija i čišćenje 117
K on struk tori elim inišu veliku g ru p u p ro b lem a i čine p ro g ram e razum ljivijim . U pret-
h o d n o m p rim e ru ne postoji eksplicitan poziv nekoj m eto d i inicijalizacija() koja je k o n -
ceptu alno odvojena o d pravljenja objekta. U Javi, pravljenje objekta i njegova inicijalizacija
objed injen i su kon cepti - je d n o bez d ru g o g ne ide.
K o n stru k to r je n eo b ič n a vrsta m eto d e je r n e m a p o v ra tn u v red n o st. O vo je p o tp u n o
različito o d p o v ra tn e v re d n o sti tip a void, kad a m eto d a ne vraća n išta, ali i dalje im ate m o -
g u ćn o st d a je n ap ra v ite tako da v raća nešto. K o n stru k to ri n em aju p o v ra tn u v re d n o st i
d ru g a m o g u ćn o st ne p o sto ji (izraz n e w v raća referencu n o v onapravljenog objekta, ali
sam k o n stru k to r ne vraća n išta). Ako bi p o sto jala p o v ra tn a v red n o st i ako b iste m o g li da
je b irate , prevo d ilac bi nekako m o ra o da zna šta d a rad i s to m p o v ra tn o m v red n o šću .
Vežba 1: (1) N apravite klasu koja sadrži n e in iđ ja liz o v a n u String referencu. Pokažite da
Java tu referencu inicijalizuje v red n o šću null.
Vežba 2: (2) N aprav ite klasu s je d n im String p o ljem koje se inicijalizuje n a m estu defi-
nisanja i d ru g im koje inicijalizuje k o n stru k to r. Po čem u se razlik u ju ta dva pristu p a?
Preklapanje metoda
Jedno od važnih p itan ja u svakom p ro g ram sk o m jeziku jeste u p o tre b a im ena. Praveći
objekat, vi zađajete im e oblasti u m em o riji. M etoda je im e neke akcije. Svim o b jek tim a i
m eto d am a obraćate se prek o im ena. D o b ro o d a b ra n a im en a čine sistem koji je lakše razu -
m eti i m enjati. To veo m a liči na p isanje p ro ze - cilj je da b u d e te u vezi sa čitaocim a.
P rob lem nastaje kada nijanse ljudskog jezika p rim en ju jete n a p ro g ram sk i jezik. Č esto
ista reč im a više različitih značenja - o n a je preklopljena (engl. overloaded). To je korisno,
naro čito ako se radi o je d n o stav n im razlikam a. Vi ćete reći „operi košulju“, „operi kola“ i
„operi psa“. Bilo bi b u d alasto ako biste m o rali da g ovorite „operiV ešM ašinom k ošulju“,
„operiC rev o m kola“ i „operiK upanjem psa“ sam o da bi slušalac m ogao da n ap rav i razliku
izm eđu tih akcija. Većina Ijudskih jezika je re d u n d a n tn a , pa i dalje m ožete da od red ite
značenje čak i ako ispu stite nekoliko reči. Jedinstveni id en titik a to ri n am n isu p o tre b n i
- značenje m o ž e m o shvatiti iz konteksta.
U većini p ro g ram sk ih jezika (p o seb n o C -u ) svaka m eto d a (koje se u tim jezicim a obič-
no nazivaju funkcije) m o ra im ati jed in stv en identifikator. Tako ne m ožete da im ate jed n u
funkciju p o d im en o m p rin t() koja bi ispisivala cele brojeve i d ru g u istog im en a za ispisi-
vanje brojeva u fo rm a tu p o k retn o g zareza - svaka funkcija m o ra da im a jed in stv en o im e.
U Javi (i C + + -u ) p o sto ji jo š je d a n činilac koji n am eće prek lap an je im ena m etoda: kon-
struk to r. Pošto je im e k o n stru k to ra u n a p re d o d re đ e n o im e n o m klase, k o n stru k to r m ože
im ati sa m o to je d n o im e. Ali šta ako želite da objek at p rav ite na više načina? N a p rim er,
p re tp o sta v im o da p rav ite klasu koja m ože da se inicijalizuje na sta n d a rd a n način ili čita-
njem info rm ac ija iz dato tek e. P otreb n a su vam dva k o n stru k to ra , jed an bez arg u m en ata
(p o đ ra zu m ev a n i k o n stru k to r) i d ru g i, ćiji je arg u m e n t tipa String u kom e je im e datoteke
iz koje treb a inicijalizovati objekat. O b a su k o n stru k to ri, pa m o raju im ati isto im e - im e
te klase. Stoga je prcklapanje m etoda suštinski zn ačajn o u Javi, je r o m o g u ćuje da se isto
im e m eto d e koristi s dru g ačijim tip o v im a arg u m e n ata . Iako je prek lap an je m eto d a neop-
h o d n o za k o n stru k to re , to je opšta p o g o d n o st d o stu p n a za bilo koju m etodu.
118 Misliti na Javi
class Drvo {
i n t v is in a ;
Drvo() {
print("S ađ en je m la dic e ");
v is in a = 0;
}
Drvo je visoko 3 m
preklopljena metoda: Drvo je visoko 3 m
Pravimo novo Drvo koje je visoko 4 m
Drvo je visoko 4 m
preklopljena metoda: Drvo je visoko 4 m
Sađenje mladice
* ///:-
O bjekat klase Drvo m o že d a b u d e nap rav ljen ili kao m lađica, b ez arg um enata, ili kao
biljka odgajena u rasad n ik u s d a to m visin om . D a bi se to o m ogućilo, postoje dva k o n -
stru k to ra ; je d an bez arg u m e n a ta i d ru g i, čiji je a rg u m e n t postojeća visina.
M ože vam zatreb ati d a pozovete m e to d u info() n a više načina. N a p rim er, ako želite da
ispišete još n e k u d o d a tn u p o ru k u , o n d a n a re d ite a rg u m e n t tip a String, a ako ne želite ni-
šta više d a kažete, o n d a v am n e tre b a a rg u m e n a t. Bilo bi n eo b ič n o da date dva različita
im ena nečem u što očigled n o im a isti kon cep t. N a sreću, p rek lap an je m eto d a om ogućava
da isto im e koristite za obe stvari.
//: inicijalizacija/PreklapanjeProstihTipova.java
// Proširivanje prostih tipova i preklapanje.
import static net.mind vi ew .u ti l.Print.*;
void f 6 ( f l o a t x) { p r i n t n b ( " f 6 ( f l o a t ) “ ) ; }
void f6(double x) { p r i n t n b ( " f 6 ( d o u b le ) " ) ; }
void f 7 (double x) { p r i n t n b ( " f 7 ( d o u b le ) " ) ; }
void testKonstantom() {
printnb ("5: " ) ;
f 1 ( 5 ) ; f 2 ( 5 ) ; f 3 ( 5 ) ; f 4 ( 5 ) ; f 5 ( 5 ) ; f 6 ( 5 ) ; f 7 (5 ); p r i n t ( ) ;
}
void testChar() {
char x = ' x ' ;
Poglavlje 5: Inicijalizacija i čišćenje 121
printnb("char: ");
fl(x);f2(x);f3 (x);f4(x);f5 (x);f6(x);f7 (x); p r i n t ( ) ;
}
void testByte() {
byte x = 0;
printnb("byte: ");
f l ( x ) ;f2 (x);f3(x);f4(x);f5(x);f6(x);f7 (x); p r i n t ( ) ;
}
void testShort() {
short x = 0;
printnb("short: ");
fl (x);f2(x);f3 (x);f4(x);f5 (x);f6(x);f7 (x); pri n t ();
}
void t e s t l n t O {
int x = 0;
printnbC'int: ");
f l ( x ) ; f 2 ( x ) ; f 3 ( x ) ; f 4 ( x ) ; f 5 ( x ) ; f 6( x ) ; f 7( x ) ; p r i n t ( ) ;
}
void testLong() {
long x = 0;
p r in t n b ( " io n g : " ) ;
fl(x);f2 (x);f3(x);f4(x);f5 (x);f6(x);f7 (x); p r i n t ( ) ;
}
void t e s t F l o a t( ) {
f l o a t x = 0;
p rin tn b ("flo a t: ");
f l ( x ) ; f 2 ( x ) ; f 3 ( x ) ; f 4 ( x ) ; f 5 ( x ) ; f 6 ( x ) ; f 7( x ) ; p r i n t ( ) ;
}
void testDouble() {
double x = 0;
printn b ("d o u b le : " ) ;
f l ( x ) ; f 2 ( x ) ; f 3 ( x ) ; f 4 ( x ) ; f 5( x ) ; f 6 ( x ) ; f 7( x ) ; p r i n t ( ) ;
}
pu blic s t a t i c void m a in(S trin g [] args) {
PreklapanjeProstihTipova p =
new PreklapanjeProstihTip ova();
p.testKonstantom ();
p .te s t C h a r O ;
p.te stB yte ();
p.te s tS h o rtO ;
p .te s tln t();
p .t e s t L o n g ( ) ;
p .te s tF lo a tO ;
p.testDoubleO ;
}
} / * Is p is :
5: f l ( i n t ) f 2( i n t ) f 3( i nt ) f 4 ( i n t ) f5(lo ng ) f 6 ( f 1oat) f7(double)
char: f l ( c h a r ) f 2 ( i nt ) f 3 ( i n t ) f 4 ( i n t ) f5(lo n g ) f 6 ( f l o a t ) f 7 (double)
byte: f 1(byte) f2(byte ) f 3( s h o r t ) f 4 ( i n t ) f 5 ( 1ong) f 6 ( f l o a t ) f7(double)
sho rt: f l ( s h o r t ) f2 (s h o rt) f 3 ( s h o r t ) f 4 ( i n t ) f 5 (lo n g ) f 6 ( f l o a t ) f7(double)
122 Misliti na Javi
P rim etićete d a se k o n sta n ta 5 tre tira kao v red n o st tip a int, pa se, ako postoji, poziva
prek lopljen a m eto d a čiji je a rg u m e n t tip a int. U svim d ru g im slučajevim a tip p o d a tk a se
p ro širu je ako je m an ji o d tip a a rg u m en ta u m eto d i. K od tip a char javlja se nešto drugačiji
efekat - ako se n e n ađ e ta č n o odgo v araju ći p a rn ja k tip a char, o n se p ro širu je n a int.
Šta se dešava ako je a rg u m e n t veći o d a rg u m e n ta koji se očekuje u p reklopljenoj m e-
todi? O dgo v o r daje izm en jen i p re th o d n i p rim er:
void testDouble() {
double x = 0;
print("argum ent t ip a d o u b le ;");
fl(x );f2 ((flo a t)x );f3 ((lo n g )x );f4 ((in t)x );
f5 ((s h o rt)x );f6 ((b y te )x );f7 ((c h a r)x );
}
pu b lic s t a t i c void m a in (S trin g [] args) {
Degradacija p = new Degradacija ( ) ;
p.tes tD o ubleO ;
}
} / * I s p is :
argument t ip a double:
fl( d o u b le )
f2 (flo a t)
f 3(1ong)
f4 (in t)
f5 ( s h o r t )
f 6 ( byte)
f7(c ha r)
* ///:-
U o vom slučaju m etode u zim aju sužene v red n o sti p ro sto g tipa. A ko je vaš a rg u m e n t
širi, m o ra te ga eksplicitno k o nv erto vati u p o treb an tip. U koliko to ne u rad ite, prevodilac
će prijaviti grešku.
void f () { }
i n t f ( ) { return 1; }
f0;
kako izvršno o k ru žen je m ože da o d red i koju m eto d u f() treb a da pozove? I kako bi neko
ko čita p ro g ram to m ogao da zna? Z bog ove vrste p ro b lem a, tip p o v ra tn e v red n o sti ne
m ože da se koristi za razlikovanje m etoda.
124 Misliti na Javi
Podrazumevani konstruktori
Kao što je ranije p o m e n u to , p o d ra z u m e v a n i k o n stru k to r (tj. n o -a rg k o n stru k to r) n em a
arg u m en te i k oristi se za p rav ljen je p o d ra z u m e v a n ih o b jekata. A ko n ap rav ite klasu koja
nem a k o n stru k to re, p rev o d ilac će u m esto vas a u to m a tsk i n a p ra v iti p o d ra z u m ev a n i k o n -
struk to r. Na p rim er:
class Ptica {}
Izraz
pravi nov objekat i poziva p o d raz u m ev an i k o n stru k to r, iako o n ranije nije izričito nave-
den. Bez njega ne bi p ostojala n ijed n a m eto d a koju b ism o m ogli da p o zovem o kako bism o
napravili objekat. M eđ u tim , ako definišete m ak ar jed a n k o n stru k to r te klase (sa arg u m en -
tim a ili bez n jih), prevodilac neće um esto vas n ap rav iti p o d razu m ev an i k o n stru k to r:
class Ptica2 {
Ptica2 ( i n t i ) { }
Ptica2 (double d) { }
}
p u blic class NemaSinteze {
pu blic s t a t i c void m a in (S tr in g [] args) {
/ / ! Ptica2 p = new P t ic a 2 ( ) ; / / Nema podrazumevanog konstruktora
Ptica2 p2 = new P t i c a 2 ( l ) ;
Ptica2 p3 = new P tic a 2 ( 1 . 0 );
}
1 III--
Ako sada napišete:
new P t ic a 2 ( ) ;
B a n a n a . o lju s t i( a , l ) ;
B a n a n a . o lju s t i( b , 2 ) ;
O vo su saino in tern i oblici i ako ih napišete, p revodilac ih ne prih v ata, ali vam daje ide-
ju o to m e šta se dešava.
P retp ostavim o da se nalazite u n u ta r m eto d e i da želite da koristite referencu na tekući
objekat. Pošto je tu referencu prevodilac skriveno prosledio, za nju ne postoji identifikator.
Za tu n a m en u , m eđ u tim , postoji rezervisana reč: this. R ezervisana reč this - koja m ože da
se koristi sam o u n u ta r ne-static m eto d e - vraća referencu na objekat za koji je m etoda po-
zvana. Tu referencu m ožete da koristite kao i svaku d ru g u referencu na objekat. Vodite
126 Misliti na Javi
//: i n i c i j a l i z a c i j a / K a j s i ja . j a v a
pu blic class K a js ija {
void o t r e b i ( ) { / * . . . * / }
void k o s tic a () { o t r e b i ( ) ; / * . . . * / }
} ///:-
U n u tar m eto d e kostica() tnogli biste d a n ap išete this.otrebi(), ali to nije p o tre b n o .1
Prevodilac to au to m atsk i radi u m e sto vas. R ezervisana reč this k o risti se sam o kada treb a
da izričito k o ristite referencu n a tek u ći objekat. Na p rim e r, o n a se često k o risti uz n a red b e
return kada treba da v ratite referencu n a tekući objekat:
public class L is ta {
i n t i = 0;
Lista p r e l i s t a j O {
i++;
return t h i s ;
}
void i s p i s ( ) {
S y s te m . o u t. p r in t ln (" i = " + i ) ;
}
pu blic s t a t i c void m a in (S trin g [] args) {
L ista x = new L i s t a ( ) ;
x . p r e l i s t a j ( ) . p r e l i s t a j ( ) . p r e l i s t a j ( ) . i spi s ( ) ;
}
} / * Is p is :
i = 3
* ///:-
Pošto m etoda p re lista j() vraća referencu n a tekući o b jek at preko rezervisane reči th is,
lako se m ože izvršiti više o p eracija n ad istim o b jek to m .
th is se koristi i za prosleđivanje tekućeg objekta d ru g o j m etodi:
class Osoba {
pu blic void pojedi(Jabuka jabuka) {
Ima Ijudi koji kao opsednuti pišu this ispred svakog poziva m eto d e i svake reference polja, obra-
zlažući da je takav ko d „jasniji i eksplicitniji“. Ne činite to. Z na se zbog čega koristim o jezike visokog
nivoa: oni m nogo toga rade um esto nas. Ako budete pisali this kada nije n eo p b o d n o , zbuničete i
naljutiti sve koji če čitati vaš program , pošto u svem ostalom kodu koji su pročitali this nije bio po-
sejan posvuda. Ljudi očekuju da se this upotrebljava sam o o n d a kada je n eo p h o d an . Pridržavajući se
doslednog i jednostavnog stila program iranje štedite vrem e i novac.
Poglavlje 5: Inicijalizacija i čišćenje 127
Jabuka o lju s te na = j a b u k a . o l j u s t i S e ( ) ;
S y s t e m . o u t . p r i n t ln ( " M lja c " ) ;
}
}
class lj u s t a c {
s t a t i c Jabuka 1ju s ti(J a b u k a jabuka) {
/ / . . . ukloni Ijusku
re tu rn jabuka; / / Oljustena
}
}
class Jabuka {
Jabuka o l j u s t i S e ( ) { return 1j u s t a c . l j u s t i ( t h i s ) ; }
}
Jabuka zove Ijustac.ljusti(), sp o ljn u m e to d u koja obavlja o p eraciju koja, iz nekog raz-
loga, m o ra biti spo ljna za objekat Jabuka (m o žd a se sp o ljn a m eto d a m ože p rim e n iti na
više različitih klasa, a vi ne želite da p o navljate n jen ko d ). D a bi se p rosledila spoljnoj m e-
todi, o n a m o ra da u p o treb i this.
Vežba 8: (1) N apravite Idasu s dve m eto d e. U n u ta r prve m eto d e, d v ap u t pozovite d ru g u :
prvi p u t bez this, a d ru g i p u t p o m o ć u this - tek da biste videli kako radi; tc ne bi treb alo
da rad ite u praksi.
To je m oguće sam o kada toj statičnoj m etodi prosledite referencu na objekat (i statična m etoda m ože
praviti sopstvene objekte). Tada preko te reference (koja je u to m slučaju igra ulogu refe ren ce th is)
m ožete da pozivate nestatične m etode i da pristupate nestatičnim poljim a. Ipak, kada želite nešto
tako da radite, napravite sam o običnu nestatičnn m etodu.
130 Misliti na Javi
p rim er, p retp o stav im o da se to k o m stv aran ja vaš objekat iscrta n a ekranu. Ako eksplicit-
n o ne ob rišete njegovu sliku sa ek ran a, m o žd a o n a n ikad neće biti u klonjena. U koliko
m e to d u fin alize() p ro g ra m ira te za n ek u v rs tu b risan ja, a o b jek at do sp e p o d dejstvo saku-
pljača sm eća i fin alize() b u d e p o zv an a (a niko ne ja m č i d a će se to desiti), u prvoj fazi čiš-
ćenja slika će b iti u k lo n jen a sa ek ran a. U s u p ro tn o m , ako sakupljač sm eća ne reši da
počisti vaš objekat, slika o staje na ekranu.
M ože se d o g o d iti d a p ro s to r koji zau zim a vaš objekat n ik ad ne b u d e o slo b o đ en , jer
vašem p ro g ra m u n ik a d a neće p o n e stati m em o rije. A ko se p ro g ra m završi, a sakupljač
sm eća nije im ao p rilik u d a o slo b o d i p ro s to r koji je bilo koji vaš objekat zauzim ao, celo-
k u p an p ro sto r će b iti odjednom v raćen o p e ra tiv n o m sistem u na izlasku iz p ro g ram a. Tako
se izbegava d o d a tn a u p o tre b a raču n a rsk ih resursa - ako čišćenje nije p o tre b n o p a se p o -
boljšavaju p erfo rm an se p ro g ram a.
Jo sh u a B loch ide još dalje u odeljku pod naslovom „Izbegavajte korišćenje završnih m etoda": „Fina-
lizatori su nepredvidljivi, često o p a s n i, i po pravilu nepotrebni." Efiknsno programiranje na lavi, s. 18
(M ikro knjiga, 2004).
Poglavlje 5: Inicijalizacija i čišćenje 131
Stanje okončanja
U glavnom ne m ožete da se oslonite na to da će finalize() biti pozvana, već m o ra te da na-
p rav ite zasebne funkcije za čišćenje i da ih eksplicitno pozovete. Z ato se čini da je finali-
ze() je d in o korisna za o slo b ađ an je m em o rije rezervisane n a neu ob ičajen način, koju
većina p ro g ra m e ra nikada neće koristiti. Postoji, m eđ u tim , je d n a vrlo k o risna p rim e n a
m eto d e finalizef) koja se ne oslanja na to da će ona biti svaki p u t pozvana. To je provera
p o sto jan ja stanja okončanja' (engl. ten n in a tio n condition) nekog objekta.
O d tre n u tk a kada vas objekat više ne zan im a - kada je sp rem an da b u d e počišćen - taj
o bjekat treba da b u d e u stan ju u kom e njegova m em o rija m ože b ezb ed no da b u de oslo-
b o đ en a. Na p rim er, ako objekat predstavlja o tv o ren u d a to tek u , pro g ra m e r treb a da zatvo-
ri tu d a to tek u pre nego što sakupljač sm eća počisti taj objekat. A ko bilo koji deo objekta
nije p ra v iln o počišćen, im aćete grešku u p ro g ram u koju je vrlo teško o tk riti. M etoda fi-
n a lize() je značajna zato što m ože da se iskoristi za otk riv an je tak vih slučajeva i ako se ne
poziva uvek. Ako neka finalizacija o tk rije grešku, otkrili ste p ro b lem , što vas, uostalom ,
jedino i brin e.
' Izraz koji je skovao Bill V enners (w w w.artima.com) tokom našeg sem inara.
132 Misliti na Javi
/ / : in ic ij a li z a c ija / S t a n je O k o n c a n ja . ja v a
/ / O tkrivanje objekta k o ji n i j e ispravno počišćen
/ / pomoću metode f i n a l i z e ( ) .
class Knjiga {
boolean pozajmljena = f a ls e ;
Knjiga(boolean p2) {
pozajmljena = p2;
}
void v r a t i ( ) {
pozajmljena = f a ls e ;
}
protected void f i n a l i z e ( ) {
i f (pozajml jena)
System .out.println (''Greška: k njig a n i j e vraćena");
/ / Normalno b is te u r a d i l i i sledeće:
/ / s u p e r . f i n a l i z e O ; / / Poziv v e r z i j e osnovne klase
}
}
Sve pozajm ljene knjige u stan ju o k o n ćan ja treb alo bi da b u d u v raćen e pre nego što ili
pok u pi sakupljač sm eća, ali se do g o d i da su m eto d i m a in () p ro g ra m e r greškom ne vrati
jednu o d knjiga. Bez m eto d e fin alize() koja proverava stanje o k o n ćan ja, to bi predstavlja-
lo grešku koju je p o ten cijaln o teško o tkriti.
O b ra tite p ažn ju na to da je za zahtevanje čišćenja korišćen p oziv S ystem .gc(). Ć ak i da
nije, vrlo je verovatno da bi zalutala K n jiga je d n o m bila o tk riv en a u p o n o v ljen im izvrša-
v an jim a p ro g ram a (p o d p retp o stav k o m da p ro g ra m zau zim a d o v o ljn o m em o rije kako bi
bio p o k re n u t sakupljač sm eća).
Po prav ilu , treb alo bi da p retp o stav ite kako i verzija fin alize() u osnovnoj klasi irna
važnog posla i da je pozovete p o m o ć u rezervisane reči su p e r, kao što vidite u K njiga.fi-
nalize(). U ovom slučaju, taj red je pretvoren u k o m e n ta r zato što zahteva o b ra d u izuze-
taka, a m i ih još n ism o razm o trili.
Poglavlje 5: Inicijalizacija i čišćenje 1 33
Vežba 10: (2) N apravite klasu s m e to d o m finalize() koja ispisuje n eku p o ru k u . U m etod i
main() n apravite objekat te klase. O b jasn ite p o n aša n je svog p ro g ra m a.
Vežba 11: (4) Prepravite p re th o d n u vežbu tak o d a se vaša m e to d a finalize() uvek poziva.
Vežba 12: (4) N aprav ite klasu Rezervoar koja se m o že p u n iti i p raz n iti, i koja im a takvo
stanje okončanja da m o ra b iti p ra z n a p rilik o m čišćenja objekta. N apišite m e to d u finali-
ze() koja proverava ispun jen je tog uslova o k o n čan ja . U m eto d i main() ispitajte scenarije
koji se m o g u o d ig rati p rilik o m u p o tre b e klase Rezervoar.
kada se završi p o stu p ak označavanja. T okom čiščenja o slo b ađa se m em o rija koju su zau-
zim ali m rtv i objekti. M eđ u tim , n išta se ne k o p ira, p a ako sakupljač odluči d a sabije frag-
m e n tira n i d in am ički m em orijski prostor, o n to rad i tako što p o m e ra objekte.
Tehnika stani-i-kop iraj počiva na ideji da ovaj tip saku pljan ja sm eća ne rad i u p o zad i-
ni, već se p ro g ram zaustavlja d o k se obavlja sak u p ljan je sm eća. U literatu ri koj u je objavio
Sun p ro n aći ćete više referenci na sakupljač sm eća kao p o zad in sk i proces niskog p rio ri-
teta, ali se ispostavilo da u ran ijim verzijam a S unove JVM saku pljanje sm eća nije realizo-
vano n a taj način. U m esto toga, S unov sakupljač sm eća je zaustavljao p ro g ra m k ad a
p o n estan e m em o rije. I teh n ik a o zn ači-i—p o čisti zahteva zaustavljanje p ro g ram a.
Kao što je ranije p o m e n u to , o p isan a v irtu eln a m ašin a zau zim a m e m o riju u velikim
b lokovim a. A ko napravite veliki objekat, o n d o b ija so pstveni blok. S trik tn a teh n ik a stani-
i-kopiraj zahteva kop iran je svih živih objek ata sa izvorišnog d in am ičk o g m em orijsk og
p ro sto ra u novi p re nego što se stari oslobodi, za šta je n e o p h o d n o p u n o m em orije. Sa-
kupljač sm eća to k o m sakupljanja o bično m ože d a k o p ira objekte u m rtve blokove. Svaki
blo k im a brojačproizvodnje koji vodi evidenciju d a li je b lo k živ. O b ič n o se sabijanje vrši
sam o n ad b lokovim a koji su n apravljeni posle posled n jeg sak up ljan ja sm eća; svim osta-
lim blok ov im a b rojač p roizvodnje se uvećava ako su o d n e k u d referencirani. N a ovaj n a-
čin se b rin e o uobičajenim slučajevim a p riv re m e n ih o b jekata k ratk o g životnog veka.
P erio dičn o se radi p o tp u n o čišćenje - veliki o bjekti se i dalje ne k o p iraju (već im se sam o
uveća brojač proizvodnje), a blokovi koji sadrže m ale o b jek te k o p iraju se i slažu. JVM
p rati efikasnost sakupljača sm eća i ako se za njegovo korišćenje n e o p h o d n o troši vrem e
jer su svi objekti dugog životnog veka, preb acuje ga u režim o zn ači-i-p očisti. Slično tom e,
JVM vodi raču n a o u spešnosti tehnike o zn ači-i-po čisti, i ako d in am ič k i m em o rijsk i p ro -
sto r p o stan e fragm en tiran , prebacuje se nazad u režim stan i-i-k o p iraj. P reth o d n a m eto d a
prebacivanja predstavlja p o m e n u ti prilagodljivi deo, tj. p o sto ji teh n ik a prilagodljiva sta-
ni-i-k o p iraj označi-i-počisti.
Postoji više d o d atn ih m o g u ćn o sti za ub rzav an je v irtu e ln e m ašine. Posebno važna m o -
gućnost o đn o si se na rad p ro g ram a za učitavanje (engl. loader) i o n og a što se naziva p re-
vodilac b aš-k ad a-treb a (engl. Just-In-Time, JIT). JIT p revodilac delim ičn o ili p o tp u n o
pretvara p ro g ram u lokalni m ašinski kod, tako da JVM ne m o ra da ga in terp re tira, pa je
izvršavanje m n o g o brže. Kada klasa m o ra da se u čita (o b ično , prvi p u t kada želite da na-
pravite objekat te klase), pronalazi se d atoteka .class i b ajtk od te klase se učitava u m em o -
riju. U to m tre n u tk u jedan p ristu p m ože biti da p rim e n im o JIT p rev ođ enje na sav k od , ali
to im a dve m ane. Prvo, nešto više traje (što, kada se n ak u p i kroz životni vek p ro g ram a , ne
m ože da b u d e zanem arljivo). D rugo, povećava se izvršni p ro g ram (bajtk od je z n a tn o
k o m p ak tniji od raspakovanog JIT koda), pa m o g u d a se izm en e m em o rijsk e stranice, što
defin itiv n o u sporava program . A lternativni p ristu p je lenja p ro cen a (engl. lazyevaluation),
što znači da se II I prevođenje ne radi sve d o k nije n e o p h o d n o . T im e se postiže da kod koji
se nikad a ne izvrši, m ožda nikada ne b ud e JIT p reveden. T ehnologije Java H o tS p o t u n o-
vijim razvojnim o k ru žen jim a (JDK) p o stu p aju slično, jer o p tim iz u ju svaki deo koda sva-
ki p u t kada se on izvršava - što se kod više izvršava, p o staje sve brži.
136 Misliti na Javi
Inicijalizacija članica
fava se baš tru d i d a g a ra n tu je k ako će sve p ro m en ljiv e biti p ra v iln o in iđ jalizo v an e p re ko-
rišćenja. Kad su p ro m en ljiv e definisane lo kaln o u m eto d i, ovo g a ra n tu je prevodilac (jer
inače prijavljuje grešk u ). Z nači, ako napišete:
void f() {
int i ;
i++; // Greška -- i nije inicijalizovana
/ / : i n i c i j a l i z a c i j a / I n i c i j a l n e V r e d n o s t i . java
/ / Prikazuje podrazumevane i n i c i j a l n e vred no sti.
import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ;
p u b lic class I n ic ija ln e V r e d n o s t i {
boolean t ;
char c;
byte b;
short s ;
i nt i ;
1ong 1;
flo a t f ;
double d;
I n i cija ln e V re d n o sti reference ;
void i s p i s l n i c i j a l n e V r e d n o s t i () {
p r i n t ( " T i p podatka I n i c i j a l n a vrednost");
p r i n t ( “ boolean " + t);
print("char [" + c + "] " ) ;
prin t("b y te " + b);
print("short " + s)
print("int " + i)
pri nt("1ong " + 1)
p rin t("flo a t ■ + f)
print("double " + d);
printC'reference " + reference);
iv.ispislnicijalneVrednosti();
/* U ovom slučaju takođe možete napisati:
new InicijalneVrednosti().ispislnicijalneVrednosti();
*/
}
} / * I s p is :
Tip podatka I n i c i j a l n a vrednost
boolean fa ls e
char [ 1
byte 0
short 0
in t 0
long 0
flo a t 0.0
double 0.0
reference null
* I I I,-
Zadavanje inicijalizacije
Šta se dešava ako želite da p rom enljivoj d odelite p o č e tn u vrednost? D irek tan n ačin d a to
urad ite jeste da joj d o d elite v red n o st na m estu gde tu p rom enljivu definišete u klasi. (To
nije m o guće u C + + -u , iako početn ici u jeziku C + + to uvek pokušavaju.) U sledećem p ri-
m eru definicije polja u klasi In ic ija ln e V r e d n o s t i p ro m en jen e su da bi obezbedile p o čet-
ne v red n osti:
/ / : i n i c i j a l i z a c i j a / I n ic ija ln e V r e d n o s t i 2 . ja v a
/ / E k s p lic itn o zadavanje i n i c i j a l n i h v r e d n o s ti.
pu b lic c la s s Mera {
Dubina d = new Dubina ( ) ;
/ / •••
1 ///:-
Ako prom enljivoj d n iste dali p o č e tn u v red n o st, a ipak p ok u šate da je koristite. d o-
bićete grešku p ri izvršavanju koja se u Javi naziva izuzetak (engl. exception), što je o b ra-
đ eno u poglavlju O brada grešaka potnoću izuzetaka.
M ožete čak d a pozovete m e to d u d a b iste obezb edili v re d n o st za inicijalizaciju:
/ / : i n i c i j a l iz a c ij a / I n i c i j a l iz a c ijaMetodom2.java
p u blic c la s s In icijalizacijaM eto d o m 2 {
in t i = f ();
in t j = g ( i ) ;
in t f ( ) { re tu rn 11; }
irit g (in t n) { re tu rn n * 10; }
} III--
Ali ovo ne m ožete d a urad ite:
Inic/jalizacija konstruktorima
K o n stru k to r m ože d a se k oristi za inicijalizaciju. To daje veću fleksibilnost u p ro g ra m ira -
n ju , je r m o žete da pozivate m e to d e i izvršavate akcije to k o m rad a p ro g ra m a kako biste
od red ili p o četn e v red n o sti. Jeđnu stvar treb a im ati na u m u : tim e ne sprečavate a u to m a t-
sku inicijalizaciju koja se odigrava p re nego što se u đe u k o n stru k to r. Stoga, ako biste, na
p rim er, napisali:
Redosled inicijalizacije
Redosled inicijalizacije u n u ta r klase o d re đ en je red o sled o m kojim su p rom enljive defini-
sane. D efinicije pro m en ljiv ih m ogu biti razbacane u n u ta r i izm eđ u definicija m eto d a, ali
se p rom enljive inicijalizuju pre poziva bilo koje m eto d e - čak i k o n stru k to ra . Na p rim er:
class Kuca {
Prozor pl = new P r o z o r ( l) ; / / Pre konstruktora
Kuca() {
/ / Ukazuje da smo unutar konstruktora
p r i n t ( 'KucaO" ) ;
p3 = new Prozor(33); / / Ponovo i n i c i j a l i z u j e p3
}
Prozor p2 = new Prozor(2); / / Nakon konstruktora
void f () { p r i n t ( " f ( ) " ) ; }
Prozor p3 = new Prozor(3); / / Na kraju
140 Misliti na Javi
class C in ija {
C i n i j a ( i n t marker) {
p r i n t ( " C i n i j a ( " + marker + " ) " ) ;
}
void f l ( i n t marker) {
p r i n t ( " f l ( " + marker + " ) " ) ;
}
}
Poglavlje 5: Inicijalizacija i čišćenje 141
class Sto {
s t a t i c C i n ij a c i n i j a l = new C i n i j a ( l ) ;
S to() {
p rin t("S to ()” ) ;
c in ija 2 .f(l);
}
void f 2 ( i n t marker) {
p r i n t ( " f 2 ( " + marker + " ) " ) ;
}
s t a t i c C i n ij a c in i j a 2 = new C i n i j a (2 );
class Kredenac {
C i n ij a c in i j a 3 = new C i n i j a ( 3 ) ;
s t a t i c C i n ij a c i n i j a 4 = new C i n i j a ( 4 ) ;
Kredenac() {
p r i n t (" K r e d e n a c ( )" );
c in ija 4 .fl(2 );
}
void f 3 ( i n t marker) {
p r i n t ( " f 3 ( " + marker + " ) " ) ;
}
s t a t i c C in ija ci n i j a 5 = new C i n i j a ( 5 ) ;
}
pu b lic class S t a t i c k a l n i c i j a l i z a c i j a {
pu b lic s t a t i c void m a in (S trin g [] args) {
p r i n t ( " P r a v l je n je novog objekta klase Kredenac() u metodi main");
new KredenacO;
p r i n t ( " P ra vlje n je novog objekta klase Kredenac() u metodi main " ) ;
new Kredenac( ) ;
s t o . f 2 ( 1) ;
k r e d e n a c . f3 ( l) ;
}
s t a t i c Sto sto = new S to () ;
s t a t i c Kredenac kredenac = new Kredenac();
} / * I s p is :
C in ija (l)
Cini j a (2)
Sto()
fl(l)
Ci ni j a(4)
C in ija ( 5 )
Cini ja (3)
Kredenac()
f 1 (2)
P r a v lje n je novog objekta klase Kredenac() u metodi main
C in ija ( 3 )
KredenacO
142 Misliti na Javi
fl(2 )
P ra v lje n je novog objekta klase Kredenac() u metodi main
C in ija ( 3 )
KredenacO
f 1 (2)
f 2 (1)
f 3 (1)
* ///:-
Klasa Cinija om ogućava da p ra tite pravljenje objekata, a klase Sto i Kredenac na više
m esta u n u ta r definicija im aju statičn e članove klase Cinija. O b ra tite p a ž n ju na to da Kre-
denac pre statičnih definicija p rav i nestatičan objekat Cinija cinija3.
Iz rezu ltata p re th o d n o g p ro g ra m a v idite d a se inicijalizacija statičn ih elem en ata javlja
sam o ako je n e o p h o d n a. A ko n e n a p rav ite objekat klase Sto i ako se n ik ad a ne o b ratite
m e to d a m a Sto.cinijal ili Sto.cinija2, statičn i objekti Cinija cinijal i cinija2 nik ad a neće
b iti n apravljeni. O n i se inicijalizuju sam o kada se p rav i prvi o bjek at klase Sto (ili kada se
p rv i p u t p ristu p a statičn o m p o d a tk u ). N akon toga, više se ne obavlja p o n o v n a inicijali-
zacija statičn ih objekata.
Prvo se inicijalizuju statičn i elem en ti, ako to već nije u čin jen o , a zatim nestatičn i. D o-
kaz za to vidite u rezu ltatu p re th o d n o g p ro g ram a . D a b i se izvršila statičn a m e to d a m ain(
), m o ra b iti učitan a klasa Staticlnitialization, a zatim se inicijalizuju n jen a statičn a polja
sto i kredenac, što p ro u zro k u je učitavanje tih klasa; p o što o b e te klase sadrže statičn e o b -
jekte Cinija, zatim se učitava Cinija. Tako se sve klase ovog p ro g ra m a učitavaju p re počet-
ka izvršavanja m etode main( ). To najčešće nije slučaj, p o što u tip ič n o m p ro g ram u sve
neće b iti povezano statičn im e lem en tim a kao u ovom p rim eru .
Da b ism o napravili sažetak procesa pravljenja objekta, p o sm a tra jm o klasu p o d im e-
n o m Pas:
1. Iako se u njoj rezervisana reć sta tic ne upotrebljava eksplicitno, k o n stru k to r jeste
statičn a m eto d a. Stoga, p ri p rv o m pravljenju objekta tip a Pas ili kada se prvi p u t
p ristu p a statičkoj m eto d i ili statičk o m polju klase Pas, Javin in te rp re te r m o ra da
p ro n a đ e d ato tek u Pas.class, što čini pretraživanjem p u ta n je klasa (engl. classpath).
2 . Pri učitavanju P as.class (pri čem u se pravi objekat klase C lass, o čem u će kasnije
biti reči) pokreće se inicijalizacija svih statičn ih elem enata. Z naći, o n i se inicijalizu-
ju sam o jed an p u t, kada se odgovarajući objekat klase C lass učitava p o prvi put.
3. Kada napravite objekat o p eracijo m n ew Pas(), proces kon stru k cije objekta nalaže
da se prvo zauzm e do v o ljn o skladišnog p ro sto ra u d in am ič k o m m em o rijsk o m
p ro sto ru .
4 . Taj p ro sto r se p o p u n i n u lam a, čim e se au to m atsk i zadaje p o d ra zu m e v a n a v red n o st
svih pro m en ljiv ih pro sto g tipa u to m o bjektu Pas (n u la za brojeve i njen ekvivalent
za b o o le a n i c h a r), a sve reference d obijaju vređ n o st nu ll.
5. Izvršavaju se sve inicijalizacije koje se javljaju na m estu definicije polja.
6. Izvršavaju se k o n stru k to ri. Kao što ćete videti u poglavlju Ponovno korišćenjc klasa,
to m ože da b u d e veliki posao, n aro čito p ri nasledivanju.
Poglavlje 5: Inidjalizacija i čišćenje 143
O vo podseća na m eto d u , ali je to sam o rezervisana reč sta tic posle koje sledi b lo k koda.
Ovaj k o d se, kao i d ru g e inicijalizacije sta tič n ih elem enata, izvršava sam o je d n o m , kada
prvi p u t p rav ite objekat te klase ili kada p rv i p u t p ristu p a te statič n o m član u klase (čak i
ako n ik ad a n e n ap rav ite o b jek at te klase). N a p rim e r:
class S o l jic e {
s t a t i c S o ljic a s o l j i c a l ;
s t a t i c S o ljic a s o ljic a 2 ;
s ta tic {
s o l j i c a l = new S o l j i c a ( l ) ;
s o l jic a 2 = new S o l jic a ( 2 ) ;
}
S o l jic e ( ) {
p rin t("S o lj i c e ( ) " ) ;
} / * I s p is :
Unutar metode main()
S o ljic a (l)
S o ljic a ( 2 )
f (99)
* ///:-
Statični elem en ti klase S oljice in icijalizu ju se bilo da se sta tič n o m o b jek tu s o ljic a l p ri-
stu p a u red u o zn a čen o m sa (1), b ilo d a je red (1) k o m en ta risan a iz redova o zn ačen ih sa
(2) uk lo n jen k o m en tar. A ko su i (1) i (2) k o m en tarisan i, statičn i elem en ti neće b iti inici-
jalizovani, kao što v id ite iz rezu ltata p ro g ra m a . Takođe nije b itn o da li je k o m e n ta r uklo-
njen isp red sam o je d n o g red a ili o b a re d a o zn ačen a sa (2); statičn i elem en ati če se
inicijalizovati sam o je d n o m .
V ežba 13: (1) P roverite n are d b e iz p re th o d n o g pasusa.
V ežba 14: (1) N ap rav ite klasu s je d n im sta tičn im p o ljem tip a S trin g koje se inicijalizuje
n a m e stu definisanja i d ru g im p o lje m koje in iđ ja liz u je statičan blok. D ođajte statičn u
m e to d u koja ispisuje o b a p o lja i p o k azu je d a se o b a inicijalizuju p re u p o treb e.
// : inicijalizacija/Solje.java
/ / Javina " I n i c i j a l i z a c i j a i n s t a n c i. "
i mp o r t s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ;
class Solja {
S o l j a ( i n t marker) {
p r i n t ( " S o l j a ( " + marker + " ) " ) ;
}
void f ( i n t marker) {
p r i n t ( " f ( " + marker + " ) " ) ;
}
}
{
s o l j a l = new S o l j a (1);
so lja 2 = new Solj a(2 );
p r i n t ( " s o l j a l i solja 2 i n i c i j a l i z o v a n i " ) ;
}
izgleda gotovo istovetno kao o d red b a za inicijalizaciju statičn ih elem enata, sam o što ne-
dostaje rezervisana reč static. O va sin tak sa je n e o p h o d n a da bi se obezbedila p o d ršk a za
inicijalizaciju anortim nih unutrašnjih klasa (poglavlje U nutrašnje klase), ali o m o g u ću je i
da jam čite kako če se o d ređ en e op eracije izvršiti bez o bzira na to koji će eksplicitni kon-
s tru k to r biti pozvan. Iz rezultata p ro g ram a v id ite da se o d re d b a inicijalizacije instanci
izvršava p re svih k o n stru k to ra.
Vežba 15: (1) N aprav ite klasu s je d n im p o ljem tip a String koje se inicijalizuje p o m o ć u
inicijalizacije instanci.
Inicijalizacija nizova
Niz je sekvenca objek ata ili elem en ata pro sto g tip a koji su jed n o g istog tip a i upak o v an i
zajed no p o d jed n im im e n o m za identifikaciju. N izovi se definišu i koriste p o m o ć u ope-
ratora indeksiranja [ ]. R eferencu niza definišete tako što iza im ena tip a stavite prazn e
uglaste zagrade:
i n t [ ] a l;
146 Misliti na Javi
Ako uglaste zagrade stavite posle id en tifik ato ra, značenje će b iti p o tp u n o isto.
in t a l [ ] ;
int [] al = { 1, 2, 3, 4, 5 };
in t [] a2;
a2 = a l;
/ / : in i c i j a l i z a c i j a / N i z o v i P r o s t i h T i p o v a . j a v a
import s t a t i c n e t .m in dvi e w . u t i 1 . P r in t
/ / : in i c i j a l i z a c i j a / N i z o v i I N e w . j a v a
/ / P ra v lje n je nizova operatorom new.
import j a v a . u t i 1 . * ;
import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ;
Pošto se velićina niza bira p seu d o slu čajn o (m e to d o m R a n d o m .n e x tIn t(), čiji je re zu k
tat slučajan broj izm eđ u nule i n jen o g arg u m e n ta ), jasn o je da se niz zaista pravi za v rem e
izvršavanja. Pored toga, iz rezultata ovog p ro g ram a v id ite da elem en ti nizova p ro stih ti-
pova au to m atsk i d o b ijaju p o d raz u m ev an e v red n o sti. (Za n u m eričk e i p rom enljive tipa
ch a r to je nula, a za b o o le a n to je false.)
M etod a A rra y s.to S trin g (), koja p rip ad a sta n d a rd n o j Javinoj biblioteci ja v a .u til, daje
verziju je d n o d im e n z io n a ln o g niza koja se m ože ispisati.
i n t [] a = new i n t [ s l u c a j a n . n e x t l n t ( 2 0 ) ] ;
/ / : in ic ij a l i z a c i j a / N i z O b j e k a t a . j a v a
/ / P ra v lje n je niza č i j i elementi nisu prostog t i p a .
import j a v a . u t i l . * ;
import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ;
Integ er [] a = new I n t e g e r [ s lu c a ja n . n e x t l n t ( 2 0 ) ] ;
Ako to k o m izvršavanja zab o rav ite da n ap rav ite objekat, javiće se izuzetak kada po k u -
šate da u p o tre b ite p ra z n u lokaciju u nizu.
Niz o b jekata tak o đ e m o žete da inicijalizujete koristeći listu izm eđu vitičastih zagrada.
Za to p o sto je dva oblika:
p u b lic class I n i c i j a l i z a c i j a N i z a {
p u b lic s t a t i c void m a in ( S tr in g [] args) {
Integerf ] a = {
new I n t e g e r ( l ) ,
Poglavlje 5: Inicijalizaaja i čišćenje 149
new I n t e g e r ( 2 ) ,
3, / / Autopakovanje
);
In te g e r[ ] b = new Integer [ ] {
new I n t e g e r ( l ) ,
new In te g e r( 2 ) ,
3, / / Autopakovanje
);
S y s t e m . o u t. p r in t ln ( A rr a y s . to S tr in g ( a ) ) ;
System.out. p r i n t ln ( A r r a y s . t o S t r i ng(b)) ;
};
} / * I s p is :
[1. 2, 3]
[1, 2, 3]
* ///:-
U ob a slučaja, završni zarez u listi inicijalizatora nije obavezan. (Z ato je lakše o d rža-
vanje dugačkih lista.) Poslednji zarez u listi inicijalizatora je o p cio n i. (O va m o g u ćn o st je
n am en jen a lakšem o d ržavan ju d ugačkih lista.)
lako je p rv i oblik k oristan , njegova o g ran ičen ja su veća, je r se m ože u p o treb ljav ati
sam o na m estu definicije niza. D rugi i treći oblik m ožete u p o tre b iti b ilo gde, čak i u n u ta r
poziva neke m etode. Na p rim er, m ogli biste n ap rav iti niz o bjekata tip a String koje ćete
proslediti m etod i main() neke d ru g e m etode, da biste n a taj n ačin zadali a ltern ativ n e ar-
g u m en te k o m a n d n e Iinije toj m eto d i main():
/ / : i n i c i j a l i z a c i j a / D i n a m i c k i N i z . java
/ / I n i c i j a l i z a c i j a nizova.
Vežba 17: (2) N aprav ite klasu s k o n stru k to ro m koji p rim a arg u m en t tip a String. T okom
p ravljenja ispišite taj a rg u m en t. N apravite niz referenci objek ata te klase, ali n e m o jte p ra -
viti objekte koje ćete d o d eliti to m n izu. K ada p o k ren ete p ro g ram , p ro v erite d a li se ispi-
suju inicijalizacione p o ru k e iz poziva k o n stru k to ra .
Vežba 18: (2) D o p u n ite p re th o d n u vežbu p ravljenjem o bjekata koje ćete d o d eliti n iz u re-
ferenci.
class A { }
M ožete v ideti da p r in t( ) u zim a niz elem enata tip a O b jec t, a zatim foreach sintaksom
prolazi kroz niz i ispisuje sve njegove objekte. Klase sta n d a rd n e Javine biblioteke daju ra-
zum ljiv ispis, ali o bjekti ovde n ap rav ljen ih klasa štam p aju im e klase, znak @i heksadeci-
m aln e cifre. D akle, p o d ra z u m e v a n o p o n ašan je (ako za svoju klasu ne definišete m eto d u
to S trin g ( ), što će b iti o p isan o u nastavku knjige) jeste da se ispisuje im e klase i adresa
olijekta.
Poglavlje 5: Inicjjalizacija i čišćenje 151
O vako su se pre pojave Jave SE5 pisali Java p ro g ram i d a bi se d ob ile p rom enljive liste
p ara m e ta ra . M ed u tim , u Javi SE5 pojavila se du go zahtevana m o g u ćn o st da se p ro m en lji-
ve liste p a ra m e ta ra definišu p o m o ć u o p era to ra tri tačke, kao što vidite u sledećoj m e to d i
ispisiNiz():
/ / : in ic ij a li z a c i ja / O p c io n i P r a t e ć iA r g u m e n t i. java
I ovo pokazuje da prom enljiv.e p a ra m e tre m ožete u p o treb ljav ati i sa zadatim tip o m
koji nije Object. U p re th o d n o m p rim e ru , svi p ro m en ljiv i p a ra m e tri m o ra ju biti String
objekti. U p ro m en ljiv im p a ra m e trim a m o g u se ko ristiti svi tip ov i, i prosti. U sledećem
p rim e ru po k azu jem o d a lista p ro m en ljiv ih p a ra m e ta ra p ostaje niz i da je veličina to g niza
nula ukoliko ta lista n e m a sadržaja:
s t a t i c void f( L o n g . . . argumenti) {
S y s t e m . o u t . p r i n t ln ( " t r e ć i" ) ;
}
public s t a t i c void m a in (S trin g [] argumenti) {
f C a ' , ' b ' , 1c ' ) ;
f (i);
f(2 . i) ;
f (0 );
f (0 L );
/ / ! f ( ) ; / / Neće se prevesti - - višeznačno
}
} / * Is p is :
prvi a b c
drugi 1
drugi 2 1
drugi 0
treći
* ///:-
//: inicijalizacija/PreklapanjePromenljivihParametara3.java
Po pravilu, listu p ro m en ljiv ih arg u m e n a ta treb alo bi d a u p o treb ljav ate sam o u jed n o j
od p rek lo p ljen ih m eto d a. Ili, u o p šte n e u p o trebljavajte.
Vežba 19: (2) N apišite m e to d u koja p rim a String niz p ro m en ljiv ih a rg u m en ata. U verite
se da toj m e to d i m ožete proslediti listu elem en ata tip a String razdvojenih zarezim a ili
String[].
Vežba 20: ( 1) N apišite m e to d u main() koja u m esto u o b ičajen e sintakse u p o treb ljav a p ro -
menljive a rg u m en te. Ispišite sve elem en te rezu ltu ju ćeg niza args. Ispitajte tu m e to d u p o -
m oću različitog b ro ja arg u m e n ata s k o m a n d n e linije.
Nabrojani tipovi
Naizgled m ali d o d a tak u favi SE5 jeste rezervisana reč e n u m koja m n o g o olakšava život
kada tre b a da g ru pišete i u p otreb ite skup nabrojanih tipova (engl. enum erated types). N e-
kada biste napravili skup k o n sta n tn ih c elo b ro jn ih v red n o sti, ali one ,,ne znaju“ da čine
skup, pa se rizičnije i teže upo trebljavaju. Inače, n a b ro jan i tip o v i su toliko često p o tre b n i
da su oduvek postojali u C -u , C + + -u i u više d ru g ih jezika. Pre Jave SE5, p ro g ra m e ri na
Javi m orali su m n o g o d a znaju i da paze kako bi valjano proizveli e n u m efekat. Sada i Java
ima e n u m , a o n je čak i m oćniji o d o noga što im aju C /C + + . Evo je d n o stav n o g p rim era :
/ / : i n i c i j a l iza ci j a / L j u t o . java
pu b lic enum Ljuto {
NE, BLAGO, SREDNJE, MNOGO, PALI
} ///:-
156 Misliti na Javi
K ada n ap rav ite enum, p revodilac m u au to m atsk i dod aje korisne m o g u ćn o sti. N a p ri-
m er, prav i m e to d u toStringO k o jo m m o žete lako da prik ažete im e enum instance.
(U pravo tako je p re th o d n a n ared b a print dala rezultat. Prevodilac pravi i m e to d u ordi-
nal() koja daje red n i bro j d eklarisanja o d red en e enum k o n stan te u n jenoj klasi, i statičn u
m e to d u values() koja daje niz v red n o sti enum k o n stan ti p o ređ a n ih p o red o sled u kojim
su bile deldarisane:
lako enum izgleda kao nov tip p o d atak a, ta rezervisana reč sam o o d ređ u je p o n ašan je
p revodioca to k o m generisanja klase za enum, pa m ožete u m n o g o m e da ga tre tira te kao i
svaku d ru g u klasu. U stvari, n a b ro jan i tipovi i jesu klase sa sopstvenim m eto d am a.
Posebno je zg odno kako se n ab ro jan i tipovi m o g u u p o trebljavati u n u ta r n ared b e
svvitch:
p u b lic void o p i š i (} {
System .out.print("O va plje ska vic a j e " ) ;
switch(stepen) {
case NE: System .ou t.println ("po tp un o nezačinje na." ) ;
break;
case BLAGO;
case SREDNJE: S y s t e m . o u t . p r i n t l n ( " l j u t k a s t a . " ) ;
break;
case MNOGO:
case PALI:
d e fa u lt: S y s te m .o u t.p rin tln ("p re lju ta .");
}
}
p u b lic s t a t i c void m a in (S trin g [] args) {
P1jeskavic a
bez = new P1jeskavica(Ljuto.N E),
bib e r = new P1jeska(Začinjenost.SREDNJE),
fefero n = new P1jeska(Začinjenost.MNOGO);
bez.opiši ( ) ;
b ib e r . o p iš i ( ) ;
f e f e r o n . o p iš i ( ) ;
}
} / * I s p is :
Ova p lje s k a v ic a j e potpuno nezačinjena.
Ova p lje s k a v ic a j e lj u t k a s t a .
Ova p lje s k a v ic a j e p r e l j u t a .
* ///:-
Sažetak
P rilično razrađ en m e h a n iz am iniđ jalizacije, kakav je k o n stru k to r, treb alo bi da ukaže na
o g ro m n u važnost koja je u o v o m jeziku d ata inicijalizaciji. Jedno od p rv ih zapažanja o
p ro d u k tiv n o sti u C -u , koje je S tro u stru p izneo d o k je p ro jek to v ao jezik C + + , bilo je da
nep rav iln a in icijalizađ ja p ro m en ljiv ih p ro u zro k u je značajan deo pro b lem a p ri p ro g ra-
m iran ju . O v u v rstu grešaka je teško p ro n ači, a slični p ro b lem i javljaju se i p ri nepravil-
n o m čiščenju. Pošto k o n stru k to ri o m o g u ću ju da garantujete prav iln u inicijalizaciju
(prevodilac neće d o zv o liti d a o b jek at b u d e n ap rav ljen bez valjanog poziva k o n stru k to ra ),
d obijate p o tp u n u k o n tro lu i sig urno st.
U C + + -u u n ištav an je je v eom a b itn o , je r objekti n apravljen i o p e ra to ro m n ew m o raju
eksplicitno da b u d u u n išten i. Sakupljač sm eća u Javi au to m atsk i oslobađa m em o riju ,
tak o da u većini slučajeva o d g o varaju će m eto d e za čišćenje nisu p o treb n e. (Ali kada jesu,
m o rate sam i sve d a u ra d ite .) Kada v am ne treb a p o n a ša n je nalik na d estru k to re, Javin sa-
kupljač sm eća u m n o g o m e p o jed n o stav lju je p ro g ram ira n je i dodaje preko p o tre b n u si-
g u rn o st u u p rav ljan ju m e m o rijo m . N eki sakupljači sm eća m o g u čak da čiste i drug e
resurse, kao što su id en tifik ato ri d ato tek a i grafičkih objekata. M eđ u tim , sakupljač sm eća
dod aje režijske troškove to k o m izvršavanja p ro g ram a, čiju je cenu teško pro cen iti zbog
tra d iđ o n a ln e sp o ro sti Javinih in te rp re ta to ra. Iako su se p erfo rm an se Jave z n atn o po-
boljšaie to k o m v rem en a, taj jezik se, zbog svoje spo ro sti, i dalje ne p rim en ju je u nekim si-
tuacijam a.
O k o n stru k to rim a p osto ji još stvari koje nisu ra zm o tren e u ovom poglavlju. Pošto ga-
rancija ko n stru k cije važi i kada p rav ite nove klase kom pozicijom ili nasleđivanjem , p o treb-
n a je d o d a tn a sin tak sa da bi se to podržalo. O kom poziciji, nasleđivanju i to m e kako te
operacije u tiču n a k o n stru k to re , naučićete u n ared n im poglavljim a.
R ešenja o d a b ra n ih vežbi d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in Java Am iotated Solu-
tion Guide, k oji se m o ž e k u p iti na lo kaciji www.M indView.com.
Kontrola pristupa
K ontrola p ristu p a (ili sakrivan je realizacije) zn ači „m oći p o p ra v iti ono što p r v i p u t nije bilo
dobro u ra đ en o “.
SVI DOBRI PISCI - UKLJU ĆU JU ĆI T U I O N E KOJI PlSU SOFTVER - ZN A JU DA NIŠTA NIJE D O BRO
n ap isan o d o k se više p u ta n e p rerad i. A ko d eo svog k o d a na neko v rem e ostavite u fioku
i zatim ga p o n o v o p ro čitate, m o ž d a ćete o tk riti m n o g o bolji n a č in d a ga u rad ite. To je je-
dan o d glavnih razloga za p o n o v n u p o d e lu na p ro ste faktore, dakle za p re ra d u fu n k cio n al-
nog k o d a da bi se učin io čitljiviji i razum ljiviji, a sam im tim će se i lakše o d ržav ati.1
M e đ u tim , posto je i p ro b lem i zbog n asto jan ja d a se p ostojeći k o d m en ja i poboljšava.
Č esto korisnici (p ro g ra m eri klijen ti) raču n aju na to da će o d re đ e n i aspekat koda ostati
neizm enjen. D akle, vi h o ćete d a ga m en jate, a o n i d a o n o stan e neizm en jen . Z ato je jed an
od o sn o v n ih ciljeva o b jek tn o o rijen tisan o g dizajna „razdvojiti o n o što se m en ja o d stvari
koje o sta ju iste“.
O vo je p o se b n o važno za biblioteke. K orisnici biblioteke m o ra ju b iti sig u rn i kako se
m ogu o slo n iti na deo koji k o riste i kako neće m o rati p o n o v o d a p išu p ro g ra m e k ad a se
poiavi nova verzija biblioteke. S d ru g e stran e, tvorac biblioteke m o ra im ati slo b o d u da
pravi izm en e i u n a p ređ e n ja a da p ri to m b u d e siguran kako te izm ene neće uticati na kod
klijentskog p rog ram a.
Sve to m o že da se ostvari p rek o konvencija. Na p rim er, p ro g ra m e r biblioteke m o ra da
se složi kako neće izbacivati postojeće m eto d e kada m enja klasu u b iblioteci, je r b i tim e
n aru šio k o d p ro g ram era klijenta. O b rn u ta situacija je z n atn o složenija. A ko je reč o polju,
kako tv orac biblioteke m ože da zna kojim su p oljim a p ristu p ali p ro g ra m e ri klijenti? Isto
važi i za m eto d e koje su sam o deo realizacije klase i nisu n am en jen e da ih d ire k tn o u p o -
trebljavaju p ro g ram e ri klijenti. Šta ako tvorac biblioteke želi d a p o tp u n o izbaci staru re-
alizaciju i napiše novu? M enjanje bilo koje od gore p o m e n u tih članica m oglo bi d a n aruši
kođ p ro g ra m e ra klijenta. Stoga su tvorcu biblioteke vezane ruke i ne m ože ništa d a m enja.
Da bi se rešio ovaj p ro b lem , u Javi postoje specifikatori p ristu p a koji tv o rcu biblioteke
o m o g ućava ju da naznači šta je p ro g ra m e rim a klijentim a d o stu p n o , a šta nije. N ivoi d o -
zvoljenog p ristu p a od „ p o tp u n o g p ristu p a" d o „najm anjeg p ristu p a “ jesu p u b lic , p ro te c -
ted, p ak etn i p ristu p ( 7 a koji ne p ostoii rezervisana reč) i p riv a te . N a o sn o v u p re th o d n o g
m ožete p o m isliti da kao p ro jek tan t b iblioteke treb a sve da čuvate što ,,privatnije“ m oguće
i da izložite sam o m etode koje želite da p ro g ram eri klijenti koriste. To je sasvim tačno,
iako je često su p ro tn o intuiciji stru č n ja k a koji p ro g ram ira ju na d ru g im jezicim a (poseb-
no na C -u ) i koji su navikli da svem u p ristu p aju bez ograničenja. D o kraja ovog poglavlja
trebalo bi d a se uverite u značaj k o n tro le p ristu p a u Javi.
' V ideti liefactoring: Improving tlie Design o f Existing Code, koji su napisali M artin Fow!er i dr. (Addi-
son-W esley, 1999). Povrem eno se neko pobuni protiv po n o v n ep o d ele na proste faktore, sa argum en-
tom da je kod koji radi savršeno d o b ar i da prerađivati ga znači gubiti vrem e. Takav argum ent nije
vaijan zato što lavovski deo vrem ena i novca koji se uloži u projekat ne o tp ad a na prvo pisanje koda,
nego na njegovo održavanje. U činiti kod razum ljivijim , znači uštedeti m nogo novca.
16 0 Misliti na Javi
/ / : pristup/PunoIme.java
Sada ArrayList m ožete da k o ristite bez im en a paketa. M eđ u tim , nije d o stu p n a nijedna
d ru g a klasa iz p aketa java.util. Da biste uvezli sve klase biblioteke, u p o tre b ite * kao što je
u rad e n o u ostalim p rim e rim a u ovoj knjizi:
import j a v a . u t i l . * ;
Do sada se veći d eo p rim e ra u ovoj knjizi nalazio u je d n o j d ato teci i bio je n ap isan za
lokalnu u p o tre b u , te se nije b avio im e n im a pak eta. U tim p rim e rim a klase su ip ak bile u
paketu, tzv. n eim en o v an o m ili podrazumevanom paketu. To se svakako m ože rad iti, i u
nastavku knjige takav p ristu p biće korišćen gde g o d je m oguće, je d n o stav n o sti radi. Ali
ako nam eravate da prav ite b iblioteke ili p ro g ram e koje će v o d iti rač u n a i o d ru g im p ro -
gram im a na istom rač u n a ru , m o ra te da v o d ite ra č u n a o sp rečav an ju su k o b a im en a klasa.
Kada p rav ite dato tek u sa izv o rn im k o d o m za Javu, ta d ato te k a se često naziva i jedinica
za prevođenje (engl. compilation unit, translation unit ). Im e svake jed in ice za p revođenje
m ora da se završava sa .java, a u n u ta r nje m ože da p o sto ji je d n a jav n a (public) klasa istog
im ena kao i d atoteka (uk lju ču ju ći i velika i m ala slova, ali bez nastavka .java). U svakoj je-
dinici za prevođ en je m ože d a se nalazi sam o jedna jav n a klasa, in ače se prev o d ilac b u n i.
Ostale klase iz te jed in ice za prev o đ en je, ako ih u o p šte im a, skrivene su o d sp o ljn o g sveta
izvan tog paketa, jer o n e nisu javne i p red stav ljaju kiase za ,,p o d ršk u “ glavnoj javnoj klasi.
Organizacija koda
Pri prev o đ en ju dato tek e .java, d o b ijate p o je d n u izlaznu d a to te k u za svaku klasu iz d ato -
teke .java. Svaka o d tih izlaznih d ato tek a im a isto im e kao o d g o v araju ća klasa u d atoteci
.java, ali s nastavkom .class. Stoga iz m alog b ro ja d a to tek a .java m ožete da dobijete p ri-
ličan broj datotek a .class. Ako ste p ro g ram ira li n a jeziku koji se p rev o d i u izvršni oblik,
verovatno ste navikli da prevodilac p rav i m eđ u o b lik (o b ičn o d ato te k u .obj) koji zatim s
drugim d ato tek am a istog oblika p ak u jete p o m o ć u povezivača (engl. linker) d a biste na-
pravili izvršnu d atoteku ili to činite p o m o ću b ib lio tek ara (engl. librarian) da biste n a p ra -
vili biblioteku. Java ne radi n a taj način . P ro g ram koji rad i je sk u p d ato te k a .class koje
m ogu da b u d u spakovane i k o m p rim o v an e u JAR arh iv u (p o m o ć u Javinog arh iv ara jar.)
Posao Javinog in te rp re ta to ra je da p ro n ađ e, u čita i in te rp re tira 2 te d atoteke.
Biblioteka je skup takvih d ato tek a sa klasam a. Svaka izvorna d ato tek a o b ičn o im a jed-
nu klasu koja je javna i proizvoljan broj n ejavnih klasa, pa za svaku izv o rn u d ato tek u p o-
stoji je d n a javna (p u b lic) k o m p o n en ta . Ako želite d a n azn ačite da sve te k o m p o n e n te
(koje se nalaze u o d vojenim d ato te k am a .java i .class) id u zajedno, k o risti se rezervisana
reč package.
Ako ko ristite n ared b u package, o n a mora da b u d e u p rv o m re d u koji nije k o m e n ta r u
datoteci. Kada napišete:
package p r is tu p ;
Na p rim er, p retp o stav im o d a je im e d ato tek e MojaKlasa.java. To znači da u njoj m ože
d a po stoji jed n a i sam o je d n a jav n a klasa i d a im e te klase m o ra d a b u d e MojaKlasa (s to m
k o m b in acijo m velikih i m alih slova):
/ / : pristup/OdređenaMojaKlasa.java
/ / : pristup/OdređenaMojaKlasa.java
import p ris tu p .m o jp a k e t.* ;
G ru p isan je d ato teka jed n o g pak eta u zaseban d ire k to riju m rešava i jo š dva p ro b lem a:
pravljenje jedinstv enih im en a paketa i pro n alažen je ldasa koje m o g u biti zak o p an e negde
u d u b in i stru k tu re direkto riju m a. To se p ostiže tako što se p u ta n ja do d ato tek a .class ub a-
cuje u im e paketa. Po konvenciji, p rv i deo im e n a pak eta je im e In te rn e t d o m e n a tvorca
ldase, u o b rn u to m redosledu. Pošto su im en a In te rn e t d o m e n a g aran to v an o jed in stv ena,
ako p rim en ju je te ovu konvenciju, im e vašeg pak eta će sig u rn o b iti jed in stv en o i neće doći
do suk o b a im en a (tj. d o k neko d ru g i ne p reu zm e vaš d o m e n i n e po č n e da piše k o d na Javi
sa istim p u ta n ja m a koje ste i vi koristili). N aravno, ako n e m a te svoj d o m e n , sm islite m alo
v ero v atn u k om b in aciju (kao što je vaše im e i prezim e) d a b iste n ap rav ili je d in stv en o im e
paketa. A ko ste odlučili da objavljujete k o d p isan n a Javi, isp lati se ulo žiti relativ n o m ali
n a p o r u pribavljanje ličnog do m en a.
D ru g i d eo ove tehnike je d a preslikate im e p ak eta n a im e d ire k to riju m a n a v ašem ra-
ču n a ru . Kada izvršni sistem treb a da u čita d a to te k u .class, m ože da p ro n a đ e d ire k to riju m
u k o m e se o n a nalazi.
Javin in te rp re ta to r radi sledeće: prv o p ro n alazi sistem sku p ro m en ljiv u CLASSPATH3
(koja se postavlja u o p erativ n o m sistem u, što p o n ek a d u ra d i in stalacioni p ro g ram koji na
rač u n a ru instalira Javu ili na Javi zasnovanu alatk u ). CLASSPATH sadrži je d a n ili više di-
rek to riju m a koji se koriste kao korenski za tražen je d ato tek a .class. In te rp re ta to r u im en u
paketa zam enjuje svaku tačku kosom crto m da bi generisao p u ta n ju od korena definisanog
p rom enljivom CLASSPATH (tako paket foo.bar.baz postaje foo\bar\ baz ili foo/bar/baz
ili m o žda nešto drugo, u zavisnosti od vašeg o p erativ n o g sistem a). To se zatim spaja s ra-
znim d irek to riju m im a iz p u tan je CLASSPATH. N a tim m estim a in te rp re ta to r traži d ato -
teku .class čije im e odgovara klasi koju p okušavate d a napravite. (O n tak o đ e p retražu je i
neke stan d a rd n e d irektoriju m e u zavisnosti o d m esta n a k o m e se in te rp re ta to r nalazi.)
D a biste ovo shvatili, razm o trite im e m og d o m e n a - MindView.net. Kada o b rn e m o
redosled i svc ispišem o m alim slovim a, net.m indview ustanovljava se jed in stv en o global-
no im e za m oje klase. (N astavci com , edu, org itd. ranije su u Javinim pak etim a pisani ve-
likim slovim a, ali ie to p ro m e n je n o u Javi 2, tako da se celo im e piše m alim slovim a.) Ako
od lućim da n ap rav im biblioteku p o d im en o m simple, u n eću p o d elu u g lobalno im e i d o -
biti im e paketa:
package net.mindview.simple;
Sada ovo im e paketa m ože da se koristi kao im enski p ro s to r za sledeće dve datoteke:
/ / : ne t/m indview/simple/Vector.java
/ / P ra v lje n je paketa
package net.mindview.simple;
Kao što je već bilo rečeno, n are d b a package m o ra da b u d e u p rv o m red u koji nije ko-
m en tarisan . D ru g a d ato tek a je v eo m a slična:
//: net/mindview/simple/List.java
// Pravljerje paketa.
package net.mindview.simple;
C:\DOC\JavaT\net\mindview\simple
Kada p ro m en ljiv u CLASSfATH p o d esite kako treba, n ared n u d ato tek u m ožete da sta-
vite u bilo koji d irek to riju m :
/ / : p r is tu p / L ib T e s t .ja v a
/ / K o r i s t i b i b li o t e k u .
import ne t.m in dview .sim ple .*;
Pošto i java.util.* tak o đ e sadrži klasu Vector, to m ože d a izazove sukob im ena. M eđu-
tim , dokle g od ne napišete k o d koji izaziva d vosm islenost, sve će b iti u red u —to je do b ro ,
inače biste m o rali m n o g o da pišete d a biste izbegli sukob koji se nik ad a neće d ogoditi.
Sukob nastaje ako sada p o k u šate da n a p rav ite objekat klase Vector:
Vector v = new V e c to r () ;
Na koju se klasu Vector ovo odnosi? Prevodilac to ne m ože da zna, kao ni čitalac. Stoga
se prevodilac žali i zahteva da b u d ete izričiti. Ako, na p rim er, želite sta n d a rd n i Javin Vec-
tor, m o ra te da napišete:
/ / : n e t /m in d v i e w / u t il / P r i n t . ja v a
/ / Metode is p is iv a n j a koje se mogu u p o t r e b lja v a t i bez određenja
/ / paketa i putanje, kada ih u Javu SE5 uvezemo kao s t a t ič k e :
package n e t . m in d v ie w . u t il;
import j a v a . i o . * ;
p u b lic class P r in t {
/ / I s p i š i i pređi u nov red:
p u b lic s t a t i c void p r i n t (Object obj) {
S y s t e m . o u t . p r i n t ln ( o b j ) ;
}
/ / Samo pređi u nov red:
pu b lic s t a t i c void p r i n t () {
S y s t e m . o u t . p r i n t ln ( ) ;
}
/ / I s p i š i bez prelaska u nov red:
pu b lic s t a t i c void p rin tn b (Object ob j) {
S y s t e m . o u t. p r i n t ( o b j) ;
}
/ / Nova metoda p r i n t f ( ) Jave SE5 (kao u C-u):
p u b lic s t a t i c PrintStream
p r i n t f ( S t r i n g format, O b je c t . . . argumenti) {
return S y s t e m . o u t. p r in t f(f o r m a t, argumenti);
}
)///■■-
/ / : p r is tu p / P r in t T e s t . ja v a
/ / K o r i s t i s t a t ič k e metode za is p i s i v a n j e , koje pripadaju kla si
/ / P r in t . ja v a .
import n e t.m in d v ie w .u ti1. P r i n t . * ;
p rin t ( lO O L ) ;
p r i n t ( 3 . 14159);
}
) / * I s p is :
Dostupna od sada pa na dalje !
100
100
3.14159
* ///:-
O d sad a m ožete u svoju bib lio tek u d o d ati svaku k o risn u novu alatku kada god naiđete
na nju. B iblioteci n e t.m in d v ie w .u til do d av aćem o k o m p o n e n te kroz celu knjigu.
1 68 Misliti na Javi
Paketni pristup
U svim p rim e rim a d o ovog poglavlja specifikator p ristu p a u o p šte nije bio naveden. Za
p odrazu m ev an i p ristu p ne p o sto ji rezervisana reč, ali se o n o b ič n o naziva p a k e tn i (katka-
da i ,,prijateljski“ ). To znači da sve ostale klase iz tekučeg p ak eta im aju p ristu p to m članu,
ali sve klase van tog paketa taj član v ide kao p riv a tn i (i n e m o g u da m u p ristu p e ). Pošto
jedinica za prevođenje - d ato tek a - m o že d a p rip a d a sam o je d n o m p ak etu , sve klase u n u -
tar nje a u to m atsk i su d o stu p n e jed n a d ru g o j, zato što im aju p ak etn i p ristu p .
Paketni p ristu p om o g učav a da g ru pišete sro d n e klase u je d a n pak et ta k o d a m o g u lako
m eđ u so b n o da k o m u n iciraju . Kada klase grup išete u pak et, čim e p a k e tn im člano vim a
dozvoljavate m eđ u so b n i p ristu p , vi po stajete „vlasnik" k o d a u to m p aketu. Im a logike u
tom e d a sam o k o d u vašem vlasništvu treb a d a im a p ak e tn i p ristu p o stalo m k o d u u vašem
vlasništvu. M ože se reći da p ak etn i p ristu p o p ravdava g ru p isa n je klasa u pakete. U m n o -
gim jezicim a, definicije m ožete svakojako da organ izu jete, ali ste u Javi p risiljen i da ih or-
ganizujete n a razu m an način . Pored toga, v ero v atn o ćete odv ojiti klase koje n e tre b a da
im aju p ristu p klasam a defin isan im u tek u ćem pak etu .
Klasa k ontroliše odakle se m ože p ristu p a ti n jen im člano vim a. K od iz d ru g o g pak eta n e
m ože da se pojavi, kaže: „Z dravo, ja sam Perin p rijatelj“ i očekuje da će d o b iti p ristu p zaš-
tićenim , p ak etn im i p riv a tn im član o vim a klase Pera. Č lan u se dozvoljava p ristu p sam o
na sledeće načine:
1 . C lan proglasite jav n im (p u b lic). Tada svako i sa svakog m esta m ože da m u p ristu p i.
2. Ć lan u om og u ćite paketni p ristu p izostavljajući bilo kakve specifikatore p ristu p a , a
ostale klase stavite u isti paket s njim . Tada ostale klase to g paketa m o g u da p ristu -
p aju to m članu.
3. Kao što ćete videti u poglavlju Ponovno korišćenje klasa posv ećen om n asleđivanju,
p o to m a k klase m ože da p ristu p a i zaštićenim i jav n im člano vim a (ali ne i priv at-
n im članovim a) roditeljske klase. Č lan o v im a s p a k etn im p ristu p o m m ože da p ri-
stu p a saino ako su te dve klase u istom p ak etu . O nasleđivanju i zaštićenim
član ovim a n em o jte sada da b rinete.
4 . O bezbedite m eto d e koje čitaju i m enjaju željenu v re d n o st (koje se tak o đ e nazivaju
i m etode p ro čitaj/po stav i - engl. get/set rnethods). U O O P -u je to n ajp am etn iji p ri-
stu p i suštinski je značajan za zrn a Jave, kao što ćete videti u poglavlju Grafička ko-
risnička okruženja.
/ / : p r is tu p / d e s e rt /K o la c ic . ja v a
/ / Pravi b ib lio t e k u
package p r is tu p . d e s e rt ;
170 Misliti na Javi
/ / : pristu p/Vecera.ja va
/ / K o r i s t i b i b li o t e k u .
import p r i s t u p . d e s e r t . * ;
m ožete da nap rav ite objek at klase K olacic, je r je njegov k o n stru k to r javni i klasa je javna.
(K oncept javnih klasa detaljnije ćem o razm o triti kasnije.) M ed u tim , m eto d a članica za-
g rizi() nije d o stu p n a klasam a iz datoteke V ecera.java, jer m eto d a z a g rizi() o m ogućava
p ristu p sam o klasam a iz paketa d e se rt, pa će vas prevodilac sprečiti da je u p o treb ite.
Podrazumevani paket
M ožda će vas iznenaditi kada o tk rijete da će naredni p ro g ra m biti ispravno preveden iako
na prvi pogled izgleda da krši pravila:
/ / : p r is tu p / T o r t a . ja v a
/ / Pristupa k la s i iz druge (zasebne) je d in ic e za prevođenje (dato teke).
class Torta {
pu b lic s t a t i c void m a in (S trin g [] args) {
P ita x = new P i t a ( ) ;
x .f 0 ;
}
} / * I s p is :
P i t a . f ()
* ///:-
Poglavlje 6: Kontrola pristupa 171
/ / : p r i s t u p / P it a . ja v a
/ / Druga klasa.
class Pita {
void f ( ) { S y s t e m . o u t . p r i n t l n ( " P i t a . f ( ) ' ' ) ; }
} ///:-
U p rv o m tre n u tk u , ove dve dato tek e m ogli biste d a p o sm a tra te kao p o tp u n o odvojene
pa ip a k o b jek at klase Torta m ože d a n ap ra v i objek at klase Pita i d a pozove n je n u m e to d u
f()! (V odite ra č u n a o to m e d a u svojoj prom en ljiv o j CLASSPATH m o ra te im a ti ta č k u da
bi se ove datotek e p rav iln o prevele.) Pom islili biste d a klasa Pita i f() im aju p a k e tn i p ri-
stu p i d a su stoga n e d o stu p n e klasi Torta. O n e imaju p ak etn i p ristu p —taj d eo zaključka
je ispravan. D o stu p n e su klasi u dato teci Torta.java zato što se nalaze u isto m direktori-
ju m u i n em aju izričito im e paketa. Takve dato tek e Java tre tira kao deo „p o d razu m ev an o g
p aketa“ za taj d ire k to riju m i stoga om o g u ćav a p ak etn i p ristu p svim o stalim k lasam a u
istom d ire k to riju m u .
/ / : pris tu p /S la d o le d .ja v a
/ / Prikazuje rezervisanu reč " p r iv a t e " .
class SladoledSaVocem {
p r iv a t e SladoledSaVocem() {}
s t a t i c SladoledSaVocem napraviSladoledSaVocem() {
r e tu rn new SladoledSaVocemf);
}
172 Misliti na Javi
//: pristup/CokoladniKeks.java
// N ije moguć pristup članu s paketnim pristupom u drugom paketu
import pristup.desert.*;
//: pristup/kolacic2/Kolacic.java
package pris tu p.kolacic2;
//: pristup/CokoladniKeks2.java
import pr istup.kolacic2.*;
Interfejs i realizacija
K ontrola p ristu p a se često naziva i sakrivanje realizacije. Pakovanje p o d atak a i m etoda
u n u ta rk la sa u k o m binaciji sa skrivanjem realizacije često se naziva kapsuliranje.' Kao re-
zultat dobija se tip p o d atak a sa svojstvenim obeležjim a i o d re đ e n im p o n ašan jem .
K ontrola p ristu p a n am eće o g ran ičen ja u n u ta r tip a p o d a ta k a iz dva b itn a razloga. Prvi
je da od red ite šta p ro g ram eri klijenti sm eju da koriste, a šta ne. In tern e m eh an izm e m o-
žete slo b o d n o da u g rad ite u stru k tu ru , bez b rig e o to m e da će p ro g ram e ri klijenti slučajno
koristiti in tern e delove interfejsa.
To nas uvodi d ire k tn o u d ru g i razlog, a to je razdvajanje interfejsa i realizacije. A ko se
stru k tu ra koristi u više p ro g ra m a, a p ro g ram e ri klijenti sam o m ogu da šalju p o ru k e jav-
n o n i interfejsu, tad a m ožete da m enjate sve što nije jav n o (znači sve to je prijateljsko, za.š-
tićeno ili p riv atn o ), a da ne po k v arite klijentske p ro g ram e.
Radi boljeg razum evanja, kao stil pravljenja klasa m o žete usvojiti da na p o četk u stavite
javne članove, a zatim zaštićene, p ak etn e i p riv atn e. P red n o st ovog načina je to što kori-
snik klase m ože d a k ren e o d v rh a i prvo vidi o n o što m u je b itn o (javne članove, jer njim a
m ože da p ristu p i izvan date datoteke) a zatim će se zau stav iti kada naide na ostale članove
koji su deo in tern e realizacije:
private void p r iv l( ) { / * . . . * / }
private void priv2( ) { / * . . . * / }
private void priv3( ) { / * . . . * / }
private ir t i ;
/ / •••
} ///:-
O vo će sam o delim ično olakšati čitanje jer su interfejs i realizacija i dalje zajedno. O d n o -
sno, i dalje vidite izvorni ko d - realizaciju - je r se o n a nalazi tu , u klasi. Pored toga, dok u-
m entacioni k o m en tari koje p održava alatka Javadoc u m a n ju ju značaj čitljivosti prog ram a
za prog ram era klijenta. Prikazivanje interfejsa k o risniku klase u stvari je posao čitača klasa
(engl. class browser), alatke čiji je zadatak d a nap rav i pregled svih raspoloživih klasa i pokaže
šta se s njim a korisno m ože u rad iti (tj. koje članice su d o stu p n e). Prikazivanje d o k u m en ta-
cije razvojnog okruženja za Javu p o m o ć u čitača W eba daje isti rezultat kao i čitač klasa.
Pristup klasama
Specifikatori p ristu p a u Javi tak o đ e se m o g u u p o tre b iti d a o d red e koje će klase u n u ta rb i-
blioteke biti d o stu p n e n je n o m k o risn ik u. A ko želite d a klasa b u d e d o stu p n a p ro g ra m e ru
kliientu, stavite rezervisanu reč p u b lic n a čelo definicije klase. T im e od ređ u jete d a li pro -
gram er klijent u op šte m ože da n ap rav i objekat date klase.
Da bi kon tro lisao p ristu p klasi, specifikator m o ra da se n ađ e pre rezervisane reči class.
Stoga m ožete d a napišete:
import pristup.Spravica;
import pristup.*;
Šta ako u n u ta r b iblioteke pristup im ate klasu k o ju k o ristite sam o za zadatke koje izvr-
šava klasa Spravica ili neka d ru g a javna klasa iz te biblioteke? N ećete d a pišete d o k u m e n -
tacije za p ro g ra m e ra k lijenta i m islite kako ćete k asnije v ero v atn o h te ti d a p o tp u n o
izm en ite stvari i celu tu klasu izbacite i zam e n ite je n ek o m novom . D a b iste zadržali tu
m o g u ćn ost, m o ra te o sig u rati da n ijed an p ro g ra m e r klijen t ne p o sta n e zavisan o d o d re-
đ e n ih detalja realizacije skrivenih u n u ta r biblioteke pristup. Da biste to postigli, izosta-
vite rezervisanu reč public ispred klase, p ri čem u o n a d o b ija p a k e tn i p ristu p . (Ta klasa
m ože d a se k o risti sam o u n u ta r paketa.)
Vežba 7: ( 1) N ap rav ite bib lio tek u pristup n a o sn o v u p re th o d n ih delova k ođa koji o p isu ju
n ju i klasu Spravica. Z atim n a p rav ite o b jek at klase Spravica u klasi koja nije d eo pak eta
pristup.
K ada n ap rav ite k lasu s p ak etn im p ristu p o m , im a sm isla d a n jen a p o lja b u d u p riv a tn a
—p olja b i uvek treb alo d a b u d u p riv atn a —ali je p o p rav ilu u p u tn o d ati m e to d a m a je d n ak i
(p aketni) p ristu p k ao klasi. Pošto se klasa s p ak e tn im p ristu p o m o b ič n o u p o treb ljav a
sam o u n u ta r svog pak eta, m eto d e takve klase tre b a da u čin ite jav n im sam o ako m o rate , a
to će v am reći prevodilac.
Z ap am tite da klasa n e m ože da b u d e p riv a tn a (tim e bi postala d o stu p n a sam o sebi)
n iti zaštićena.6 Za p ristu p klasi, stoga, im ate sam o dva izbora: p ak etn i ili javni. A ko želite
da nik o dru g i n e m a p ristu p nekoj klasi, sve k o n stru k to re m o žete da p roglasite p riv atn im ,
čim e ćete sprečiti sve osim sebe da n aprave ob jek at te klase. (I vi ćete to m oći sam o u n u ta r
statičn e članice te klase.) Evo p rim era:
//: pristup/Rucak.java
// Pokazuje specifikatore pristupa k la s i. Klasu pretvarate
// u privatnu proglašavajući njene konstruktore privatnim:
class Supal {
private Supal() {}
// (1) Dozvoljava pravljenje statičkom metodom:
public s ta tic Supal napraviSupu() {
return new Supal( ) ;
}
class Supa2 {
private Supa2() {}
// (2) Napravi statičn i objekat i vra ti referencu ako je zahtevano.
// ("Sing ularni" projektni obrazac):
private s ta tic Supa2 psl = new Supa2();
public s ta tic Supa2 pristupO {
return psl;
}
public void f ( ) {}
}
" U n u tra š n ja (e n g l. inner) klasa m o ž c b iti p riv a tn a ili z aštič e n a, ali to je s p ec ija la n slučaj. Te klase će
b iti o b ja šn je n e u p o g la v lju Unutrašnje klase.
Poglavlje 6: Kontrola pristupa 177
isprva m ože m alo da zbuni. Reč Supal p re im ena m eto d e (napraviSupu) g o v o ri šta m e-
toda vraća. Do sada je u ovoj knjizi to najčešće bila reč void, što znači da ne vraća ništa.
Ali tak o d e m ožete da vratite i referencu na objekat, što se ovde i đešava. M eto d a vraća re-
ferencu na objekat klase Supal.
Klase S u p a l i S u p a2 pokazu ju kako se proglašavanjem k o n stru k to ra p riv a tn im sp re-
čava d ire k tn o stv aranje objekata. Z apam tite, ako izričito ne n a p ra v ite n ijed an k o n stru k -
tor, au to m atsk i će biti n apravljen p o d razu m ev an i k o n stru k to r (k o n s tru k to r bez
arg u m en ata). Ako sam i napišete p o d razu m ev an i k o n stru k to r, sprečićete d a se o n a u to -
m atski napravi. U koliko ga zatim proglasite p riv atn im , niko neće m o ći d a n ap rav i obje-
kat te klase. Ali, kako će sada iko tu klasu d a koristi? P re th o d n i p rim e r p o k azu je dva
n a č in a . U S u p a l napravljena je statična m etoda koja pravi novi objekat klase S u p a l i v ra-
ća referencu na njega. To m ože da b u d e k o risn o ako želite da sa o b jek to m klase S u p a l
u rad ite još neke operacije pre nego što v ratite referencu ili ako želite da v o d ite ra ču n a o
broju naprav ljenih o b jekata kiase S u p a l (recim o da biste o graničili n jih o v u p o p u laciju ).
U S u pa2 koristi se projektni obrazac, o čem u više m ožete da p ro čitate u knjizi T hinking
in Patterns (w ith Java), na adresi w w w .M indV iew .net. U po treb ljen i o b razac u p rim e ru na-
ziva se „singularan", jer dozvoljava da b u de naprav ljen sam o je d a n objekat. O b jek at klase
Supa2 je napravljen kao statični i priv atn i član klase S upa2, pa p o stoji jed a n i sam o jed an
i njem u m ožete da p ristu p ite sam o preko javne m eto d e p ris tu p ().
Kao što je ranije n a p o m e n u to , ako ne stavite nijedan specifik ato r p ristu p a za klasu,
pod razu m ev a se da je u p ita n ju paketni p ristu p . To znači da b ilo koja d ru g a klasa u paketu
m ože da n apravi ob jek at date klase, d o k o n e izvan paketa to ne m ogu. (Z a p a m tite da su
sve datotek e koje se nalaze u istom d ire k to riju m u i koje n em aju izričitu p a k e tn u deklara-
ciju, eksplicitno svrstane u p o d razu m ev an i paket za taj d ire k to riju m .) A ko je, m e đ u tim ,
statični član te klase javni, p ro g ram e r klijent još uvek m ože da p ristu p a sta tič n im člano-
vim a, iako ne m ože da n ap rav i objekat te klase.
178 Misliti na Javi
///: pristup/lokal:UpakovanaKlasa.java
package pr is tup.lokal;
class UpakovanaKlasa {
public UpakovanaKlasa () {
System.out.print1n("Pravljenje upakovane klase1');
}
}
///: pristup/strana/Strana.java
package pristup.strana;
import pristu p. lo ka l.*;
Sažetak
U svakoj vezi b itn o je da sve uklju čen e stran e p o štu ju izvesne granice. Kada pravite bibli-
o tek u , vi uspostavljate vezu s k o risn ik o m te biblioteke - p ro g ra m e ro m klijen to m - koji je
tako đ e p ro g ram er, ali koji koristi vašu b iblioteku da bi n ap rav io neku aplikaciju ili još
veću biblioteku.
Kad ne bi bilo pravila, p ro g ra m e ri klijenti m ogli bi da rade šta im je volja sa svim čla-
n o v im a klase, čak i k ada biste vi više voleli da ne rade d irek tn o s nekim od njih. Sve bi bilo
p o tp u n o izloženo.
O vo poglavlje se o d n o silo na pravljenje klase da bi se fo rm irala biblioteka: prvo, na
koji način se klase p ak u ju u je d n u bib lio tek u i dru g o , način na koji klasa kontroliše pri-
stu p svojim članovim a.
Poglavlje 6: Kontrola pristupa 179
P ro cen a je da pro jek ti pisani n a jeziku C p o čin ju da se uru šav aju kada d o stig n u izm e-
đ u 50 K i 100 K redova koda. C im a jeđ in stv en im enski p ro sto r, p a im en a p o č in ju d a se
su d a ra ju te ih m o rate raspetljavati. U Javi, rezervisana reč package, te h n ik a im en o v an ja
pak eta i rezervisana reč import, o b ezb eđ u ju p o tp u n u k o n tro lu n ad im e n im a , čim e se
p ro b le m sa su d ara n je m im en a lako zaobilazi.
Postoje dva razloga za k o n tro lu p ristu p a članovim a. Prvi je obezb ed iti da korisn ici
d rže ru k e podalje o d delova koje ne bi sm eli da diraju . Ti delovi su n e o p h o d n i za in te rn i
ra d klase, ali nisu deo interfejsa koji je p o treb an p ro g ram e rim a k lijentim a. Z ato p ro g la-
šavanjem m eto d a i p olja p riv atn im p rav ite uslugu p ro g ram e rim a k lijen tim a - lako m o g u
da razluče šta je za njih b itn o , a šta m o g u d a ignorišu. Z bog to g a bolje ra z u m e ju klase.
D ru g i i najvažniji razlog k o n tro le p ristu p a jeste o m o g u ćiti p ro je k ta n tu bib lio tek e da
p ro m e n i u n u tra šn ji izgled ldase b ez brige o to m e kako će to u ticati n a p ro g ra m e ra kli-
jen ta . N a p rim e r, ldasu isprva n ap rav ite n a jed an način , a zatim otk rijete kako će se re-
k o n stru k cijo m k oda b itn o u b rz ati izvršavanje. Ako su interfejs i realizacija jasn o
razd v o jen i i zaštićeni, to m ožete da postig n ete bez p rim o ra v a n ja p ro g ra m e ra k lijenta da
p o n o v o p išu svoje p ro g ram e. K ontrola p ristu p a osigurava d a n ijed an p ro g ra m e r k lijent
ne p o sta n e zavisan o d realizacije koja se nalazi u osn o v i klase.
K ada im ate m o g u ćn o st da m en jate o sn o v n u realizaciju, ne sam o da m o žete da u n a -
pre đ u je te projekat, već i da prav ite greške. Bez ob zira na to koliko pažljivo p lan ira te i p ro -
jek tu jete, naprav ićete greške. Ako zn ate da greške neće izazvati m n o g o štete, bićete više
rasp olo ženi za eksperim ente, brže ćete učiti i brže završavati projekte.
K orisnik vidi javni interfejs, pa je to, p rilik o m analize i p ro jek to v an ja p ro g ra m a , naj-
važniji deo klase koji treba da u rad ite kako valja. Ć ak vam je i tu d ata izvesna slo b o d a da
m en jate. A ko interfejs ne ,,pogodite“ iz prve, uvek m ožete da dodate d ru g e m eto d e, dokle
god ne izbacite neke koje je p ro g ram e r klijent već u p o treb io u svom p ro g ra m u .
O b ra tite pažn ju na to da se kon tro la p ristu p a u sred sređ u je na o d n o s - i n ek u v rstu
k o m u n ik a c ije - izm eđu au to ra biblioteke i n jenih spoljnih klijenata. Im a m n o g o situacija
u kojim a to nije slučaj. P rim era radi, sam i pišete sav kod ili rad ite zajedno s m a lo m eki-
p o m i sve što napišete ide u isti paket. U tim situacijam a p o tre b n a je drugačija k o m u n i-
kacija, pa k ru to prid ržavan je pravila p ristu p a m ože da sm eta. Tada bi p o d ra zu m e v a n i
(p ak etn i) p ristu p m ogao da b u d e o p tim alan .
Rešenja o d a b r a n i h vežbi data su u e l e k tr o n sk o m d o k u m e n t u Thinking Iti Java A nnotated Solution
Guide, koji se m o ž e kupiti na Iokaciji www.M indView.net
Ponovno korišćenje klasa
Jedna od najlepših m ogućnosti u Javijeste ponovno korišćenje koda. Ali, da biste u tom p o slu
bili revolucionarni, sa kodom m orate u m eti da uradite m nogo više od p u ko g kopiranja i
menjanja.
Sintaksa kompozicije
D o ovog m esta u knjizi često sm o k oristili k o m p o ziciju - sm eštan je referenci na objekte
u n u ta r nove klase. P retp o stav im o , na p rim e r, da želite da n a p rav ite objekat koji čuva ne-
koliko o bjekata tip a S trin g , nekoliko p ro stih tip o v a i o b jek at neke d ru g e klase. Kod slo-
ženih tipova, u n u ta r nove klase stavljate reference, d o k p ro ste tipove d ire k tn o definišete:
//: ponovnaupotreba/Prskalica.java
// Ponovna upotreba koda kompozicijom.
class Izvor {
private String s;
Izvor() {
System.out.pri n t ln("Izvor()");
s = "Konstruisan";
}
public String t o S t r i n g O { return s; }
}
private int i ;
private float f;
public String t o S t r i n g O {
return
"ventill = " + ventill + " " +
"venti!2 = " + ventil2 + " 11 +
"ventil3 = " + ventil3 + " " +
"ventil4 = " + ventil4 + "\n" +
"i = 11 + i + " " + "f = " + f + " " +
"izvor = " + izvor;
}
public static void main(String[] args) {
Prskalica prskalice = new Prskalica();
System.out.println(prskal i c e ) ;
}
} /* Ispis:
Izvor()
ventill = null ventil2 = null ventil3 = null ventil4 = null
i = 0 f = 0.0 izvor = Konstruisan
* ///:-
prevodilac p rim eću je kako p okušavate da na objek at tipa String ("izvor = ") nadovežete
objekat tipa Izvor. Pošto na objek at tip a String m o žete da nadovežete sam o d ru g i takav
olijekat, o n kaže: „pretv oriću Izvor u tip String po ziv an jem m e to d e toString()“. N akon
toga, o n m ože da k o m b in u je dva o b jek ta klase String i da rezultat pro sled i m eto d i Sy-
stem.out. println() (ili p re th o d n o u knjizi d efin isan im statićn im m eto d a m a print() i
printnb()). Kad god želite da klasi koju p rav ite o m o g u ćite takvo p o n ašan je, sam o nap i-
šite m e to d u toString().
Prom enljive pro stog tipa koje su polja klase, au to m a tsk i su inicijalizovane v rednošću
nula, kao što je n a p o m e n u to u poglavlju Sveje objckat. M eđ u tim , reference na objekte ini-
cijalizovane su v red n o šću null i ako preko bilo koje od tih referenci p o k u šate da pozovete
neku m eto d u , izazvaćete izuzetak - grešku to k o m izvršavanja. D o b ro je što ipak m ožete
da ispišete null referencu, a da se p ri to m ne javi izuzetak.
Im a sm isla to što prevodilac ne pravi p o d razu m ev a n i objek at za svaku referencu, je r bi
to u m n o g im slučajevim a izazvalo n ep o tre b n e režijske troškove. Ako želite da reference
b u d u inicijalizovane, to m o rate da u rad ite sam i:
1. Na m estu gde se objekti definišu. To znači d a će uvek biti inicijalizovani p re nego što
k o n stru k to r b u d e pozvan.
2. U k o n stru k to ru te klase.
182 Misliti na Javi
3. N eposredno p re nego što v am je zaista taj o bjek at p o tre b a n . To se često naziva i le-
nja inicijalizacija. O n a m o že d a sm an ji režijske troško ve o n d a k ad a je pravljenje
objekta skupo, a ne treb a ga p ra v iti svaki p u t.
4. Inicijalizacijom instance (p rim erk a ).
Sva četiri p ristu p a p rik azan a su ovde:
//: ponovnaupotreba/Kada.java
// Inicijalizacija u konstruktoru i kompozicija.
import static ne t.mindview.util.Print.*;
class Sapun {
private String s;
Sapun() {
print{"Sapun()");
s = “Konstruisan";
}
public String t o S t r i n g O { return s; }
}
p rin t(b );
}
} /* Ispis:
Unutar klase Kada()
Sapun()
sl = Srećan
s2 = Srećan
s3 = Radostan
s4 = Radostan
i = 47
igracka = 3 . 1 4
sapuncic = Konstruisan
* ///:-
Sintaksa nasleđivanja
N asleđivanje je sastavni deo Jave (i u o p šte o b jek tn o o rijen tisa n ih jezika). U stvari, kada
pravite klasu, ispostavlja se da uvek p rim en ju jete nasleđivanje. A ko eksplicitno ne nasle-
dite neku klasu, im plicitno ćete n aslediti favinu p o d ra z u m e v a n u ko ren sk u kiasu O b ject.
S intaksa kom pozicije je oćigledna, ali se za n asleđivanje up otreb ljav a p o seb n a sin tak -
sa. Pri nasleđivanju kažete „nova klasa ie kao ta sta ra klasa“. U p ro g ra m u to navodite pre
p o ćetn e vitićaste zagrade tela klase, pišući rezervisanu reć e x te n d s iza koje sledi im e
osnovnc kluse. Kada to učinite, au to m atsk i p reu zim a te sva polja i m eto d e o sn o v n e klase.
Evo p rim era :
//: ponovnaupotreba/Deterdzent.java
// Sintaksa i osobine nasleđivanja.
import static net.mindview.util.Print.*;
class Cistac (
private String s = "Cistac";
public void dodaj(String a) { s += a; }
public void razredi() { dodaj(" razredi()"); }
public void sipaj() { dodaj(" sipaj()"); }
public void ribaj() { dodaj(" ribaj()"); }
public String toStringO { return s; }
public static void main(String[] args) {
Cistac x = new Cistac();
184 Misliti na Javi
M e đ u tim , ako bi n ek a klasa iz nekog d ru g o g pak eta nasledila klasu Cistac, o n a b i m ogla
da p ristu p a sam o n je n im jav n im članovim a. D akle, da biste om ogućili nasleđivanje klase,
opšte p rav ilo je da sva polja b u d u p riv atn a, a sve m eto d e javne (izvedena klasa tak ođ e
m ože da p ristu p a zaštićenim članovim a; o to m e nešto kasnije). N aravno d a u o d ređ en im
slučajevim a m o ra te u čin iti izvesna prilag o đ av an ja, ali ovo je k o risn a sm ernica.
Klasa Cistac im a g ru p u m eto d a u svom interfejsu: dodaj(), razredi(), sipaj(), ribaj()
i toString(). Pošto je klasa Deterdzent izvedena iz klase Cistac (p o m o ću rezervisane reči
extends), njen interfejs au to m atsk i sadrži i navedene m eto d e, iako ne v id ite d a su o n e u
njoj izričito definisane. Z nači d a nasleđivanje m o žete d a p o sm a tra te kao p o n o v n o kori-
šćenje klase.
Na p rim e ru m eto d e ribaj() v id i se d a je m og uće m en jati m e to d u koja je već bila defi-
nisan a u o sn o vno j klasi. U ovom slučaju, iz n ove verzije je p ozv an a i m eto d a iz osno v n e
klase. M e đ u tim , u m e to d i ribaj() nije ispravno d a se pozove sam o m eto d a ribaj(), je r bi
to b io rek u rziv an poziv, a to niste n am eravali. D a b i rešila ovaj p ro b lem , Javina rezervi-
sana reč super označava natk lasu (engl. superclass) koju tek u ća klasa nasleđuje. Stoga n a-
redba super.ribajO poziva verziju m eto d e ribaj() iz o sn o v n e klase.
Pri n asleđivanju ne m o ra te ko ristiti sam o m eto d e o sn o v n e klase. Izvedenoj klasi m o -
žete d a dod ajete nove m eto d e na isti način na koji b iste to radili i s bilo k o jo m d ru g o m
klasom : definišite ih. P rim e r za to je m etod a za p e n i().
U m eto d i Deterdzent.main() v idite da preko objekta klase Deterđzent m o žete da p o -
zivate m eto d e koje su raspoložive za klasu Cistac i o n e za klasu Deterdzent (na prim er,
zapeni()).
Vežba 2: (2) Iz klase D e te rd z e n t nasledite novu klasu. Redefinišite m eto d u rib a j(), a d o -
dajte i n o v u m e to d u p o d im e n o m ste rilisi().
//: ponovnaupotreba/Karikatura.java
// Poziv konstruktora pri nasleđivanju.
import s ta tic net.m indview .util.P r in t.* ;
186 Misliti na Javi
class UmetnickoDelo {
UmetnickoDelo() { print("Konstruktor klase UmetnickoDelo"); }
}
Konstruktori sa argumentima
U p re th o d n o m p rim e ru su k orišćeni p o d ra zu m e v a n i k o n stru k to ri; o d n o sn o , oni nisu
im ali nikakve arg u m e n te . P rev o d io cu je lako da ih poziva, je r nem a su m n je koje arg u -
m ente treb a p roslediti. A ko o sn o v n a klasa n em a p o d raz u m e v a n i k o n stru k to r ili ako želite
da pozovete k o n stru k to r o sn o v n e klase kom e su p o tre b n i a rg u m e n ti, m o rate izričito po-
zvati k o n stru k to r o sn o v n e klase koristeći rezervisanu reč s u p e r i proslediti m u odgova-
rajuću listu arg u m en ata:
//: ponovnaupotreba/Sah.java
// Nasleđivanje, konstruktori i argumenti.
import s ta tic net.m indview.uti1 .P r in t .*;
Poglavlje 7: Ponovno korišćenje klasa 187
class Igra {
Ig ra (in t i) {
printC'Konstruktor klase Ig ra ");
}
}
Delegiranje
Java ne podržava d ire k tn o treći o d n o s koji se naziva delegiratije. To je nešto na pola p u ta
izm eđu nasleđivanja i k om pozicije, p o što u klasu k o ju p ra v ite sm eštate o b jek at član (kao
kom pozicija), ali u novoj klasi isto v rem en o ek sp o n ira te sve m eto d e to g objekta člana
(kao nasleđivanje). N a p rim er, svem irskom b ro d u treb a m o d u l za upravljanje:
//: ponovnaupotreba/UpravljačkiUređajiSvemirskogBroda.java
//: ponovnaupotreba/SvemirskiBrod.java
N aravno, S v em irsk iB ro d u stvari n ije „jed an od o bjekata“ klase U prav Ijačk iU ređ ajiS -
ve m irsk o gB ro d a, čak i ako o bjektu klase S v em irsk iB ro d , p rim e ra radi, m ožete „narediti"
da ide n a p red (). Tačnije je reći da S v em irsk iB ro d sadrži U p rav Ijačk iU ređ ajiS v em irsk o g -
B roda. Istovrem eno, u o b jek tu ldase S v em irsk iB ro d ek sp o n iran e su sve m eto d e objekta
klase U prav ljačkiU red ajiS v em irsk o g B ro d a.
O vu n ed o u m icu rešava delegiranje:
//: ponovnaupotreba/DelegiranjeSvemirskogBroda.java
upravljačkiUređaji.nazad(brzina);
}
public void naniže(int brzina) {
upravljačkiU ređaji.naniže(brzina);
}
public void napred(int brzina) {
upravljačkiUređaji.napred(brzina);
}
public void nalevo(int brzina) {
upravljačkiU ređaji.nalevo(brzina);
}
public void nadesno(int brzina) {
upravljačkiUređaji.nadesno(brzina);
}
public void turboPotisak(int brzina) {
up ravljačkiUređaji.turboPotisak(brzina);
}
public void naviše(int brzina) {
upravljačkiU ređaji.naviše(b rzina);
}
public s ta tic void m ain(String[] args) {
DelegiranjeSvemirskogBroda š t i t =
new DelegiranjeSvemirskogBroda("PVO t i t " ) ;
štit.napred(lO O );
}
} III---
V idi se kako su m etode pro sleđ en e p rip a d n o m o b jek tu tip a u p ra v lja č k iU re đ a ji, pa je
interfejs zato je d n a k kao pri nasleđivanju. M eđ u tim , deleg iran je o m o g u ću je više k o n tro -
le, p ošto u objekat član m ožete p ren eti proizvoljan p o d sk u p svih m etoda.
Iako Java ne podržava delegiranje, im a m n o g o razv o jn ih alatki koje ga p održavaju. Re-
cim o, gornji p rin ie r je au to m atsk i g enerisan p o m o ć u razvojnog o k ru žen ja JetB rains Idea.
V ežba 11: (3) Izm enite D e te rd z e n t.ja v a tako da se u njoj koristi delegiranje.
class Pribor {
Prib o r(in t i) {
print("Konstruktor klase P rib o r");
}
}
}
public s ta tic void main(String[] args) {
PostavkaStola x = new PostavkaStola(9);
}
} /* Isp is:
Konstruktor klase Obicaj
Konstruktor klase Pribor
Konstruktor klase Kasika
Konstruktor klase Pribor
Konstruktor klase Viljuska
Konstruktor klase Pribor
Konstruktor klase Noz
Konstruktor klase Tanjir
Konstruktor klase TanjirZaVeceru
Konstruktor klase PostavkaStola
* ///:-
Iako vas prevodilac prisiljava da inicijalizujete osn ov ne klase i zahteva da to u čin ite od-
m ah n a p o četk u k o n stru k to ra , o n ne vodi ra č u n a o to m e d a li ste inicijalizovali objekte
članove, pa m o rate sam i o to m e voditi raču na.
P rilično je zadivljujuće kako su klase jasn o razdvojene. N ije vam čak p o tre b a n ni iz-
vorni k od m eto da da biste ih p o n o v o koristili. U većini slučajeva treb a sam o d a uvezete
paket. (O vo važi i za nasleđivanje i za ko m poziciju.)
//: ponovnaupotreba/CADSistem.java
// Obezbeđivanje pravilnog čišćenja
package ponovnaupotreba;
import s ta tic net.mindview.uti1. P rin t.*;
cl ass Obli k {
O blik( i nt i) { print("Konstruktor klase O b lik "); }
void ciscenje() { print("Č išćenje klase O b lik "); }
}
192 Misliti na Javi
U ovom sistem u sve je neka v rsta Oblika (koji je i sam tip a Object, jer im p licitn o na-
sleđuje k orensku klasu). Svaka klasa redefiniše m e to d u ciscenje() klase Oblik i p o red
ostalog, poziva i verziju te m etod e iz osn o v n e klase p o m o ć u rezervisane reči super. Poje-
dine vrste klase Oblik - Krug, Trougao i Linija - im aju svoje k o n stru k to re koji „crtaju",
p rem d a bilo koja m e to d a to k o m životnog veka objekta m ože da u rad i nešto što zahteva
čišćenje. Svaka klasa im a svoju m e to d u ciscenje() koja vraća stvari bez ikakve veze s m e-
m o rijo m , u stanje u kom e su bile pre nego što je objekat napravljen.
194 Misliti na Javi
U m eto d i main() m o žete d a p rim e tite dve n o ve rezervisane reči koje neće biti d etaljn o
o b jašnjene sve d o poglavlja O brada grešaka pom oću izuzetaka, a to su try i finally. Rezer-
visana reč try naznačava da je b lo k koji sledi (u n u ta r v itičastih zagrada) zaštićeni region,
što znači d a im a p o seb an tre tm a n . Jedan d eo to g p o se b n o g tre tm a n a o d n o si se n a k o d
u n u ta r b lo k a finally koja sledi iza to g zaštićenog regiona. Taj k o d se uvek izvršava, bez o b-
zira na to kako se iz b loka try izlazi. (P ri o b ra d i izuzetaka, b lo k try m ože se n ap u stiti na
više n eu o biča jen ih načina.) U o vo m slučaju, b lo k finally kaže: ,,Za x uvek pozovi m e to d u
ciscenje(), bez o b zira n a to šta se d o g ađ a“.
U koliko jed an p o d o b je k a t zavisi o d d ru g o g , zap am tite da u svojoj m e to d i za čišćenje
(u ovom slučaju, m e to d i ciscenje()) tak o đ e m o rate da v odite ra č u n a o p o retk u čišćenja
osnov n e klase i o b jekata članova. Po prav ilu , treb a da p ra tite p rin c ip koji je uveo prevo-
dilac jezika C + + za svoje d estru k to re: prv o o b av ite sve čišćenje k oje je specifično za vašu
klasu, i to red osled o m koji je o b rn u t o d redo sled a pravljenja. (U o p štem slučaju, za to je
p o tre b n o d a elem en ti o sn o v n e kiase i dalje b u d u u životu.) Z atim pozovite m e to d u za či-
šćenje o sno v n e klase, kao što je u n ašem p rim e ru p o k azan o .
U m n o g im slučajevim a čišćenje nije p ro b le m i m o žete d a p u stite sakupljača sm eća da
obavi posao. Ali, kada je izričito čišćenje p o tre b n o , n je m u m o ra te da p ristu p ite m arljivo
i pažljivo, p o što n a sak u p ljan je sm eća ne m ožete previše d a se oslo n ite. Sakupljanje sm eća
u o pšte n e m o ra da se d o go d i, a ako se d o g o d i, m ože d a čisti objekte bilo kojiin redosle-
d o m . N ajbolje je n e oslan jati se na sakupljač sm eća ni za šta o sim za o slobađanje m em o -
rije. Ako v am je čišćenje p o tre b n o , n a p rav ite svoje m eto d e za njega i ne oslanjajte se na
m eto d u finalize().
V ežba 12: (3) D odajte o d g o v araju ću h ijerarh iju m eto d a ciscen je() svim klasam a iz vež-
ban ja 9.
Sakrivanje imena
Ako je u osnovnoj klasi u Javi defin isano im e m eto d e koje je kasnije nekoliko p u ta pre-
klopljeno, redefinisanje tog im en a m eto d e u izvedenoj klnsi //ect’ sakriti n ijed n u verziju iz
o sn o v n e klase (za razliku o d C + + -a ). Stoga prek lap an je fu n k cio n iše bez obzira na to da li
je m eto d a definisana na to m nivou ili u o sn o v n o j klasi:
/ / : ponovnaupotreba/Sakrivanje.java
// Preklapanje imena metode osnovne klase u izvedenoj klasi
// ne sakriva v erz ije iz osnovne klase.
import s ta tic net.mindview.u t i1. Print
class Homer (
char doh(char c) {
p rin t("d o h (ch a r)");
return 'd ';
}
flo a t doh(float f) {
p rin t("d o h (flo a t)" ) ;
return l.O f;
}
Poglavlje 7: Ponovno korišćenje klasa 195
class Milhaus {}
Viciite da su sve preklopljene m eto d e klase Homer d o stu p n e u klasi Bart, iako Bart uvo-
di novu p reklopljenu m e to d u (tim e bi se u C + + - U sakrile m etode osnovne klase). Kao što
ćete p ročitati u n ared n o m poglavlju, m eto de istog im ena se m n o g o češće redefinišu p o m o -
ću p o tp u n o istog potp isa i p o v ratn o g tipa kao i u osnovnoj klasi. Inače rezultati m o g u biti
zbun juju ći (zbog čega to u C++-U i nije dozvoljeno, da ne biste napravili grešku).
U Javi SE5 d o d a ta je an o tacija @Override, što nije rezervisana reč, ali se m ože u p o -
trebljavati kao da jeste. Kada hoćete da redefinišete m eto d u , m ožete d o d ati tu an o tac iju i
prevodilac će prijaviti grešku ukoliko je n e n a m e rn o preklo p ite um esto da je redefinišete:
//: ponovnaupotreba/Lisa.java
// {Compi1eTimeError} (Neće se prevesti)
V ežba 13: (2) N ap rav ite klasu s m e to d o m k oja je prek lo p lje n a trip u t. N eka je nasledi n ova
klasa, dodajte još je d n o p rek lap an je m eto d e i pok ažite d a su sve četiri m eto d e d o stu p n e u
izvedenoj klasi.
//: ponovnaupotreba/Kola.java
// Kompozicija pomoću javnih objekata.
class Motor {
public void s t a r t() {}
public void rik v e rc () {}
public void stop() {}
}
class Tocak {
public void naduvaj(int psi) {}
}
class Prozor {
public void podigni() {}
public void spusti () {}
}
class Vrata {
public Prozor prozor = new Prozor();
public void o tv o ri() {}
public void z a tvo ri() {}
}
public Kola() {
fo r (in t i = 0; i < 4; i++)
to ck o v i[i] = new Tocak();
)
public s ta tic void m ain(String[] args) {
Kola kola = new K o la ();
kola.leva.prozor.podigni( ) ;
kola.tockovi [ 0 ].naduvaj (72);
}
} ///= -
Pošto je k o m p o z iđ ja kola u ovom slučaju deo analize p ro b le m a (a n e sam o d eo osnov-
no g p ro jek ta), proglašavan jem članova jav n im p o m aže te p ro g ra m e ru klijen tu d a shvati
kako d a k oristi klasu, a tim e se i sm an ju je slo žen o st k o d a koji a u to r klase m o ra d a napiše.
Im ajte n a u m u da je ovo specijalan slučaj i d a polja u g lav n o m treb a da b u d u p riv atn a.
Pri nasleđivanju polazite o d postoječe klase i p rav ite n je n u specijalnu verziju. U
o pštem slučaju, to znači d a uzim ate klasu o p šte n a m e n e i specijalizujete je za o d ređ e n e
potrebe. A ko m alo razm islite, uvidečete k ako n em a sm isla d a klasu a u to p rav ite k o m p o -
zicijom o d klase v o zilo - a u to n e sadrži vozilo, o n jeste vozilo. Relacija jesteizražav a se na-
sleđivanjem a relacija sadrži izražava se k o m p o zicijo m .
V ežba 14: (1) U datoteci K ola.java klasi M o to r d o d ajte m e to d u serv is() i pozo v ite je iz
m eto de m a in ().
//: ponovnaupotreba/Ork.java
// Rezervisana reć protected.
import s ta tic net.mindview.uti1. P r in t.*;
class Nitkov {
private String ime;
protected void postavi(String im) { ime = im; }
public N itkov(String ime) { this.ime = ime; }
public String toStringO {
198 Misliti na Javi
Svođenje naviše
N ajbitniji vid nasleđivanja nije o b ezbeđivanje m eto d a n ovim klasam a. N asledivanje
predstavlja relaciju izm ed u nove i osn o v n e klase. Relacija m ože da se sažm e kao „nova
klasa je tipa postojeće klase“.
P reth o d n i opis nije izm išljen sam o da bi se objasnilo nasledivanje - jezik ga d irek tn o
podržava. Na p rim er, p o sm atrajte o sn o v n u klasu p o d im en o m Instrument, i izvedenu
klasu po d im en o m Duvacki. Pošto nasleđivanje podra/.um eva da su sve m etode osnovne
klase d o stu p n e i u izvedenoj klasi, svaku p o ru k u koju m ožete da pošaljete osnovnoj klasi,
m ožete da pošaljete i izveđenoj klasi. Ako klasa Instrument im a m eto d u sviraj(), im aće je
i Duvacki in stru m en ti. To znači da, bez greške, m ožem o reći kako je objek at tipa Duvacki
takođe i tip a Instrument. N aredni p rim e r pokazuje kako prevodilac p održava tu notaciju:
Poglav[je 7: Ponovno korišćenje klasa 199
//: ponovnaupotreba/Duvacki.java
// Nasleđivanje i svođenje naviše.
class Instrument {
public voiđ s v ir a j() {}
s ta tic void melodija(Instrument i) {
/ / •••
i .s v ira j ( ) ;
}
}
Finalni podaci
M nogi p ro g ram sk i jezici im a ju n ačin da p revodiocu naznače kako je neki p o d a ta k ,,kon-
sta n ta n “. K onstante su k o risn e iz dva razloga:
1. M ogu da zad aju k onstantne vrednosti p ri prevođenju koje se nikada neće p ro m en iti.
2. M ogu da predstavljaju v red n o sti koje inicijalizujem o to k o m izvršavanja, a ne želi-
m o da b u d u m enjane.
Poglavlje 7: Ponovno korišćenje klasa 201
// : ponovnaupotreba/Finalni Podaci.java
// Efekat rezervisane reči fin al na polja.
import s ta tic ja v a .u til
import s ta tic net.mindview.util .P r in t.* ;
class Vrednost {
in t i ; // Paketni pristup
public Vrednost(int i) { th is .i = i }
1
public class FinalniPodaci {
private s ta tic Random slucajan = new Random(47);
private String id;
public F inalniPodaci(String id) { th is .id = id }
// Mogu da budu konstante pri prevođenju
private fin al int vrednostJedan = 9;
private s ta tic fin al in t VREDNOST_DVA = 99;
// Tipična javna konstanta:
public s ta tic final in t VREDNOST_TRI = 39;
// Ne mogu da budu konstante pri prevođenju:
private fin al int i4 = slucajan.nextlnt(20);
s ta tic fin al in t INT_5 = slucajan.nextlnt(20);
private Vrednost vl = new Vrednost(11);
private fin al Vrednost v2 = new Vrednost(22);
private s ta tic fin al Vrednost VRE_3 = new Vrednost(33);
// N izovi:
private fin al in t [] a = { 1, 2, 3, 4, 5, 6 };
public String toStringO {
202 Misliti na Javi
reference. (Koliko ja znam , ne postoji način da sam e reference u nizu proglasite finalnim .)
Proglašavanje referenci finalnim čini se m anje korisnim o d proglašavanja prom enljivih
prostog tipa finalnim.
V ežba 18: (2) N apravite klasu koja im a statičn o finalno polje i finalno polje i p okažite raz-
like izm eđ u njih.
// : ponovnaupotreba/PraznoFinalno.java
// "Prazna" finalna polja.
class Poppet {
private int i ;
Poppet(int i i ) { i = i i ; }
1
public class PraznoFinalno {
private final int i = 0; // Inicijalizovana finalna promenljiva
private final int j ; // prazna finalna promenljiva
private final Poppet p; // prazna finalna referenca
// Prazna finalna polja MORAJU da budu in ic i j a l izovana u konstruktoru:
public PraznoFinalno () {
j = 1; // In ic ij a l izovanje praznog finalnog polja
p = new Poppet(l); // I n i c i j a l izovanje prazne finalne reference
}
public PraznoFinalno (in t x) {
j = x; // In icija liz o v a n je praznog finalnog polja
p = new Poppet(x); // In ic ijalizovanje prazne finalne reference
}
public sta tic void main(String[] args) {
new PraznoFinalno ( ) ;
new PraznoFinalno (47);
}
} III--
Finalnim poljim a m o rate da dodeiite v rednosti p o m o ću izraza na m estu definicije ili
u svakom k o n stru k to ru . Na taj način se g aran tu je da su finalna polja uvek in iđjalizov ana
pre upotrebe.
Vežba 19: (2) N apravite klasu s p razn o m finalnom referencom na objekat. U n u tar svih
k o n stru k to ra inicijalizujte prazan finalni elem ent. D okažite da finalni elem ent m o ra biti
inicijalizovan pre u p otreb e i da nakon toga više ne m ože da se m enja.
204 Misliti na Javi
Finalni argumenti
A rgum ente m ožete da pro g lasite fin aln im tak o što ih deklarišete k ao finalne u listi argu-
m enata. To znači da u n u ta r m e to d e n e m ožete m en jati o n o na šta taj a rg u m en t kao refe-
renca ukazuje:
//: ponovnaupotreba/FinalniArgumenti.java
// Upotreba rezervisane reči " fin a l" uz argumente metode.
class Stvar {
public void z a v r ti() {}
}
M etode f() i g() pokazuju šta se dešava kada su arg um enti prostog tipa finalni: argum ent
m ožete čitati, ali ga ne m ožete pro m en iti. To se koristi prvenstveno za prosleđivanje poda-
taka an o n im n im u n u trašn jim klasam a koje čete u poznati u poglavlju Unutrašnje klase.
Finalne metode
Za u p o tre b u finalnih m eto d a posto je dva razloga. Prvi je da se m eto da ,,zaključa“ kako bi
se sprečilo da bilo koja klasa naslednica p ro m en i n jeno značenje. To se radi zbog projekt-
n ih razloga, kada želim o da b u d e m o sigurni kako če p on ašan je m etod e biti neprom enje-
n o p rilikom nasleđivanja, o d n o sn o kako m eto d a ne m ože biti redefinisana.
Ranije se kao dru g i razlog za koriščenje finalnih m eto da navodila efikasnost. U pre-
th o d n im realizacijam a Jave, kada biste m eto d u proglasili finalnom , dozvoljavali ste pre-
vodiocu da sve pozive toj m eto d i u gradi d irek tn o u kod (engl. inline call). Kada bi
prevodilac naišao na poziv finalnoj m etod i, m ogao je (p o svom n a h o đ e n ju ) da preskoči
uobičajen poziv (stavi arg u m en te na stek, skoči na kod m eto de i izvrši ga, vrati se, počisti
arg u m en te sa steka i razm o tri p o v ra tn u v red n o st). U m esto toga, poziv m etode m ogao je
da se zam eni k opijom stv arn o g k oda iz njenog tela. T im e su se elim inisali režijski troškovi
sam og poziva. N aravno, ako je m etod a velika, kod počinje da se povečava i verovatno ne
Poglavlje 7: Ponovno korišdenje klasa 205
biste d o b iti poboljšanje p erfo rm an si, jer b i sva u n ap ređ en ja bila zanem ariva u o d n o su na
vrem e izvršavanja m etode.
U novijim verzijam a Jave, v irtu e ln a m ašin a (ko n k retn o , tehnologije v ru ćih tačaka)
m ože da prep ozna takve situacije i p a m e tn o o d ab ere da li d a k oristi u m etan je k oda na
m estu poziva za finalnu m eto d u , p a više ne treb a da p o m ažete o p tim iz ato ru tako što ćete
koristiti rezervisanu reč fin al (p o p ravilu, to ne b i treb alo da rad ite). U Javi SE5/6, pustite
da se prevodilac i izvršno o k ru žen je (JVM ) staraju o efikasnosti, a m e to d u proglasite fi-
n aln o m sam o ako želite izričito da sprečite n jen o redefinisanje.1
Finalno i privatno
Svaka p rivatna m etod a u klasi im p licitn o je i finalna. Pošto priv atn o j m eto d i ne m ožete
da pristu p ite iz nasleđenih klasa, ne m ožete n i da je redefinišete. P rivatnoj m eto d i m ožete
da dodelite specifikator final, ali njoj to ne daje nikakvo d o d a tn o značenje.
O vo stanje m ože da izazove zab u n u - ako pokušate da redefinišete p riv atn u m eto d u (ko-
ja je im plicitno finalna), to ćete sam o naizgled uspeti, jer prevodilac neće prijaviti grešku:
//: ponovnaupotreba/IluzijaRedefinisanjaFinalnih.java
// Privatnu i l i privatnu finalnu metodu samo naizgled možete da redefinišete.
import static net.mindview.uti1. P r in t.*;
class SadrziFinalne {
// Identično kao i kada sto ji samo "priva te":
private final void f ( ) { p rin t("S a d rz iF in a ln e .f( ) " ) ; }
// Takođe automatski finalna:
private void g() { p rin t("S a d rz iF in a ln e .g ()" ) ; }
Finalne klase
Kada ozn ačite da je cela klasa finalna (stavljajući rezervisanu reč final pre njene definici-
je), saopštav ate da tu klasu ne želite da nasleđ u jete niti dozvoljavate bilo kom e da to učini.
D ru g im rečim a, iz nekog razloga p ro jekat vaše klase nikad se neće m enjati ili iz bezbed-
n o sn ih razloga ne želite da o m o g u ćite nasledivanje.
//: ponovnaupotreba/Preistorijski.java
// Proglašavanje cele klase finalnom.
class MaliMozak {}
Pažljivo sa final
Kada p ro jek tu jete klasu, m ože vam se učiniti lcao d o b ra iđeja da m e to d u proglasite final-
n o m . M ožda p om islite da ionako n em a šanse da iko ikada ređefiniše vaše m etode. Pone-
kad je to zaista tako.
Ipak, pažljivo razm islite. U glavnom je teško pred v id eti kako će se klasa p o n o v o kori-
stiti, n aro čito klase opšte naraene. Ako m eto d u proglasite fin aln o m , sprečićete da vašu
klasu p u te m nasleđivanja u p o treb i neki d ru g i p ro g ram er, i to sam o zato što niste m ogli
zam isliti d a se o n a m ože koristiti na taj način.
S ta n d a rd n a Javina biblioteka je d o b a r p rim er. N aročito klasa V ector iz Jave 1.0/1.1
koja se često koristi i koja je m ogla da b u d e još korisnija da nisu, u im e efikasnosti (što je
gotovo sig u rn o bila zabluda) sve m etode proglašene finalnim . Lako je p retp o stav iti kako
želite da nasledite i redefinišete tako fu n d am en taln o k o risn u klasu, ali su p ro je k tan ti ne-
kako odlučili d a to nije u redu. Iro n ičn o - iz dva razloga. Prvo, klasa S tack je nasledena iz
klase V ector, što govori da Stack icstc V ector, m ada to i nije baš tačno s logičke tačke gle-
dišta. U prkos to m e, sam i pro jek tan ti Jave izveli su tu klasu iz klasc V ector. Č im su n a taj
način napravili Stack, treb alo je da shvate kako su finalne m eto d e previše ograničene.
D rugo, m noge najvažnije m etode klase Vector, kao što su ad d E Ie m e n t() i eIem en tA t(),
sin h ro n izo v an e su (rezervisana reč sy n ch ro n ized ). Kao što ćete videti u poglavlju Paralel-
no izvršavanje, to p rav i značajne režijske troškove koji verovatno poništavaju svu korist o d
toga što su inetode finalne. O va priča p o tv rđ u je teo riju da p ro g ram eri loše p ogađaju m esta
na kojim a treb a o p tim izovati. Zaista je loše što je tako nesp retan projekat ušao u sta n d ard -
nu bib lio teku pa svi m orarn o da se b o rim o s njim . (Na sreću, kontejnerska biblioteka
208 Misliti na Javi
savrem ene Jave zam enjuje ldasu Vector klasom ArrayList, koja se po n aša m n o g o p rim ere-
nije. N ažalost, i dalje do sta n o v ih p ro g ra m a k oriste sta ru k o n tejn ersk u bib lio tek u .)
V ažno je n a p o m e n u ti d a klasa Hashtable, jo š je d n a v ažn a klasa iz sta n d a rd n e bib lio -
teke Jave 1.0/1.1, tiema n ije d n u fin aln u m e to d u . Kao što je već rečen o n a d ru g im m estim a
u ovoj knjizi, priličn o je o čigledno d a su neke klase p ro jek to v ali sasvim različiti Ijudi. (Vi-
dećete i da su im ena m eto d a u klasi Hashtable m n o g o k raća o d im en a u klasi Vector, što
je još je d a n dokaz.) Baš to k o risn icim a b ib lio teke klasa ne bi treb alo d a b u d e očigledno.
Kada stvari nisu dosledne, k o risn ik sam o im a više posla. To go v o ri d a p ro jek a t i k o d treb a
pregledati više p u ta. (U k o n tejn ersk o j b ib lio teci sav rem en e Jave, klasa Hashtable je zam e-
n jen a klasom Hashmap.)
Inicijalizacija i nasleđivanje
Da bism o dobili celovitu sliku o svem u ovom e, p o g le d a jm o ceo proces inicijalizacije
uključujući i nasleđivanje. R azm o trite n are d n i p ro g ram :
//: ponovnaupotreba/Buba.java
// Potpuni proces in ic ija liz a c ije .
import s ta tic net.m indview .util.P r in t.* ;
class Insekt {
int i = 9;
i nt j ;
Insekt() {
' I k o n stru k to r je statična m etoda, m ada se rezervisana re ć s ta tic ne m ora eksplicitno navesti. Dakle,
preciznije je reći da se klasa učitava o n d a kada se pristupi biio kojem njen o m statičnom članu.
Poglavlje 7: Ponovno korišćenje klasa 209
p r in t("i = “ + i + “ , j = " + j ) ;
j = 39;
}
private s ta tic in t xl =
p r in t ln it ( “ in ic ijaliz o v a n a statična promenljiva In s e c t.x l“ ) ;
s ta tic in t p rin tIn it(S trin g s) (
p r in t (s );
return 47;
}
}
poziv se obavlja au to m atsk i, ali tak o đ e m o žete d a o d red ite koji k o n s tru k to r o sn o v n e klase
treb a da b u d e pozvan (prva o p eracija u k o n stru k to ru klase Buba) p o m o ć u rezervisane
reči super. K o n stru k to r osn o v n e klase p ro lazi k roz isti p ro ces i istim red o sled o m kao
k o n stru k to r izvedene klase. N akon obavljene k o n stru k cije o sn o v n e klase, pro m en ljiv e in -
stance inicijalizuju se p o p o retk u pisanja. K onačno, izvršava se o statak tela k o n stru k to ra .
Vežba 23: (2) D okažite da se klasa učitava sam o je d n o m . D o k ažite d a učitavanje m ože da
b u d e p ro u zro k o v an o bilo p ravljenjem prve in stan ce te klase bilo p ris tu p o m statičn o m
članu.
Vežba 24: (2) U dato teci Buba.java, iz klase Buba n asledite specifičnu v rstu b u b e, držeći
se istog fo rm ata kao kod postojećih klasa. P ratite i objasn ite rezu ltat p ro g ra m a .
Sažetak
I nasleđivanje i kom pozicija o m o g u ćav a ju da o d po sto jećih tip o v a n a p ra v ite nove. K om -
pozicija upotrebljav a postojeći tip kao d eo o sn o v n e realizacije n o v o g tip a, a nasleđivanje
pon o v o u potrebljava interfejs.
U nasleđivanju, izvedena klasa o b u h v a ta interfejs o sn o v n e klase, p a m ože da b u d e sve-
dena navišc ka osnovi, što je veom a b itn o za p o lim o rfizam (kao što ćete videti u n ared -
n o m poglavlju).
U prkos veom a n aglašenom n asledivanju u o b jek tn o o rije n tisa n o m p ro g ra m ira n ju , u
p rvo m p ro lazu kroz p ro jek at treb alo b i d a se k o n cen triše te na k o m p o ziciju (ili na delegi-
ranje), a nasleđivanje koristite sam o kada je n eo p h o d n o . K om pozicija u m e da b u d e flek-
sibilnija; Iukavo p rim en ju ju ći nasleđivanje na objekte članove, m o žete im p ro m en iti
tačan tip, a sam im tim i p o n ašan je to k o m izvršavanja. D ru g im rečim a, p o n ašan je objekta
nastalog p o m o ć u kom pozicije m o žete da m en ja te to k o m izvršavanja.
Kada projektujete sistem , vaš cilj je d a p ro n a đ e te ili n ap rav ite sk u p klasa u k om e svaka
klasa im a specifičnu n a m en u . Uz to, n ijed n a klasa ne treb a da b u d e ni prevelika (s toliko
m o gućno sti da postaje n ezg rap n a da bi se p o n o v o koristila) ni p rem ala (toliko da ni vi ne
m ožete da je koristite takvu kakva je već je m o rate p ro širiti). U koliko p ro jek at p o stan e
previše složen, često p o m aže kada d o d a te nove objekte nastale cep an jem p ostojećih na
m anje delove.
Kada počnete da p ro jek tu jete sistem , važno je da shvatite kako je razvoj p ro g ram a p o -
stu p an proces, baš kao i čovekovo učenje. N jegova osnova je ek sp erim e n tisan je; m ožete
b eskrajno dug o da an alizirate stvari, ali i dalje nećete znati sve o d g o v o re kada se u p u stite
u projekat. Im aćete m n o g o više u sp eh a - i više n ep o sred n ih p o d ata k a o rezu ltatim a - ako
,,uzgajate“ svoj projekat kao organsko biće koje se razvija, u m esto da ga o d je d n o m pravite
kao stakleni soliter. N asleđivanje i k o m pozicija sp ad aju m eđ u najvažnije alatke u objekt-
no o rijen tisan o m p ro g ra m ira n ju koje om o g u ćav aju takvo ek sp erim e n tisan je.
R ešenja o d a b ra n ih vežbi d a ta su u e le k tro n sk o m d o k u m e n tu Thinking in Jnva Annotated Solutiini
Guide, koji se m o ž e k u p iti n a VVeb lokaciji www.MindView.net.
Polimorfizam
„Pitali su tne: ’M o lim vas, gospodine Bebidže, recite hoće li iz vaše m ašine izaći tačni odgovori
čak i ako u nju unesetepogrešne brojeve?’N e m ogu da p o jm im k a kva je zbrka u glavam a Ijudi
koji p ita ju tako šta“. Čarls Bebidž (1791-1871)
//: polimorfizam/muzika/Nota.java
// Note koje će se s v ira ti na tnuzičkim instrumentima.
package polimorfizam.muzika
//: polimorfizam/muzika/Instrument.java
package polimorfizam.muzika
import s ta tic net.m indview .util.P r in t.* ;
class Instrument {
public void sviraj(N ota n) {
print(''Instrum ent.sviraj ( ) " ) ;
}
}
III--
//: polimorfizam/muzika/Instrument.java
package polimorfizam.muzika
//: polimorfizam/muzika/Muzika2.java
// Preklapanje umesto svođenja naviše
package polimorfizam.muzika
import s ta tic net.mindview.util .P r in t.* ;
Ovaj p ristu p dovodi d o željenih rezu ltata, ali u z veliku m an u : za svaki novi Instru-
ment koji dodate, m o ra te d a n ap išete p o se b n u m eto d u . To n a sa m o m p o četk u zahteva
više p ro g ram iran ja, ali tak o đ e znači da ćete im ati i p u n o posla ako želite d a d o d a te n o v u
m e to d u sličnu m eto d i m elodija() ili n o v u p o tk lasu klase Instrument. D o d ajte to m e
činjenicu d a prevodilac neće p rijav iti p o ru k u o grešci ako zab o rav ite da p rek lo p ite n eku
m e to d u , p a ceo proces ra d a s tip o v im a p o staje v eo m a nep o d esan .
Z ar n e bi bilo m n o g o lepše ako b iste m o g li d a napišete sam o je d n u m e to d u , čiji b i ar-
g u m en t bila osn ov n a klasa, a ne neka p o seb n o izvedena klasa? O d n o sn o , zar ne bi bilo
fino ako biste m ogli z an em ariti čin jen icu d a su to izvedene klase i m ogli da napišete p ro -
g ram tak o d a se ob raća sam o o sn o v n o j klasi?
P olim orfizam v am om ogućava baš to. M eđ u tim , većina p ro g ra m e ra sa iskustvom u
p ro c e d u ra ln o m p ro g ra m ira n ju m a lo teže ra z u m e n ačin n a koji p o lim o rfizam deluje.
Vežba 1: (2) N apravite klasu Cikl s p o tk lasam a Unicikl, Bicikl i Tricikl. Pokažite da se
p rim e ra k svakog o d tih tipova m e to d o m v o z iti( ) m ože svesti naviše na Cikl.
Začkoljica
P roblem s p ro g ram o m Muzika.java m ože d a se u oči n ak o n p o k retan ja p ro g ram a. Rezul-
tat će b iti poziv m eto d e Duvacki.sviraj(). D o b ijen je željeni rezultat, ali ne izgleda logič-
n o zašto to tak o radi. P o sm atrajte m e to d u melodija():
Svođenje naviše
kroz dijagram
nasleđivanja
K v a d ra t Trougao
o .n a crta j( ) ;
M ogli biste, ponovo, očekivati kako će b iti p o zv an a m eto d a nacrtajO klase Oblik, p o -
što je to ipak referenca n a Oblik - kako bi, o n d a, prev o d ilac m o g ao bilo šta d ru g o da u ra -
di? A ipak, zbog kasnog povezivanja (p o lim o rfizm a) poziv a se o d g o v araju ća m e to d a
Krug.nacrtajO.
N ared n i p rim e r p o kazuje taj p rin c ip n a nešto d rugačiji način . N ajpre ćem o n ap rav iti
v išek ratn o u p otreb ljiv u b ib lio tek u tipo va Oblika:
//: polimorfizam/oblik/Oblik.java
package polimorfizam.oblik;
// : polimorfizam/obli k/Kvadrat.java
package polimorfizam.oblik;
import s ta tic net,mindview.uti1. P rin t.* ;
Prosirivost
V ratim o se sada p rim e ru s m u zičk im in stru m e n tim a . P olim orfizam o m o g u ću je da to m
sistem u d o d ate koliko god želite novih in s tru m en ata, bez p o treb e d a m en jate m eto d u
m eIo d ija (). U d o b ro p ro jek to v an o m O O P p ro g ram u , većina m eto d a ili sve naše m eto d e
p ratile bi m o d el p rim e n jen na m e to d u m eIo d ija() i k o m u n ik aciju obavljale sam o preko
interfejsa o sno v n e klase. Takav p ro g ram je proširiv; jer m ožete da m u d o d ate novu fu nk-
c io n aln o st ako nove tipove p o d atak a nasledite iz zajedničke o sn o v n e klase. M etode koje
obavljaju neki p osao preko interfejsa o sn o v n e klase, u o p šte ne treb a da m en jate da bi pri-
hvatile i nove klase.
R azm o trite šta se dešava ako u p rim e ru sa in stru m e n tim a d o d a m o nove m eto d e u
o sn o v n u klasu, kao i izvestan broj n o v ih klasa. D ijagram hijerarh ije nasleđivanja d a t je na
slici:
Poglavlje 8: Polimorfizam 219
Sve ove nove klase p rav iln o rade sa starom i n e izm en jen o m m e to d o m m elo d ija (). Ć ak
i ako se m eto d a m e lo d ija () nalazi u posebnoj d a to te đ i ako se interfejsu klase In s tru m e n t
d o d a ju n o \re m etode, m e lo d ija () će, bez po no v n o g prev o đ en ja, p rav iln o raditi. Evo i re-
alizacije dijagram a:
//: polimorfizam/muzika3/Muzika3.java
// Pro širiv program
package polimorfizam.muzika3;
import polimorfizam.muzika.Nota;
import s ta tic net,mindview.uti1 .P rin t.*;
class Instrument {
void sviraj(N ota n) { p rin t("In stru m en t.sviraj() " + n ); }
String sta () { return "Instrument"; }
void nastimuj() { p r in t( “ Štimovanje instrumenta"); }
}
N ove su m e to d a sta(), koja v ra ća referen cu n a ob jek at klase String u kojem je o pis kla-
se, i m e to d a nastimuj() koja o m o g u ćav a da se svaki in s tru m e n t n a neki način naštim uje.
Svaki p u t k ada nešto u b acite u niz orkestar u m eto d i m ain(), a u to m atsk i obavljate
svođenje naviše ka klasi Instrument.
M eto da meIodija() je p o tp u n o im u n a na sve izm ene u p ro g ra m u i rad i p ravilno. Po-
lim o rfizam baš to om ogućava. Izm en e u k o d u n e rem ete delove p ro g ra m a n a koje i ne bi
tre b alo da utiču. D ru g im rečim a, p o lim o rfiza m je važna te h n ik a koja p ro g ra m e ru o m o -
gućava da „razdvoji o n o što se m e n ja o d o n o g što ostaje isto“.
Vežba 6: (1 ) P ro m e n ite d a to tek u Muzika3.java tak o d a m e to d a sta() p o stan e m e to d a to-
StringO iz korenske klase Object. P ro b ajte d a objekte klase Instrument ispisujete p o m o -
ću m eto d e System.out.println() (bez ikakve konverzije).
Vežba 7: (2) U d a to te k u Muzika3.java d o d ajte n o v u p o tk lasu klase Instrument i uverite
se da p o lim o rfizam rad i i za n o v i tip.
Vežba 8: (2) Izm enite d a to te k u Muzika3.java tako da objekte klase Instrument p rav i na
slučajan način kao u dato teci Oblici.java.
Vežba 9: (3) N apravite h ijerarh iju nasleđivanja klase Glodar: Mis, Pacov, Hrcak itd.
U o sn o v n o j klasi obezb ed ite m e to d e koje su zajedničke za sve glodare i redefinišite ih u iz-
vedenim klasam a, tako da se različito p o n ašaju za razne tipove glodara. N apravite niz
objek ata klase Glodar, p o p u n ite ga razn im o b jek tim a p otklasa klase Glodar, pozovite
m eto d e definisane u o sn o v n o j klasi i v idite šta se događa.
Vežba 10: (3) N apravite o sn o v n u klasu s dve m etode. Pozovite d ru g u m eto d u iz prve m e-
tode. N asledite tu klasu i redefinišite d ru g u m eto d u . N apravite objekat izvedene klase, sve-
dite ga naviše d o njegovog pro sto g tip a i pozovite prv u m etod u. O bjasnite šta se dešava.
//: polimorfizam/PristupPolju.java
// Neposredno pristupanje polju biva razrešeno u trenutku prevođenja.
class Nad {
public int polje = 0;
public int p rib aviPolje () { return po lje; }
( /* Is p is :
nad.polje = 0, nad.pribaviPolje() = 1
pod.polje = 1, po d.pribaviPolje() = 1, pod.pribaviNadPolje = 0
* / / / :-
Kada se Pod objekat svede naviše na Nad referencu, sva p ristu p an ja po ljim a razrešava
prevodilac, p a o na zato nisu polim o rfn a. U ovom p rim eru , Nad.polje i Pod.polje ne do b i-
jaju isti m em orijski prostor. Z ato Pod zapravo sadrži dva polja Polje: sopstveno i o n o koje
dobija o d klase Nad. M eđ u tim , Nad verzija nije o n a p o d razu m ev an a koja se d o b ija kada
izraz u pu ćuje na polje u klasi Pod; ako hoćete Nad polje, m o rate izričito napisati nad.polje.
Iako m o žd a izgleda da b i ovo m o g lo izazvati zab u n u , u p raksi se to gotovo n ik ad a n e
dešava. Prvo, p o p rav ilu sva p o lja tre b a d a b u d u p riv atn a, p a im nećete p ristu p a ti n ep o -
sredn o , nego sam o kao sp o red n i efekat p o zivanja m eto d a. Uz to, v erovatno nećete d ati
isto im e p o lju osnov n e klase i p o lju izvedene klase, je r biste tako izazvali zbrku.
Ako je m e to d a statična, o n a se n e p o n a ša p o lim o rfn o :
class StaticnaNad {
public s ta tic String s ta tic n a P rib a vi() {
return "Osnovna s ta tic n a P rib a vi( ) " ;
}
public String dinamicnaPribavi() {
return "Osnovna dinamicnaPribavi( ) " ;
}
}
Konstruktori i polimorfizam
K o n struktori se, kao i obično, razlikuju o d ostalih m etoda. To važi i u slučaju p olim orfizm a.
Iako k o n stru k to ri nisu po lim orfni (to su zapravo statične m etode, ali je njihova deklaracija
statičnosti im plicitna), b itn o je da shvatite način na koji o n i rade u složenim hijerarh ijam a
i s polim orfizm om . To znanje će vam p o m o ći da izbegnete n ep rijatn a zapetljavanja.
//: polimorfizam/Sendvic.java
// Poredak poziva konstruktora
package polimorfizam
import s ta tic net.m indview .util.P r in t.* ;
class Obrok {
Obrok() ( p rint("Obrok( ) " ) ; }
}
class Hleb {
Hleb() { p r in t("H le b ()"); }
}
class S ir {
S ir ( ) { p r in t ( " S ir ( ) " ) ; }
}
class Salata {
SalataO { p r in t (" S a la t a ()" ) ; }
}
Poglav[je 8: Polimorfizam 225
Nasleđivanje i čišćenje
K ada n o v u klasu prav ite k o m p o zicijo m i n asledivanjem , uglav n o m nećete m o ra ti da bri-
n ete o ciscenju; p o d o b jek ti se o b ičn o m o g u p rep u stiti sakupljaču sm eća. U koliko čišćenje
ip ak treb a d a obavite, za svoju n o v u klasu m o rate sam i da n ap rav ite n eku m e to d u ci-
scenje() (ako im ate bolje im e, n azovite je drugačije). I p rilik o m nasleđivanja, u izvedenoj
klasi m o rate da redefinišete m e to d u ciscenje() ako je p rilik o m saku p ljanja sm eća p o tre b -
n o nekakvo p o seb n o čišćenje. Kada u izvedenoj klasi redefinišete m e to d u ciscenje(), b it-
no je d a se setite da pozovete verziju te m eto d e iz osn o v n e klase, inače o sn o v n a klasa neće
biti očišćena. To dokazuje n a red n i p rim er:
//: polimorfizam/Zaba.java
// Čišćenje i nasleđivanje.
package polimorfizam;
import s ta tic net.mindview.uti1 .P rin t.*;
class Karakteristika {
private String s;
Karakteristika(String s) {
th is .s = s ;
p rin t( "Pravljen je Karakteristike " + s );
}
protected void ciscen je() {
p rin t("č iš ć e n je Karakteristike " + s ) ; }
}
}
class Opis {
private String s;
Opis(String s) {
th is .s = s ;
p rin t("P ra v lje n je Opisa " + s ) ;
}
prottected void ciscen je() {
p rin t("č iš ć e rje Opisa " + s );
}
}
class ZivoBice {
private Karakteristika p =
new K a rak teristik a("živo j e “ );
private Opis t =
Poglavlje 8: Polimorfizam 227
Svaka klasa u h ijerarh iji sadrži i objekte članove tipova K a ra k te ristik a i O p is, koji ta-
kode m o raju biti očišćeni. R edosled čišćenja treb a da je su p ro ta n red o sled u inicijalizacije,
za slučaj da je d an p o d o b jek at zavisi od dru g o g . Za polja to znači da redosled čišćenja tre-
ba da je su p ro tan redosledu d eklarisanja (p o što se p olja inicijalizuju o n im red o m kojim
su deklarisana). Za o sn o v n e klase (p rateći fo rm u u p o tre b ljen u za d estru k to re u C + + -u )
prv o treba da očistite izvedene klase, a zatim i o sn o v n e klase. R azlog je to što p ri čišćenju
izvedene klase m ožete d a pozovete neke m eto d e iz o sn o v n e klase koje zahtevaju da kom -
p o n e n te osnovne klase jo š b u d u žive, pa ne sm ete da ih u n ištite pre vrem en a. Iz rezultata
p re th o d n o g p ro g ra m a v idite da se delovi objekta Z ab a čiste re d o m su p ro tn im redosledu
kojim su pravljeni.
Iz ovog p rim era vidite sledeće: iako ne m o ra te uvek sam i da čistite, o n d a kada m orate,
taj proces zahteva p ažnju i znanje.
V ežba 12: (3) Izm enite vežbu 9 tako da prikazuje ređosled inicijalizacije u osnovnoj klasi
i u izvedenim klasam a. Sada i osn o v n o j klasi i izvedenim klasam a d o d ajte objekte članove
i pokažite kojim redosledom se inicijalizuju p rilik o m konstru k cije.
Poglav[je 8: Polimorfizam 229
//: polimorfizam/BrojanjeReferenci.java
// Čišćenje deljenih objekata članova.
import s ta tic net.m indview .util.P r in t.* ;
class Deljena {
private in t brojacref = 0;
private s ta tic long brojac = 0;
private fin al long id = brojac++;
public DeljenaO {
p rin t("P ra v l jenje 11 + th is );
}
public void dodajRef() { brojacref++; }
protected void c isce n je() {
if(- - b ro jacre f == 0)
p rint("Č išćenje " + th is );
}
public String toStringO { return "Deljena" + id ;}
class Kompozicija {
private Deljena deljena;
private s ta tic long brojac = 0;
private fin al long id = brojac++;
public Kompozicija(Deljena deljena) {
p rin t("P ra v lje n je " + th is );
this.d eljena = deljena;
th is.d elje n a .d o d a jR ef();
}
protected void ciscen je() {
p rin t("č iš ć e n je " + th is );
del jena.ciscenjeO
}
public String toStringO { return "Kompozicija " + id ;}
for(Kompozicija c : kompozicija)
c .c is c e n je O ;
}
} /* Isp is:
Pravljenje Deljena 0
Pravljenje Kompozicija 0
Pravljenje Kompozicija 1
Pravljenje Kompozicija 2
Pravljenje Kompozicija 3
Pravljenje Kompozicija 4
čišćenje Kompozicija 0
ćišćenje Kornpozicija 1
čišćenje Kompozicija 2
čišćenje Kompozicija 3
čišćenje Kompozicija 4
čiščenje Deljena 0
* ///.-
Statičan brojac tip a long p ra ti bro j n ap rav ljen ih p rim e ra k a o b jek ta tip a Deljena i daje
v re d n o st prom enljivoj id. T ip brojaca je long u m esto int da b i se izbeglo p rek o račen je (u
ovom slučaju, to je sam o d o b ra praksa; v ero v atn o n ijed an p rim e r iz ove knjige neće
p ro u zrok o vati prekoračenje takvog brojača). P rom enljiva id je fin a ln a , zato što ne očeku-
jem o da će m en jati v red n o st to k o in životnog veka ovog objekta.
Kada svojoj klasi p rid ru ž ite deljeni objekat, ne sm ete zabo rav iti da pozovete dodaj-
Ref(), ali će m eto d a ciscenje() p ra titi broj referenci i odlu čiti kada da obavi čišćenje. Ova
teh n ik a zahteva đ o d a ta n tru d , ali ako su objekti koji zahtevaju čišćenje deljeni, nem ate
m n o g o izbora.
Vežba 13: (3) D atoteci BrojanjeReferenci.java d o d ajte m e to d u finalize() da biste p ro-
verili postoji li stanja okončanja (videti poglavlje Inicijalizacija i čišćenje).
Vežba 14: (4) Izm enite vežbu 12 tako da jed an o d objekata ćlanova b u d e đeljen s b ro-
jan jem referenci i pokažite da to ispravno radi.
jed an k o rak u pravljenju objekta klase koja je izvedena iz klase to g k o n stru k to ra , u v rem e
poziva tekućeg k o n stru k to ra izvedeni delovi jo š n isu inicijalizovani. M ed u tim , d in am ičk i
povezan poziv seže ka „spoljašnjosti11h ijerarh ije nasleđivanja. O n poziva m e to d u izvede-
ne klase. Ako ga iskoristite u n u ta r k o n stru k to ra , pozvaćete m e to d u koja m o žd a rad i sa
član ov im a koji još uvek nisu inicijalizovani, što je sig u ran recep t za k atastrofu.
P rob lem m ožete d a sagledate u n a re d n o m p rim e ru :
//: polimorfizam/PoliKonstruktori.java
// Konstruktori i polimorfizam
// ne pružaju ono što možda očekujete.
import s ta tic net.m indview.util.P r in t.* ;
class G lif {
void c r t a j( ) { print ("Gl i f . crta j ( ) " ) ; }
GlifO {
p r in t { " G lif ( ) pre metode c r t a j ( ) " ) ;
c rta j ( ) ;
p r in t ( " G lif ( ) posle metode c r t a j ( ) " ) ;
}
}
P oredak inicijalizacije koji je opisan u ran ijem o deljku n ije baš k o m p le ta n i to je ključ
za rešenje m isterije. Proces inicijalizacije zaista teče na sledeči način:
1. P ro sto r za sm eštan je objekta p o p u n jav a se b in a rn im n u la m a p re nego što se bilo šta
d ru g o odigra.
2 . Pozivaju se k o n stru k to ri o sn o v n ih klasa kao što je ran ije op isan o . N a to m m estu
poziva se redefinisana m eto d a c rta j() (da, pre nego što se pozove k o n stru k to r klase
O k ru g liG lif), koja zbog koraka 1 kao v re d n o st p o lu p reč n ik a d o bija n u lu .
3. Pozivaju se inicijalizatori članova p o red o sled u deklaracije.
4. Izvršava se telo k o n stru k to ra izvedene klase.
Postoji je d n a d o b ra stran a svega ovoga - sve je b arem in icijalizovano n u la m a (ili šta
god n u la značila za o d re đ e n i tip) i nije ostavljeno sa slu čajn o zatečen im v re d n o stim a koje
su beskorisne. To uključuje i reference na objekte koje su u g ra đ e n e u klasu p rek o k o m p o -
zicije i do b ijaju v red n o st n u ll. Stoga, ako takvu referencu zab o rav ite d a inicijalizujete, ja-
viće se izuzetak p rilik o m izvršavanja. Sve d ru g o do b ija v re d n o st nula, što je o b ičn o
siguran znak da ćete u re z u lta tu v ideti grešku.
S d ru g e stran e, treb alo bi da vas p riličn o užasne rezu ltat ovog p ro g ra m a . P o stu p ili ste
logično, a ipak je njegovo p o n ašan je tajan stveno pogrešno, a p revodilac se nije b u n io .
(U ovakvim situ a đ ja m a se C + + p o n aša razb o ritije.) O vakve greške lako m o g u da b u d u
d u b o k o u k o p an e i njihovo pro n alažen je iziskuje p u n o v rem en a.
Z bog toga, d o b a r savet za p isanje k o n stru k to ra giasi: „U rad ite što m an je posla da obje-
kat dovedete u isp rav n o stan je i ako ikako m ožete, izbegavajte da pozivate bilo kakve d ru -
ge m etode u toj klasi“. Sa sig u rn o šću m o žete da pozivate sam o fin aln e m e to d e osn o v n e
klase. (O vo se au to m atsk i o d n o si i na p riv atn e m eto d e, koje su au to m a tsk i i finalne.) O n e
ne m o g u da b u d u redefinisane i stoga ne kriju ovakvu v rstu izn en ađ en ja. N ećete uvek
m oći d a se p rid ržav ate ovog saveta, ali to je ideal kojem tre b a težiti.
V ežba 15: (2) D atoteci P o IiK o n stru k to ri.ja v a d o dajte P ra v o u g a o n iG lif i p okažite p ro -
blem opisan u ovom odeljku.
//: polimorfizam/KovarijantnoVracanje.java
class Zito {
public String toStringO { return "Ž ito "; }
}
class Mlin {
Zito obradi() { return new Zito(); }
}
K ljučna razlika izm eđu Jave SE5 i ran ijih verzija Jave leži u sledećem : ranije verzije bi
naterale redefinisanu verziju m eto d e obradi() da v rati objek at klase Zito, a ne Psenica,
iako je Psenica izvedena iz klase Zito i stoga je i dalje legitim an p o v ra tn i tip. K ovarijantni
p o v ra tn i tipovi o m ogućavaiu specifičniji p o v ra tn i tip Psenica.
//: polimorfizam/Kompozicija.java
// Dinamička promena ponašanja objekta
// pomoću kompozicije (projektni obrazac "stanje")
import static net.mindview.uti1.P r i n t .*;
class Glumac {
publi c voi d glumi( ) ;
234 Misliti na Javi
class Pozornica {
private Glumac glumac = new Sr ec an Gl um ac ();
public void p r om en i() { glumac = new T u za nG lu ma c( ); }
public void kreni () { glumac.glumi (); }
}
Oblik
nacrtajO
obrisi()
7V
O vo m ože da b u d e o zn ačen o kao p o tp u n a relacija ,,je“, jer interfejs klase o d red u je šta
ova h ijerarh ija predstavlja. N asleđivanje g a ran tu je da će bilo koja izvedena ldasa im ati
interfejs osn ovn e klase i ništa m anje. Ako p ratite ovaj dijagram , uočićete da izvedene ldase
tak ođ e neće im ati ništa više osim interfejsa o snovne klase.
To m ože da se p o sm a tra kao p o tp u n a za m en a, jer o bjekti izvedenih klasa m o g u savr-
šeno d a se zam ene o sn o v n o m klasom , pa kada ih koristite ne m o rate o n jim a im ati nika-
kve d o d a tn e inform acije:
Obrati se Obliku
Poruka Krug, Kvadrat, Linija
relacija ,.je" ili novi tip Oblika
Pretpostavimo
da ovo predstavlja
veliki interfejs
„je kao"
Proširivanje
interfejsa
Obrati se objektu
Korisni deo
klase Koristan Poruka
KoriSugfdeo
Ako u ovom slučaju ne obavljate svođenje naviše, to vas neče opterečivati, ali često
m ožete da d o đ e te u situaciju kada tre b a p o n o v o o tk riti taćan tip objekta kako biste mogli
da p ristu p ite njegovim d o d a tn im m e to d am a. U n a re d n o m odeljku p ok azu je m o kako se
to radi.
class Koristan {
public void f() {}
public void g() {}
}
Kao što se viđi s p re th o d n o g dijagram a, klasa Korisniji proširuje interfejs klase Kori-
stan. Pošto je nasleđena, o n a takođe m ože da b u d e svedena naviše ka klasi Koristan. To
m ožete da viđite u okviru inicijalizacije niza x u m etodi main(). Pošto oba objekta u nizu
pripad aju klasi Koristan, za oba m ožete da pozovete m etode f() i g(), a ako pokušate da p o -
zovete m eto d u u() (koja postoji sam o u klasi Korisniji), dobićete grešku p ri prevođenju.
238 Misliti na Javi
U koliko želite da p ristu p ite p ro širen o m interfejsu objekta klase K orisn iji, p rim en ite
svodenje naniže. Ako je tip odgovarajući, o n o će b iti uspešno. U su p ro tn o m , dobićete izu-
zetak tipa C lassC astE xception. Za o b rad u ovog izuzetka nije n e o p h o d n o da pišete poseban
kod, jer o n naznačava p ro g ram ersk u grešku koja je m ogla d a se desi bilo gde u p ro g ram u .
K om en tar - oznaka {Throw sException} - kazuje sistem u za au to m atizo v an o prevođenje i
pakovanje (engl. build) p rim era iz ove knjige kako očekuje da će ovaj p ro g ra m prilikom
izvršavanja generisati izuzetak.
RTTI je više o d o bične konverzije. Postoji, na p rim er, način da u stanovite tip pre nego što
po kušate da obavite njegovo svođenje naniže. K om pletno poglavlje Podaci o tipovim a p o-
svećeno je proučav an ju različitih vidova po d atak a o tip o v im a prilik o m izvršavanja u Javi.
V ežba 17: (2) K oristeći h ijerarh iju C ik l iz p rim e ra 1, m e to d u ra v n o te ž a () d o d ajte klasa-
m a U n icik l i B icikl, ali ne i klasi T ricik l. N apravite p rim e rk e sva tri tip a i svedite ih naviše
n a niz tip a C ikl. Pozovite m e to d u ra v n o te ž a () za svaki elem en t to g niza i p o gledajte re-
zultate. Svedite naniže, pozovite m e to d u ra v n o te ž a () i p o g led ajte šta se dešava.
Sažetak
Po lim o rfizam znači „različiti o blici“. U o b jek tn o o rije n tisa n o m p ro g ra m ira n ju im ate isti
interfejs osn o v n e klase i različite oblike koji u p o treb ljav aju taj interfejs: različite verzije
d in am ičk i pov ezan ih m etoda.
U ov o m poglavlju videli ste da nije m oguće ra zu m e ti niti p rim e n iti p o lim o rfiz a m bez
u p o treb e ap strakcije p o d ata k a i nasleđivanja. P o lim o rfizam je o so b in a koja ne m ože d a se
p o sm a tra izolovano (kao što m ože, n a p rim er, n ared b a svvitch), već sam o kao deo „veće
slike“ relacija izm eđ u klasa.
D a biste u p ro g ra m im a efikasno koristili p o lim o rfizam - a sam im tim i o b jek tn o o ri-
jen tisan e teh n ik e - m o ra te da p ro širite svoje p o im an je p ro g ra m ira n ja. P o red članova i
p o ru k a zasebne klase, u zm ite u o b z ir i zajedničke o so b in e m e đ u klasam a kao i njihove
m eđ u so b n e relacije. Iako to iziskuje značajan n ap o r, o n se i te kako isplati. Postiže se brži
razvoj p ro g ra m a, bolja organizacija koda, pro širiv i p ro g ra m i i lakše od ržav an je koda.
Rešenja odabranih vežbi data su u elektronskom dokum entu Thinking in Java Annotatcd Solution
Guide, koji se može kupiti na lokaciji www.MindView.net.
Interfejsi
Interfejsi i apstraktne klase pružaju strukturiraniji načiti razdvajanja interfejsa od realizacije.
C + + IMA
T a k v i m e h a n i z m i n i s u t a k o C e s t i u p r o g r a m s k i m j e z i c i m a . P r i m e r a RA DI,
sam o p o sred n u p o d ršk u za te koncepte. Č in jen ica da o n i p o sto je u Javi, govori d a su ih
pro je k tan ti jezika sm atrali d ovoljno b itn im da se za n jih ob ezbed i d ire k tn a p o d ršk a kroz
rezervisane reči.
N ajpre ćem o ra z m o triti apstraktne kla seko je su o tp rilik e na pola p u ta izm eđ u o b ičn e
klase i interfejsa. Iako ćete biti skloniji p ravljenju interfejsa, a p stra k tn a klasa je važna i
n e o p h o d n a alatka za pravljenje klasa koje im aju neke nerealizovane m etode. N e m o žete
uvek k o ristiti čist interfejs.
Klasa koja sadrži ap strak tn e m eto d e naziva se apstraktna klasa. Ako kJasa sadrži je d n u
ili više ap strak tn ih m etoda, o n a m o ra biti kvalifikovana kao ap strak tn a . (U su p ro tn o m ,
prevodilac će prijaviti grešku.)
Pošto je a p stra k tn a klasa n e p o tp u n a , šta bi p revodilac treb alo da u rad i kada n eko p o-
kuša da napravi objekat takve klase? Pošto n e m ože da n ap rav i objek at a p strak tn e klase na
siguran način, prevodilac će prijaviti grešku. Na ovaj način se o b ezb eđ u je p o tp u n o s t ap-
stra k tn e klase i vi ne m o rate da b rin ete o n jenoj ev en tu aln o j p o g rešn o j u p o treb i.
A ko a p stra k tn u klasu n asled ite i želite d a p rav ite objekte to g novog tipa, m o ra te da
obezbedite definicije za sve a p stra k tn e m eto d e o sn o v n e klase. U koliko to ne u rad ite (a
m o žete i tako d a o dlu čite), ta d a i izvedena klasa o staje a p stra k tn a , te će vas prevodilac p ri-
siliti i da tu klasu označite rezerv isano m reči a b s tra c t.
A p strak tn u klasu m o žete d a n ap ra v ite i tak o d a ne sadrži n ijed n u a p stra k tn u m eto d u .
To je k o risn o k ad a u klasi n e m a sm isla p ra v iti bilo kakve ap stra k tn e m etode, ali ipak želite
da sprečite pravljenje o b jek ata te klase.
Klasa In s tr u m e n t iz p re th o d n o g poglavlja lako m ože da b u d e p retv o re n a u a p stra k tn u
klasu. Sam o neke m e to d e će b iti a p stra k tn e - ne m o ra ju sve. Evo kako to izgleda:
Interfejsi
Rezervisana reč interface u n a p re đ u je k o n cep t apstrakcije. R ezervisana reč abstract o m o -
gućava da u klasi n ap rav ite je d n u ili više m e to d a koje n em aju definicije - tim e obezbedu-
jete deo interfejsa, ali izostavljate o d g o v araju ću realizaciju, koju će n ap rav iti naslednici te
klase. R ezervisana reč interface o značava p o tp u n o a p stra k tn u klasu, u kojoj uop šte nem a
Poglavlje 9: Interfejsi 243
/ / : interfejsi/muzika5/Muzika5.java
// Interfejsi.
package interfejsi.muzika5;
import polimorfizam.muzika.Nota;
import static net.mindview.util.Print.*;
interface Instrument {
// Konstanta pri prevođenju:
int IZNOS = 5; // statična i finalna
// Ne može da ima definicije metoda, već samo deklaracije:
void sviraj(Nota n); // Automatski javne
void nastimuj();
}
Vežba 7: ( 1) Izm enite vežbu 9 iz pogiavlja Polim orfizam tak o da klasu Glodar p re tv o rite u
interfejs.
Vežba 8: (2) U datoteci polimorfizam.Sendvic.java n a p ra v ite interfejs p o d im e n o m Br-
zaHrana (sa o d o gov araju ćim m eto d am a) i p ro m e n ite Sendvic tako d a realizuje i interfejs
BrzaHrana.
Vežba9: (3) Izm enite p ro g ram Muzika5.java praveći a p stra k tn u klasu koja sadrži zajed-
ničke m eto d e po tklasa Duvacki, Udaraljke i Zicani.
Vežba 10: (3) Izm enite d ato tek u Muzika5.java d o d aju ći interfejs MozeDaSvira. D eklara-
ciju m eto d e sviraj( ) prebacite iz interfejsa Instrument u MozeDaSvira. Izvedenim klasa-
m a dodajte i interfejs MozeDaSvira, u b acujući ga u listu iza rezervisane reči implements.
Izm enite m eto d u m eiodija( ) tako da n jen a rg u m en t b u d e interfejs MozeDaSvira, um esto
interfejsa Instrument.
Potpuno razdvajanje
Kad g od m e to d a rad i s klaso m u m esto sa interfejsom , o g ran ićen i ste na u p o tre b u te klase
i n jen ih podklasa. U koliko biste baš h teli da p rim e n ite tu m e to d u na n ek u klasu koja nije
u toj hijerarh iji, nem ate sreće. Interfejs z n a tn o slabi to ograničenje. Stoga m ožete da piše-
te kod koji je lakše u p o tre b iti više p uta.
Na p rim er, p retp o stav im o da im ate klasu Procesor s m e to d a m a im e ( ) i o b rad i( ) koja
p rim a ulazne podatk e, m odifikuje ih i šalje na izlaz. O sn o v n a klasa se p ro širu je prilik o m
pravljenja više različitih v rsta Procesora. U ovom slučaju, p o d tip o v i Procesora m odifi-
kuju String objekte (vodite raču n a o to m e da k o v arijan tn i m o g u biti p o v ra tn i tipovi, ali
ne i tip o v i arg um enata):
//: interfejsi/klasaprocesor/Primeni.java
package interfejsi.klasaprocesor;
import java.util
import static net.mindview.uti1 .P r i n t .*;
class Procesor {
public String ime() {
return g e t C l a s s O . g e t S i m p l e N a m e O ;
}
Object obradi(Object ulaz) { return ulaz; }
}
return ( (String)ulaz).toLowerCase();
}
}
//: interfejsi/filtri/Talasnioblik.java
package interfejsi.filtri;
248 Misliti na Javi
ne m ožete u p o tre b iti s m e to d o m Primeni.obradi(), iako bi to bilo lepo. U suštini, veza iz-
m ed u Primeni.obradi() i Procesora jača je nego što je p o tre b n o , a to sprečava k o d iz Pri-
meni.obradi() d a se p o n o v o upotreb ljav a ta m o gde bi to trebalo. O b ratite p ažn ju na to da
su i ulazi i izlazi tip a Talasnioblik.
D a je Procesor interfejs, o g ra n ičen ja b i bila to lik o oslabljena d a biste m ogli k o ristiti
m eto d u Primeni.obradi() koja u zim a taj interfejs. Evo m o d ifik o v an ih verzija Procesora
i Primeni:
//: interfejsi/interfejsprocesor/Procesor.java
package interfejsi.interfejsprocesor;
//: in te rf ej si/interfejsprocesor/Primeni.java
package interfejsi.interfejsprocesor;
import static ne t. mi nd vi ew .u ti l.Print.*;
Prvi način na koji m o žete p o n o v o u p o treb iti k o d jeste o n aj kada p ro g ram e ri klijenti
la p išu svoje klase tako d a b u d u usaglašene sa ov im interfejsom ; n a p rim er:
M eđ u tim , često n e m ožete da m o đifik u jete klase koje h o čete da u p o tre b ite . Za elek-
tron ske filtre, na prim er, p ro n a đ e n a je gotova b iblioteka, a nije pravljena nova. U takvim
slučajevim a m o žete u p o tre b iti p ro jek tn i o b razac A dapter (a d a p te r). N avodim o način
kako u A d ap teru pišete k o d koji če preuzeti postojeći interfejs i n ap rav iti interfejs koji
vam je p o treb an :
! ..........A ........
• i i Interfejs n i
: : ............A .......
M ožete da n avedete koliko g o d želite interfejsa - svaki po staje nezavisan tip, ka kojem
m o žete da svedete naviše n o v u klasu. U n a re d n o m p rim e ru p rik a za n a je k o n k re tn a klasa
k oja uz nekoliko interfejsa daje n o v u klasu:
//: interfejsi/Avantura.java
// Više interfejsa
interface MozeDaSeBori {
void boriSe();
}
interface MozeDaPliva {
void p l i v a j O ;
}
interface MozeDaLeti {
void leti ();
}
class AkcioniJunak {
public void boriSe() {}
}
class Heroj extends AkcioniJunak
implements Mo ze D a S e B o r i , MozeDaPliva, MozeDaLeti {
public void plivaj() {}
public void leti () {}
}
novog tip a, m orate da napravite klasu u kojoj postoje definicije svih m etoda. Iako m eto d a
boriSe() nije izričito definisana u klasi Heroj,o n a dolazi u z klasu Akcionijunak, čim e au to -
m atski p ostaje i deo klase Heroj, p a je m oguče n ap rav iti objekat te klase.
U klasi Avantura posto je četiri m eto d e čiji su a rg u m en ti razn i interfejsi k o n k retn e kla-
se. K ada se n aprav i objekat klase Heroj, o n m ože d a se p ro sledi bilo kojoj o d tih m eto d a,
što znači da se m ože svesti naviše ka svakom o d tih interfejsa. To se obavlja b ez p rep rek a
i bez p o se b n o g angažovanja p ro g ram era, zahvaljujući n ačin u n a koji su interfejsi u Javi
p ro jekto vani.
Z a p a m tite da je u g o rn je m p rim e ru po k azan suštinski razlog p o sto jan ja interfejsa:
m o g u č n o st da se obavi svođenje naviše ka više o sn o v n ih tipova. M eđ u tim , d ru g i razlog za
koriščenje interfeisa isti je kao i za korišćenje a p strak tn e osn ov ne klase: d a p ro g ra m e r kli-
je n t ne m o že da n ap ravi objekat te klase i d a b u d e svestan kako je to sam o interfejs.
Z bog toga se javlja pitanje: da li u p o tre b iti interfejs ili a p stra k tn u klasu? A ko o sn o v n u
klasu m ožete da naprav ite bez definicija m eto d a i bez p ro m en ljiv ih , uvek od a b e rite in te r-
fejse u m esto ap strak tn ih klasa. U suštini, ako znate da će nešto b iti osn ov na klasa, p rv o p o -
kušajte d a to napravite kao interfejs. (O to m e ćem o još govoriti u sažetku ovog poglavlja.)
Vežba 12: (2) U datoteci Avantura.java d o dajte interfejs p o d im e n o m MozeDaSePenje
p rate ć i o b lik ostalih interfejsa u njoj.
Vežba 13: (2) N apravite interfejs koji nasleđuju dva d ru g a interfejsa. N eka treći interfejs
nasledi ta dva.:
//: i nterfejsi/HororPredstava.java
// Proširivanje interfejsa nasleđivanjem.
interface Cudoviste {
void za p r e t i ();
}
interface Smrtonosno {
void u b i j ();
//: interfejsi/Sukobljavanjelnterfejsa.java
package interfejsi;
Prilagođavanje interfejsu
Jedan o d najvažnijih razloga za u p o tre b u in terfejsa jeste m o g u ć n o st p o sto jan ja više rea-
lizacija istog interfejsa. U je d n o stav n im slučajevim a, to se ra d i u o b lik u m e to d e koja p ri-
hvata interfejs, a vam a ostavlja d a ga realizujete i m eto d i p ro sled ite svoj objekat.
Stoga se interfejsi o b ičn o u p o treb ljav aju u p re th o d n o s p o m e n u to m p ro jek tn o m
o b rascu Strategy. N apišete m e to d u koja obavlja izvesne operacije, a ta m eto d a p rih v ata i
interfejs koji vi zadate. U suštin i, vi kažete: „M ožeš u p o tre b ljav a ti m o ju m e to d u s bilo ko-
jim o b jek to m koji je usaglašen s m o jim interfejsom ". T im e vaša m e to d a po staje fleksibil-
nija, opštija i upotrebljivija.
N a prim er, k o n stru k to r Java SE5 klase Scanner (o kojoj ćete više sazn ati u poglavlju
Z n akovn i nizovi) p rim a interfejs Readable. V idećete d a Readable nije a rg u m e n t n ijed n e
d ru g e m eto d e u stan d a rd n o j b iblioteci Jave - n ap rav ljen je sam o za Scanner, tako da
Scanner ne m o ra da ograniči svoj a rg u m e n t n a o d re đ e n u klasu. N a taj način , Scanner
m ože da radi s više tipova. A ko prav ite n o v u klasu i h o ćete d a o n a b u d e u p o treb ljiv a s kla-
som Scanner, neka b u d e Readable (čitljiva):
//: interfejsi/SlucajneReci.java
// Realizovanje interfejsa tako đa bude usaglašen sa određenom metodom.
import java.nio.*;
import ja va .u ti l.*;
} /* Ispis:
Yazeruyac
Fowenucor
Goeazimom
Raeuuacio
Nuoadesiw
Hageai kux
Ruqicibui
Numasetih
Kuuuuozog
Waqizeyoy
* ///:-
Interfejs Readable zahteva sam o realizaciju m eto d e read(). U m eto d i read(), argu-
m e n tu tipa CharBuffer dod ajete znakove (to se m o že u ra d iti n a nekoliko načina; v ideti
d o k u m e n ta c iju klase CharBuffer) ili vraćate -1 kada ulaza više nem a.
P retp o stav im o da im ate klasu koja nije realizovala Readable - kako ćete postići da o n a
radi s klasom Scanner? Evo jed n e klase koja pro izv o d i slučajne realne brojeve (s p o k ret-
n im zarezom ):
//: interfejsi/SlucajniDouble.java
import ja v a . u t i l .*;
Fonovo sm o u p o treb ili p ro jek tn i o b razac A dapter, ali se u ovom slučaju prilag o đ en a
klasa m ože nap rav iti nasleđivanjem i realizacijom interfejsa R ead ab le. D akle, koristeći
pseu do višestruk o nasledivanje koje o m ogućava rezervisana reč in terfa ce, napravili sm o
novu klasu koja je i tip a S lu c a jn iD o u b le i tipa R eadable:
//: interfejsi/PrilagodjeniSlucajniDouble.java
// Pravljenje adaptera s nasleđivanjem.
import java.nio.*;
import j a va .u ti1 .*;
Pošto na ovaj način svakoj postojećoj klasi m o žete d o d a ti interfejs, znači da m etoda
koja p rim a interfejs obezbeđuje i n ačin p rilag o đ en ja bilo koje klase za rad s to m m eto -
dom . U to m e leži m oć korišćenja interfejsa u m esto klasa.
V ežba 16: (3) N apravite klasu koja proizvodi niz znakova (objekata tip a ch a r). Prilagodite
tu klasu tako da se m ože u p o tre b iti za ulaz objekta tip a S can n er.
Polja u interfejsima
Pošto su sva polja koja stavite u interfejs au to m atsk i statičn a i finalna, interfejs je korisna
alatka za pravljenje g ru p a k o n sta n tn ih v red n o sti. Pre Jave SE5, to je b io jed in i način da se
postig ne efekat koji daje rezervisana reč e n u m u C -u ili C ++-U . D akle, naići ćete na ova-
kav k o d n apisan p re Jave SE5:
//: interfejsi/Meseci.java
// Upotreba interfejsa za pravljenje grupa konstanti.
package interfejsi;
O b ra tite p ažn ju na to da je i ovde u p o treb ljen uob ičajen i sti! pisanja statičn ih finalnih
polja u Javi, čije su v red n o sti inicijalizovane k o n stan ta m a —sve se piše velikim slovim a,
Poglavlje 9: Interfejsi 259
//: interfejsi/SlucajnePromenljive.java
// Inicijalizacija polja u interfejsu pomoću
// nekonstantnih inicijalizacionih vrednosti
import java.util
Pošto su polja statičn a, biće inicijalizovana kada klasa b u d e prvi p u t u čitana, što će se
desiti kada prvi p u t Jiudete p ristu p ili bilo koni polju. Evo i jed n o stav n o g testa:
//: interfejsi/TestirajSlucajnePromenljive.java
import static n e t .mindview.uti1 .Print.*;
Ugnežđivanje interfejsa
Interfejsi3 m o g u da b u d u u g n ežden i u n u ta r d ru g ih klasa ili d ru g ih interfejsa. T im e se o t-
k riv a nekoliko veom a k o risn ih osobina:
//: interfejsi/ugnezdjivanje/Ugnezdjivanjelnterfejsa.java
package interfejsi.ugnezdjivanje;
class A {
interface B {
void f ();
}
public class Blmp implements B {
public void f() {}
}
private class BImp2 implements B {
public void f() {}
}
public interface C {
void f();
}
class Clmp implements C {
public void f() {}
}
private class CImp2 implements C {
public void f() {}
}
private interface D {
void f();
}
private class Dlmp implements D {
public void f() {}
}
public class DImp2 implements D {
public void f() {}
}
public D uzmiD() { return new DImp2(); }
private D dRef;
public void primiD(D d) {
dRef = d;
dRef.f();
}
interface E {
interface G {
void f ();
}
// Suvišan “public":
public interface H {
void f();
}
void g ( ) ;
// Ne može da bude privatan unutar interfejsa:
//! private interface I {}
}
vatni ugnežđeni interfejs? M ogli biste p retp o stav iti kako o n m ože sam o da b u d e realizovan
kao priv atn a ugnežđena klasa u klasi D lm p, ali n am klasa A.DImp2 pokazuje d a o n takođe
m ože biti realizovan i kao jav n a klasa. Klasa A.DImp2, m ed u tim , m ože da se up otrebljava
sam o kao klasa tog o d ređ en o g tipa. Pošto ne p ostoji način da se spolja vidi kako ona, u
stvari, realizuje privatn i interfejs D, to je realizovanje p riv atn og interfejsa način da n am et-
nete p rim e n u definicija nekog interfejsa, a da p ri to m e ne do d ate i inform aciju o tip u (od-
nosno , tim e nećete dozvoliti svođenje naviše).
M etoda uzm iD() u n o si d o d a tn u d ile m u po p ita n ju p riv atn o g interfejsa: o na je javna
m eto d a koja vraća referencu n a p riv a tn i interfejs. Šta m ožete da u rad ite s p o v ra tn o m
v redno šću ove m etode? U m e to d i m ain() m o žete da uočite nekoliko po ku šaja da se ta p o -
v ra tn a v red n o st u p o treb i, ali se svi o n i završavaju n eu sp eh o m . P o v ratn u v red n o st jed in o
m ožete d a p red ate d ru g o m o b jek tu koji im a ovlašćenja da je koristi - u ovom slučaju,
d ru g o m o b jek tu klase A p rek o m e to d e p rim iD ( ).
Interfejs E po kazuje d a interfejsi m o g u d a b u d u ugn ežđen i jed an u n u ta r drugog. Pra-
vila o interfejsim a - p o seb n o o n o da svi elem en ti interfejsa m o ra ju da b u d u javni - ovde
se strogo p o štu ju , pa je interfejs u g n ežđ en u n u ta r d ru g o g interfejsa au to m atsk i javan i ne
m ože b iti proglašen p riv atn im .
Klasa Ugnezdjivanjelnterfejsa p o k azu je različite načine na koje ugnežđeni interfejsi
m o gu da b u d u realizovani. P rilikom realizovanja interfejsa n e m o rate da realizujete i in-
terfejse koji su u njega u gnežđeni. Takođe, p riv atn i interfejsi ne m o gu da b u d u realizovani
van klase u kojoj su definisani.
U početk u vam se m ože u čin iti da ove m o g u ćn o sti postoje sam o zbog k on sistentno sti
sintakse. U p rin c ip u , kada zn ate da neka m o g u ćn o st postoji, sm a tram da m ožete naići na
m esto na kom e će k o risn o d a vam posluži.
Interfejsi i Proizvođači
Interfejs bi trebalo da posluži kao p u t do više realizacija, a tipičan način pravljenja objekata
usklađenih sa interfejsom jeste p ro jek tn i obrazac Factory M ethod (P roizvodna m etoda).
U m esto da k o n stru k to r pozivate n e p o sred n o , vi pozivate m eto d u za pravljenje proizvod-
n og objekta koji proizvodi realizaciju tog interfejsa - tim e bi, teorijski, vaš kod bio p o tp u -
no izolovan o d realizacije interfejsa, što bi om ogućilo n e p rim etn u zam enu jed n e
realizacije d ru g om . Evo jed n o g p rim era koji će pokazati stru k tu ru obrasca Factory Method:
/ / : interfejsi/Proizvodjaci.java
import static ne t.mindview.util.Print.*;
interface Usluga {
void me t o d a l ( ) ;
void me t o d a 2 ( ) ;
}
interface ProizvodjacUsluga {
Usluga uzmiUslugu ();
}
Poglavlje 9: Interfejsi 263
Da n e m a obrasca Factory M ethod, negde u k odu m orali biste da navedete tačan tip in-
terfejsa U slu g a koji se prav i, da bi on m ogao da pozove odgovarajući k on stru ktor.
Z ašto da čovek do d aje još jed an nivo posredovanja? Jedan od uobičajenih razloga je
pravljenje zajedničkog kostura. P retp o stav im o da p ravite sistem za igranje igara; na pri-
m er, za igranje šaha i dam e na istoj tabli:
//: interfejsi/Igre.java
// Kostur za Igre napravljen pomoću Proizvodnih metoda.
import static net.mindview.uti1.Print.*;
264 Misliti na Javi
}
public static void main(String[] args) {
igraj Igru(new P r o i z v o d j a c D a m a O ) ;
igrajlgru(new Proizvodjacsah());
}
} /* Ispis:
Dama potez 0
Dama potez 1
Dama potez 2
Šah potez 0
Šah potez 1
Šah potez 2
Šah potez 3
* ///:-
Poglavlje 9: Interfejsi 265
Sažetak
Lako je pasti u zam k u i sve interfejse p roglasiti d o b rim pa ih zbog toga uvek treb a p ret-
p ostav iti k o n k re tn im klasam a. N aravno, u m esto svake klase g otovo uvek se m ože n a p ra -
viti interfejs i Proizvođač.
M no gi su podlegli to m iskušenju i n apravili interfejse i Proizvođače gde go d su m ogli.
Razlog: m o žd a će zatrebati dru g ačija realizacija, p a tu ap strak ciju treb a d o d a ti uvek. To se
pretv orilo u svojevrsnu p re ra n u o p tim izaciju p ro jek ta.
Svaku apstrakciju treba da m otiviše o d re đ en a stv arn a p o treb a. Ako vam zatreb aju , in-
terfejse treba da d o d ate tek p rilik o m p o n o v n e p o d ele na proste faktore. N em ojte taj d o-
d a tn i nivo indirekcije stavljati o d m a h i svugde, je r tim e povećavate složenost. D o d atn a
složenost je bitna, i ako m e neko n atera da p ro đ e m kroz n ju sam o da b ih na kraju shvatio
kako je interfejs d o d a t ,,za svaki slu čaj“, a bez prav o g razloga —p o su m n jać u u sve p ro jek te
te osobe.
U m esna sm ern ica je radije koristite klase nego interfejse. Počnite o d klasa, pa ako se
ispostavi da su interfejsi n e o p h o d n i, ponovo sve p o d elite na p roste faktore. Interfejsi su
od lične alatke, ali je lako p reterati s njih o v o m u p o tre b o m .
R e š e n ja o d a b r a n ih v e žb i d a ta su u e le k tr o n s k o m d o k u m e n t u T liitikin g in Java A n n o ta te d S olution
G uide , ko ji sc m o ž e k u p iti n a lo k a c iji w w w .M in d V iew .n et.
Unutrašnje kfase
D efiniciju klase m ožete da stavite u n u ta r definicije neke druge klase. Takva klasa se naziva
u n u tra šn ja klasa (engl. inner class).
U n u t r a Sn j a k l a s a j e v r e d n a , z a t o St o o m o g u č u j e d a k l a s e l o g i č k i G R U PlSETE I DA
k o n tro lišete n jih o v u m e đ u so b n u viđljivost. Važno je shvatiti da su u n u tra šn je klase b itn o
različite o d kom pozicije.
N a p rv i pogled, u n u tra šn je klase izgledaju kao jed n o stav an m eh an iz am za sakrivanje
koda: p rek o n jih u građ u jete klasu u n u ta r drug e klase. N aučičete d a u n u tra šn ja klasa p ru -
ža i više o d toga - o n a je svesna p risustv a klase koja je o k ru žu je i m ože s n jo m d a k o m u -
n icira. Iako su za većin u u n u tra šn je klase sasvim n o v k on cep t, p o m o ć u n jih m ožete da
pišete finiji i jasn iji ko d , m a d a niko n e jam či d a će takav i biti.
D o k učite o u n u tra šn jim klasam a, n jihova sv rh a često nije očigledna, pa navikavanje
n a njih m ože d a p o traje. N akon opisa o sn ov ne sintakse i sem an tike u n u tra šn jih klasa, u
o d eljk u „Z ašto u n u tra šn je klase?“ naći ćete p rim ere koji će o b jasniti p re d n o sti u n u tra -
šnjih klasa.
N akon to g odeljka, u o statk u poglavlja detaljnije je raz m o tren a sintaksa u n u tra šn jih
klasa. Te m o g u ćn o sti su d o d ate rad i p o tp u n o sti jezika, ali vam v ero vatno neće treb ati, na-
ro čito ne isprva. Z ato će vam p o četn i delovi poglavlja zasađ verovatno biti dovoljni, a de-
taljnije istraživanje m ožete sm a tra ti referen tnim m aterijalo m .
U p o treb a u n u tra šn je klase u n u ta r m eto d e p o sa lji() izgleda kao i u p o tre b a bilo koje
d ru g e klase. U ovom slučaju, jed in a važna razlika je to što su im en a u g n ežđ en a u n u ta r
klase P o s iljk a l. U skoro ćete videti da to nije jed in a razlika.
Spoljna klasa će ćešće im ati m e to d u koja v raća referencu n a u n u tra šn ju klasu, kao što
se vidi u m e to d a m a za() i sad r():
//: unutrasnjeklase/Posiljka2.java
// Vraćanje reference na unutrašnju klasu
} /* Ispis:
Tanzanija
* ///:-
Ako želite da o bjek at u n u tra šn je klase n ap rav ite izvan neke n estatičn e m eto d e spoljne
klase, kao tip to g objekta m o rate d a navedete Im eSpoljneKlase.ImeU nutrašnjeKlase, kao
što se vid i u m e to d i m a in ().
Vežba I: (1) N apišite klasu Spoljna koja sadrži u n u tra š n ju klasu Unutrasnja. Klasi Spolj-
na d o d ajte m e to d u koja vraća o b jek at tip a Unutrasnja. U m e to d i m a in ( ) n ap rav ite i ini-
cijalizujte referencu n a o b jek at klase Unutrasnja.
//: unutrasnjeklase/Sekvenca.java
// Čuva niz objekata
interface Selektor {
boolean kraj();
Object t e k u c i ();
void sledeci ();
}
' U ovom pogledu, ugnežđene khise u C + + -u veom a se razlikuju, jer su o n e sam o m ehanizam za sakri-
vanje im ena. U C++-U ne postoji veza sa okolnim objektom niti podrazum evana prava pristupa.
Poglavlje 10: Unutrašnje klase 269
Klasa Sekvenca ob u h v ata sam o n iz fiksne d u žin e elem enata tip a Object. M ožete da
pozovete m e to d u dodaj() kako biste d o d ali n o v elem en t tip a Object na kraj niza (ako u
n je m u im a m esta). Za tražen je elem en ata niza p o sto ji interfejs Selektor. To je p rim e r p ro -
jek tn o g o brasca Iterator (ite ra to r), o kojem ćete više saznati u nastavku knjige. Selektor
om og u ćav a d a p rov erite d a li ste na k raju niza (m eto d a kraj()), da pogledate tekući obje-
k at (m e to d a tekuci()) i da se p o m erite na sledeći elem en t u n izu (m eto d a sledeci()).
Pošto je Selektor interfejs, njega m ogu da realizuju i m noge d ru g e klase na neki svoj na-
čin, a taj interfejs m ože biti arg u m en t m n o g ih m eto d a, čim e se prav i generički kod.
U našem p rim e ru , p riv a tn a klasa SelektorSekvence pruža fu n k cio n aln o st interfejsa
Selektor. U m eto d i main() se, n ak o n pravljenja objekta klase Sekvenca, u taj objekat d o -
daje nekoliko objek ata tipa String. Z atim se poziv an jem m eto d e selektor pravi Selektor
koji se k oristi za p o m e ra n je p o o b jek tu klase Sekvenca i za p ristu p svakom elem en tu.
Na prvi pogled, pravljenje klase SelektorSekvence izgleda p o p u t još jedne u n u tra šn je
klase. P ro u čim o je pažljivije. O b ratite p ažn ju na to da se sve m eto d e - kraj(), tekuci() i
sledeci() —o b raćaju referenci obs koja nije deo klase SelektorSekvence, već je p riv atn o
polje o k oln e sp oljn e klase. U n u trašn ja klasa, m e đ u tim , m ože da p ristu p a m eto d am a i p o-
Ijima spo ljne klase kao da p rip ad a ju njoj. Ta o so b in a m ože da b u d e veom a p o g o d n a, kao
što se vidi u p re th o d n o m p rim e ru .
Z nači, u n u tra šn ja klasa im a au to m atsk i p ristu p članovim a klase koja je okružuje.
Kako se to dešava? U n u trašn ja klasa skriveno čuva referencu na onaj objekat spoljne klase
koji je tu u n u tra šn ju klasu n apravio. P rilikom p ristu p a n ja članovim a spoljne klase up o -
trebljava se ta (skrivena) referenca. Prevodilac se, na sreću, b rin e o svim d etaljim a um esto
vas. Sada k o n ačn o m ožete da shvatite zašto u n u tra šn ja klasa m ože da se n apravi sam o za-
je d n o sa o b jek to m spo ljne klase (kada je, kao što ćete videti, u n u tra šn ja klasa n estatičn a).
Za k o n stru k ciju objekta u n u tra šn je klase p o tre b n a je referenca na objekat spoljne klase i
prevodilac će se p o b u n iti ako toj referenci ne m ože da pristu p i. U većini slučajeva, ovaj
scenario se odigrava bez intervencije p ro g ram era.
Vežba 2: (1) N apravite klasu koja čuva jedan String i im a m eto d u toStringf) koja taj String
prikazuje. D od ajte nekoliko p rim erak a te klase o b jek tu tipa Sekvenca i prikažite ih.
Vežba 3: (1) Izm en ite vežbu 1 tako da Spoljna im a p riv atn o polje tipa String (koje inici-
jalizuje k o n stru k to r), a Unutrasnja m eto d u toStringO koja to polje prikazuje. N apravite
objekat tipa Unutrasnja i p rik ažite ga.
270 Misliti na Javi
//: unutrasnjeklase/TackaThis.java
// Kvalifikovanje pristupa objektu spoljne klase.
public class TackaThis {
void f() { System.out.println("TackaThis.f()"); }
public class Unutrasnja {
public TackaThis spoljna() {
return TackaThis.this;
// Golo "this" bi bilo "this" od klase Unutrasnja
}
}
public Unutrasnja unutrasnja() { return new U n u t ra sn ja (); }
public static void main(String[] args) {
TackaThis dt = new TackaThis();
TackaThis.Unutrasnja dti = d t . u n u tr as nj a( );
dt i. sp oljna().f();
}
} /* Ispis:
TackaThi s .f ()
* ///:-
//: unutrasnjeklase/TackaNova.java
// Neposredno pravljenje unutrašnje klase operatorom .new.
Z nači, ako n em ate o b jek at spo ljn e klase, ne m ožete d a n ap rav ite n i objekat u n u tra šn je
klase, zbog toga što objekat u n u tra šn je klase m o ra biti povezan sa sp o ljn o m klasom . M e-
đ u tim , u koliko prav ite ugtiežđenu klasu (sta tič n u u n u tra šn ju klasu), njoj referenca na
ob jek at spoljne klase nije p o tre b n a .
Evo kako bi se o p e ra to r ,n ew p rim e n io u p rim e ru Pošiljka:
//: unutrasnjeklase/Posiljka3.java
// Pravljenje instanci unutrašnje klase operatorom .new
V ežb a4 : (2) Klasi S ek ven ca.S elek torS ek v en ce d o d ajte m eto d u koja proizvodi referencu
na sp o ljn u klasu Sekvenca.
Vežba 5: (1) N apravite klasu koja im a u n u tra šn ju klasu. U o dvojenoj klasi n ap ravite in-
stan cu u n u tra šn je klase.
//: unutrasnjeklase/Odrediste.java
public interface Odrediste {
String pr oc it aj Oz na ku();
} ///= -
272 Misliti na Javi
//: unutrasnjeklase/Sadrzina.java
public interface Sadrzina (
int vrednost();
1III--
Sada S a d rz in a i O d re d is te p red stav ljaju interfejse d o stu p n e p ro g ra m e ru klijen tu koji
koristi našu biblio tek u klasa. (Setite se d a su svi člano vi interfejsa au to m atsk i javni.)
Kada d obijete referencu n a o sn o v n u klasu ili n a interfejs, m o g u će je da u o p šte ne m o -
žete d a otk rijete stvarni tip objekta, kao što je p rik aza n o u sledećem p rim eru :
//: unutrasnjeklase/ProbnaPosiljka.java
poznaje te ćlanove, niti m ože d a im p ristu p a bez ograničenja. Ka priv atn oj u n u trašn jo j klasi
(ili ka zaštićenoj u n u tra šn jo j klasi ako niste neki o d naslednika), u stvari, ne m ožete d a oba-
vite n i svođenje naniže, je r ne m ožete da p ristu p ite n jen o m im enu , kao što se vidi u klasi
P ro b n aP o siljk a. Privatne u n u trašn je klase stoga o m ogućavaju p ro jek tan tu klase da p o tp u -
no spreči zavisnost o d tip a i da p o tp u n o sakrije p o d atk e o realizaciji. Pored toga, proširiva-
nje interfejsa je beskorisno, iz perspektive p ro g ra m e ra klijenta, je r o n ne m ože da p ristu p i
ostalim m eto d am a koje nisu deo javnog interfejsa. T im e se takođe Javinom prevodiocu
om ogućava da pravi efikasniji kod.
V ežba 6: (2) N apravite interfejs koji sadrži b arem je d n u m e to d u u so pstv eno m paketu.
D o dajte zaštićenu u n u tra šn ju Jdasu koja realizuje taj interfejs. U trećem p ak etu nasledite
svoju kJasu i, u n u ta r m eto d e, v ratite o bjek at zaštićene u n u tra šn je kJase, svodeći naviše na
interfejs to k o m vraćanja.
V ežba 7: (2) N aprav ite Idasu koja im a p riv a tn o p o lje i p riv a tn u m eto d u . N ap rav ite u n u -
tra šn ju klasu čija m eto d a m en ja polje spo ljne kJase i poziva m e to d u spo ljne kJase. U d ru -
goj m e to d i spoljne kJase n a p ra v ite objek at u n u tra šn je ldase i pozo vite n je n u m e to d u , a
zatim pok ažite dejstvo na objekat spo ljn e kJase.
V ežba 8: (2) U tv rd ite d a li spo ljn a ldasa im a p ris tu p p riv a tn im elem en tim a svoje u n u tra -
šnje klase.
Prvi p rim e r prikazu je pravljenje cele klase čija je oblast važenja je d n a m eto d a (um esto
da je oblast važenja d ru g a klasa). To je lokalna unutrašnja klasa:
//: unutrasnjeklase/Posiljka5.java
// Ugnežđivanje klase unutar metode.
Klasa POdrediste je deo m eto d e odr(), a ne deo klase Posiljka5. Z bog toga klasi POd-
rediste ne m o žete da p ristu p ite izvan m eto d e odr(). O b ra tite p až n ju na svođenje naviše
u n ared b i return - m eto d a odr() vraća sam o referencu na o sn o v n u klasu, klasu Odredi-
ste. N aravno, to što je im e klase POdrediste navedeno u n u ta r m eto d e odr() ne znači da
o bjekti klase POdrediste n akon p o v ratk a iz te m eto d e neće biti ispravni.
O b ratite p ažnju na to da ste za im ena u n u trašn jih klasa u svim klasam a u istom poddi-
rek to riju m u m ogli da koristite identifik ato r POdrediste, a da ne đ o đ e d o sukoba im ena.
N ared n i p rim e r p o kazuje kako u n u tra šn ju klasu m ožete da u g nezdite u n u ta r proiz-
voljne oblasti važenja:
//: unutrasnjeklase/Posi1jka6.java
// Ugnežđivanje klase unutar oblasti važenja
/ / : unutrasnjeklase/Posiljka7.java
// Metoda koja vraća anonimnu unutrašnju klasu.
M etoda sad r() k o m b in u je generisanje p o v ratn e v red n o sti i d e fin iđ ju klase koja p red -
stavlja tu p o v ra tn u vrednost! Pored toga, klasa je anotiim na —n em a im e. D a stvari b u d u
jo š gore, izgleda kao da počin jete da pravite n o v objek at klase Sadrzina, ali tad a, p re nego
što d o d e te do tačke i zareza, kažete: „Sam o tren u ta k , sada ću da u b acim definiciju klase“.
276 Misliti na Javi
//: unutrasnjeklase/Posiljka7b.java
// Proširena verzija programa Posiljka7.java
//: unutrasnjeklase/Posi1jka8.java
// Anonimna unutrašnja klasa koja poziva konstruktor osnovne klase.
O dg o varaju ći a rg u m en t sam o p ro sled ite k o n stru k to ru osno v ne klase, kao što je u gor-
njem p rim e ru p a ra m e ta r x pro sleđ en n a re d b o m n ew O m o ta c (x ). Iako je O m o ta c obična
klasa s realizacijom , o n a se u p o treb ljav a i kao zajednički interfejs za svoje izvedene klase:
//: unutrasnjeklase/Omotac.java
public class Omotac {
private int i;
Poglavlje 10: Unutrašnje klase 277
public 0motac(int x) { i = x; }
public int vrednost{) { return i; }
} ///:-
P rim etili ste kako O m o ta c im a k o n stru k to r koji zahteva a rg u m en t, da bi bilo m alo za-
nim ljivije.
Z n ak tačka i zarez n a k raju a n o n im n e u n u tra šn je klase n e označava kraj tela klase. O n
označava kraj izraza koji u d ato m slučaju sadrži i a n o n im n u klasu. D akle, u p o tre b a tačke
i zareza p o tp u n o je ista kao i bilo gde d ru g d e u p ro g ram u .
Inicijalizaciju o b jek ta a n o n im n e klase m o žete d a obavite n a m e stu n a k o m e definišete
polja:
//: unutrasnjeklase/Posi1jka9.java
// Anonimna unutrašnja klasa koja obavlja inicijalizaciju.
// Kraća verzija datoteke Posiljka5.java.
I I : unutrasnjeklase/KonstruktorAnonimne.java
// Pravljenje konstruktora za anonimnu unutrašnju klasu.
import static n e t .mindview.uti 1 .P r in t.*;
abstract class Osnovna {
public Osnovna(int i) {
print("Konstruktor klase Osnovna, i = " + i);
}
public abstract void f ();
}
278 Misliti na Javi
//: unutrasnjeklase/PosiljkalO.java
// Upotreba "inicijalizacije instanci" za konstrukciju
// anonimne unutrašnje klase.
/ / : unutrasnjeklase/Proizvodjaci.java
import static net.mindview.uti1 .Prin t .*;
interface Usluga {
void m e t o d a l ();
void m e t o d a 2 ( ) ;
}
interface ProizvodjacUsluga {
Usluga uzmiUslugu ();
}
//: unutrasnjeklase/Igre.java
// Kostur za Igre napravljen pomoču unutrašnjih klasa.
import static net.mi nd vi ew .u ti l.Print.*;
Ugnežđene klase
A ko vam n e treb a veza izm eđu n jen ih o b jek ata i o b jek ata sp oljn e klase, u n u tra šn ju klasu
m ožete da proglasite statičn o m . O n a se o b ičn o naziva ugnežđena klasa2. D a biste shvatili
značenje kvalifikatora sta tic kada se p rim e n i na u n u tra šn je klase, setite se da objekat obič-
ne u n u tra šn je klase im p licitn o čuva referencu n a o b jek at spoljn e klase koja ga je napravila.
Kada kažete da je u n u tra šn ja klasa statičn a, ovo ne važi. U gnežđena klasa znači da:
1. Za pravljenje o b jek ta u g n ežđ en e klase nije p o tre b a n o b jek at spo ljne klase.
2 . Iz objekta u g n ežđ en e klase ne m o žete d a p ristu p ite o b jek tu sp o ljn e klase.
U gnežđene klase se još p o n e č em u razlik u ju o d n e statič n ih , tj. o b ič n ih u n u tra šn jih
klasa. Polja i m e to d e nestatičn e u n u tra šn je klase m o g u d a b u d u sam o n a sp o ljn o m n iv ou
klase, o d n o sn o n estatičn a u n u tra šn ja klasa ne m o že d a im a statič n e p o d atk e , statična
polja niti statičn e u n u tra šn je klase. M eđ u tim , u g n e žđ e n e (statičn e u n u tra šn je ) klase
m o g u sve to da sadrže:
//: unutrasnjeklase/Posiljkall.java
// Ugnežđene klase (statične unutrašnje klase)
2 O tprilike slično u gnežđ enim klasam a u C + + -u , sem što te klase ne m o gu p ristu p a ti priv atn im čla-
n ov im a kao u Javi.
Poglavjje 10: Unutrašnje klase 283
//: unutrasnjeklase/KlasaLIInterfejsu.java
// {main: K1asaUInterfejsu$Test}
//: unutrasnjeklase/PristupPriVisestrukomllgnezdjivanju.java
// Ugnežđene klase mogu da pristupe svim članovima
// na svim nivoima klasa u koje su ugnežđene.
class PVU {
private void f() {}
class A {
private void g() {}
public class B {
void h() {
g();
f0;
}
}
}
}
public class PristupPriVisestrukomUgnezdjivanju {
public static void m a i n (String[] args) {
PVU pvu = new P V U () ;
PVU.A pvua = pvu.new A();
PVU.A.B pvuab = pvua.new B();
pvuab.h();
}
} ///= -
D a b ism o ovo d etaljnije p ro u čili, ra z m o trim o situ aciju u kojoj im a m o dva interfejsa
koja nekako m o ra m o d a realizu jem o u jed n o j klasi. Z b o g fleksibilnosti interfejsa m o žete
da b irate je d n u o d dve m o g u ćn o sti: je d n a klasa ili u n u tra šn je klase:
interface A {}
interface B {}
class X implements A, B {}
class Y implements A {
B napraviB() {
// Anonimna unutrašnja klasa:
return new B() {};
}
}
public class Visestrukilnterfejsi {
static void uzimaA(A a) {}
static void uzimaB(B b) {}
public static void main(String[] args) {
X x = new X ();
Y y = new Y ( ) ;
u z i m aA (x );
uz i m a A ( y ) ;
uz i m a B ( x ) ;
uz im aB (y .n ap ra vi B( ));
}
} lll--~
//: unutrasnjeklase/VisestrukaRealizacija.java
// Ako su u pitanju konkretne ili apstraktne klase unutrašnje klase su
// jedini način za dobijanje efekta "višestrukog nasleđivanja realizacij e " .
package unutrasnjeklase;
class D {}
abstract class E {}
class Z extends 0 {
E napraviE() { return new E() {}; }
}
Poglavlje 10: Unutrašnje klase 287
//: un utrasnjeklase/PovratniPozivi.java
// Upotreba unutrašnjih klasa za povratne pozive
package unutrasnjeklase;
import static n e t . mi nd vi ew .u ti l.Print.*;
interface MozeDaSePoveca {
void povecaj();
}
class MojaPovecaj {
public void povecaj() { print("Druga operacija"); }
static void f(MojaPovecaj mp) { m p . p o v e c a j (); }
}
MozeDaSePoveca uzmiReferencuZaPovratm'Poziv() {
return new Z a k l j u c a k O ;
class Poziva {
private MozeDaSePoveca referencaZaPovratniPoziv;
Poziva(MozeDaSePoveca cbh) { referencaZaPovratniPoziv = cbh; }
void kreni() { referencaZaPovratniPoziv.povecaj (); }
}
Ovaj p rim e r tak ođe po k azu je d o d a tn e razlike izm eđ u realizovanja interfejsa u spoljnoj
i u n u tra šn jo j klasi. U pogled u pisanja p ro g ra m a , klasa Pozvanal je o čigledno jed n o stav -
nije rešenje. Klasa Pozvana2 nasleđuje klasu MojaPovecaj u o k v iru koje već p o sto ji m eto -
da povecaj( ) čiji zadatak n em a nikakve veze sa o n im koji o ček u je m o o d interfejsa Povecaj.
Kada klasu Pozvana2 nasled im o iz klase MojaPovecaj, n jen u m e to d u povecaj( ) ne m ože-
m o da redefinišem o tako da m ože đa se k oristi s in terfe jso m MozeDaSePoveca, pa m o rate
da ob ezbed ite p o seb n u realizaciju p o m o ću u n u tra šn je ldase. O b ra tite p až n ju na to da p ra-
vljenjem u n u tra šn je klase n iti p ro širu jete niti m en jate interfejs spo ljn e klase.
U klasi Pozvana2 sve je p riv a tn o osim m e to d e uzm iReferencuZaPovratniPoziv( ). Da
biste om o gućili bilo kakvu vezu sa sp o ljašn jim sv eto m , n e o p h o d a n vam je interfejs Mo-
zeDaSePoveca. U ovom p rim e ru v id ite kako v am Javini interfejsi p o m a ž u da p o tp u n o
razdvojite interfejs i realizaciju.
U n u trašn ja klasa Zakljucak sam o realizuje interfejs MozeDaSePoveca da bi obezbedila
p o v ratn u vezu ka klasi Pozvana2 - ali sig u rn u p o v ra tn u vezu. Svako ko d o b ije referencu na
290 Misliti na Javi
//: unutrasnjeklase/upravljac/Upravljac.java
// Generički kostur za sve upravljačke sisteme.
package unutrasnjeklase.upravljac;
import j a va .u ti1 .* ;
if(d.spreman()) {
S y s t e m . o ut .p ri nt ln (d);
d.akcija();
1 i s t a D o g ad ja ja .r em ov e(d );
}
}
} III--
M eto d a pokreni( ) k ru ži k ro z k o p iju listeDogadjaja, tražeći Dogadjaj koji je spre-
man( ) za p o k re tan je . Z a svaki Dogadjaj koji je spreman( ), isp isuju se p o d aci objek-
tov om m e to d o m toStrin g( ) i poziv a m eto d a akcija( ). Z atim se Događjaj uklanja iziiste.
U viđate d a za sad a ne zn ate n išta o to m e šta tačno klasa Dogadjaj radi. To i jeste srž
ovog p o stu p k a; k ako „razdvaja o n o što se m en ja o d o n o g što o staje isto “. O d n o sn o , da is-
k o ristim svoj te rm in , „vektor p ro m e n e “ p red stav lja različite akcije različitih v rsta objeka-
ta klase Dogadjaj koje definišete praveći različite p o tk lase za dogadaje.
N a o v o m m e s tu n a scen u stu p a ju u n u tra šn je klase. O n e o m o g u ća v a ju dve stvari:
1. C elu realizaciju p ro g ram a koji upotreb ljav a k o stu r u pravljanja m ožete da sm estite u
je d n u klasu, čim e kap su lirate sve što je za tu realizaciju p o treb n o . U n u trašn je klase se
up o treb ljav aju d a iskažu više različitih akcija koje su p o tre b n e za rešavanje problem a.
2. U n u tra šn je klase p o je d n o sta v lju ju o v u realizaciju, a vi ste u m o g u ć n o sti d a lako
p ristu p ite č la n o v im a sp o ljn e klase. Bez ov ih m o g u ćn o sti, p ro g ra m bi m ogao da
p o sta n e to lik o k o m p lik o v an a a biste m o rali d a p o tra ž ite altern ativ u .
R a z m o trim o je d n u realizaciju k o stu ra u p rav lja n ja k oja u p rav lja fu n k cijam a staklene
bašte.4 Svaka akcija je p o tp u n o različita: paljen je svetla, uklju čiv an je vode i term o stata,
uključivanje zv o nca i p o n o v n o p o k re ta n je sistem a. K o stur u p rav ljan ja o m o g u ću je d a se
taj različiti k od lako razdvaja. U n u tra šn je klase o m o g u ćav aju da p o m o ć u sam o jed n e kla-
se n a p ra v ite više izvedenih verzija o sn o v n e klase D o g a d ja j. Za svaki tip akcije napravićete
nov u u n u tra š n ju klasu izvedenu iz klase D o g ad ja j i k o d za u p rav ljan je n apisati u n u ta r
m e to d e ak cija( ).
Kao što je za k o stu r u p rav ljan ja u o b ičajen o , klasa U p ra v Ija n je S ta k le n o m B a sto m je
izvedena iz Idase U p rav ljac:
P rom enljive svetlo, voda i term ostat p rip a d a ju sp o ljn o j klasi UpravljanjeStaklenom-
Bastom, a ipak u n u tra šn je klase m o g u d a im p ristu p e bez u p o tre b e k valifikatora i bez p o -
sebn ih dozvola. U stv a rn o m p ro g ra m u , većina m eto d a ak cija( ) bi uključivala n eku vrstu
up rav ljan ja h ard v ero m .
Poglavlje 10: Unutrašnje klase 295
Većina klasa izvedenih iz klase Dogadjaj slične su, ali se klase Zvono i Restart izdvajaju.
Klasa Zvono pokreće zvonce i u listu događaja dodaje još jed an objekat klase Zvono, tako
da će p o n o v o zazvoniti nešto kasnije. O b ra tite p a žn ju na to da u n u trašn ja klasa izgleda go-
tovo kao v išestruko nasleđivanje: klasa Zvono im a sve m eto d e kao i klasa Dogadjaj, a ta-
kođe izgleda kao da im a i sve m eto d e spoljne klase UpravljanjeStaklenomBastom.
Klasa Restart do b ija niz objek ata Dogadjaj koje d od aje upravljaču. Pošto je Restart
sam o jo š je d n a v rsta klase Dogadjaj, u m eto d i Restart.akcija( ) u listu takođe m ožete da
d o d a te i o bjekat klase Restart, kako bi se sistem s v rem en a n a v rem e po no vo p o k ren u o .
N a red n a klasa kon fig u riše sistem tako što p rav i o bjek at klase UpravljanjeStaklenom-
Bastom i d o d ajte razn e v rste obj'ekata Dogadjaj. O vo je p rim e r p ro je k tn o g ob rasca C om -
m a n d (n a re d b a ) - svaki o b jek at klase listaDogadjaja jeste zah tev kap su liran kao objekat:
c la s s ImaUnutrasnju {
cla s s Unutrasnja {)
1
public c la s s NaslediUnutrasnju extends Im aUnutrasnju.Unutrasnja {
//! N aslediU nu trasn ju() { } // Ne može da se prevede
NaslediUnutrasnju(Im aUnutrasnju iu ) {
iu .su p e rO ;
}
p ub lic s t a t ic void m a in (S trin g [] args) {
ImaUnutrasnju iu = new ImaUnutrasnju ( ) ;
NaslediUnutrasnju i i = new N a s le d iU n u tra s n ju (iu );
}
} III--
Poglavlje 10: Unutrašnje klase 297
c la s s J a j e {
p riv a te Zumance z;
protected c la s s Zumance {
p u b lic Zumance() { p r in t("Ja je .Z u m a n c e ()" ) ; }
}
public J a j e ( ) {
p r i nt("Novo J a j e ( ) “ ) ;
z = new Zumancef);
}
}
c la s s Ja je 2 {
p rotecteđ c la s s Zumance {
p u b lic Zumance{) { p rin t("Ja je 2 .Z u m a n c e ()" ) ; }
p u b lic void f ( ) { p r in t("Ja je 2 .Z u m a n c e .f()" ) ; }
}
p riv a te Zumance z = new ZumanceO;
publ ic J a je 2 () { print("N o vo J a j e 2 ( ) " ) ; }
p u b lic void ubaciZumance(Zumance zz) { z = zz; }
p u b lic void g () { z . f ( ) ; }
}
in te rfa c e Brojac {
in t n e x t();
return new L o k a ln iB ro ja c ( ) ;
}
// Is to sa anonimnom unutrašnjom klasom:
B rojac L o k a ln iB ro ja c 2 (fin a l Strin g ime) {
return new B ro ja c () {
// Anonimna unutrašnja klasa ne može imati imenovani
// konstruktor, nego samo i n i c i j a l i z a t o r in stan ce:
{
pri nt ("B r o ja c O " ) ;
}
public in t nex t() {
p rin tn b (im e ); // Pristu p an je lo k aln o j fin a ln o j
return broj++;
}
};
}
p u b lic s t a t ic void m a in (S trin g [] args) {
LokalnaUnutrasnjaKlasa l i c = new Lo k a ln aU n u trasn jaK la sa();
300 Misliti na Javi
Brojac
cl = 1 ic.uzmiBrojac("Lokalna unutrašnja "),
c2 = 1 ic.uzmiBrojac("Anonimna unutrašnja ");
for(int i = 0; i < 5; i++)
p r i n t( cl .n ex t( ));
for(int i = 0 ; i <5; i++)
p r in t( c2 .n ex t( ));
}
} /* Ispis:
LokalniBrojac()
Brojac()
Lokalna unutrašnja 0
Lokalna unutrašnja 1
Lokalna unutrašnja 2
Lokalna unutrašnja 3
Lokalna unutrašnja 4
Anon im na unutrašnja 5
Anonimna uriutrašnja 6
An on im na unutrašnja 7
Anon im na unutrašnja 8
Anonimna unutrašnja 9
* ///:-
Bro jac.class
LokalnaUnutrasnjaKlasa$l. c l ass
Lokal naUnutrasnjaKlasa$lLokalniBrojac.class
LokalnaUnutrasnjaKlasa.klasa
Poglavlje 1 Unutrašnje klase 301
Sažetak
Interfejsi i u n u tra šn je Jdase n a p re d n iji su k o n cep ti o d on ih ii p o sto je u m n o g im O O P
jezicim a. N a p rim er, u C ++-U ne p o sto ji ništa nalik njim a. ( i zajed no rešavaju isti p ro -
b lem koji je i C + + p o k u šao d a reši p o m o ć u v išestru ko g nasieđivanja. Isp ostavilo se d a se
u C + + -u v išestru k o nasleđivanje p riličn o teško upotreb ljava, d o k su Javini in terfejsi i
u n u tra š n je Iđase m n o g o p ristu p ačn iji.
Iako su te m o g u ć n o sti p riličn o jasne, n jih o v a u p o tre b a je 1 f anje pro je k to v an ja, p o p u t
p o lim o rfiz m a. S v re m e n o m ćete sve b olje p rep o zn av ati situa c u k o jim a tre b a da k ori-
stite in terfe js ili u r.u tra šn ju klasu, ili i je d n o i d ru g o isto v re ire U o v o m tre n u tk u , bilo
bi d o b ro d a p o zn ajete b a re m n jih o v u sin tak su i sem an tik u. 1 o se b u d e te sretali sa nji-
m a, sve više ćete ih prih v atati.
Rešenja odabranih vežbi data su u elektronskom dokum entu Thin 1ln Java A n n o ta ted S olu-
tions G uide, koji se može kupiti na lokaciji www.MindView.net.
MojaKlasa referenca
po što n ik a d a nećete zn ati koliko će vam o b jek ata zaista treb ati.
Većina jezika im a neki n ač in za rešavanje ovog tem eljn o g p ro b le m a. Java n u d i nekoli-
ko n ačin a za ču vanje o b jek ata (o d n o sn o , referenci n a o b jek te). U sam jezik je u g ra đ en tip
niza, o k o m e je već bilo reči. N iz je najefikasniji n ačin ču vanja g ru p e o b jek ata i p re p o -
ru č u je se za čuvanje g ru p e p ro stih tip o v a. Ali n iz im a n e p ro m e n ijiv u v eličinu, a u o p šti-
je m slučaju, u tre n u tk u p isan ja p ro g ra m a vi n e zn ate koliko o b jek ata će vam biti
p o tre b n o , niti treb a li v am n eki n a p re d n iji n ačin sklad išten ja objek ata; zato je n ep ro -
m enljiva veličina niza pretešk o og ran ičen je.
Za rešavanje ovog p ro b lem a, b ib lio tek a Javinih u slu žn ih klasa im a p rilič n o zao k ru žen
sk u p kontejnerskih k'lasa, čiji su o sn o v n i tip o v i L ist, Set, Q u e u e (za redove za čekanje) i
M ap. O vi tip o v i objek ata p o z n a ti su i p o d im e n o m klase kolekcija, ali p o što se naziv C ol-
le c tio n u Javinoj biblioteci k o risti za ozn ačav an je o d re d e n o g p o d sk u p a te biblioteke, ko-
ristiću precizniji p ojam ,,k o n tejn e r“. K o n tejneri o m o g u ćav aju n a p red n ije način e za
sm eštan je o b jek ata i p o m o ć u njih m o žete d a rešite izn e n a đ u ju ć e veliki bro j p ro b lem a.
P ored o stalih svojih obeležja —na p rim er, Set (sk u p ) ne m o že da sađrži p o n o v ljen e ele-
m en te, a M ap (m ap a) je asocijativan niz koji služi za ču vanje p aro v a m e đ u so b n o asocira-
n ih objek ata - Javine k o n tejn ersk e klase a u to m a tsk i podešavaju svoju veličinu. Stoga, za
razliku o d nizova, u k o n te jn e r m o žete staviti pro izv o ljan broj p ro izv o ljn o velikih objeka-
ta; d o k pišete p ro g ram , ne m o ra te zn ati koliki k o n te jn e r treb a da n ap rav ite.
Iako u Javi n e m a ju d ire k tn u p o d ršk u kroz rezervisane reči,' k o n tejn ersk e klase su jed-
na o d n ajsn ažnijih alatki za p ro g ra m ira n je , je r p rim e tn o sk raću ju v re m e pisan ja p ro g ra-
m a. U ovom poglavlju steći ćete o sn o v n a zn an ja p o tre b n a za rad s Javinom b ibliotekom
k o n tejn era u tip ič n im p rim e n a m a . B avićem o se k o n te jn e rim a koje ćete u p o treb ljav ati u
sv ak o d n ev n o m p ro g ra m ira n ju . Kasnije, u poglavlju D etaljno razm atranje kontejnera,
u p o z n a ć e m o vas sa ostalim k o n te jn e rim a i s p o je d in o stim a o njih o v o j fu n k c io n a ln o sti i
n ač in im a korišćenja.
U građenu p o dršk u za kontejnere im aju b ro jn i jezici, m eđu kojim a Perl, P vthnn i Ruby.
Poglavlje 11: Čuvanje objekata 303
c la s s Jabuka (
p riv a te s t a t ic long b ro jac;
p riv a te fin a l long id = brojac++;
p ub lic long id ( ) { retu rn id ; }
}
c la s s Narandza {}
O v d e bi d o b r o d o š lo p re k la p a n je o p e ra to ra . K o n te jn e rsk e k la se u je z ic im a C + + i C # p ro iz v o d e č istiju
s in ta k s u z ato što se u p o tre b lja v a p re k la p a n je o p e r a to r a .
304 Misliti naJavi
( ( Ja b u k a )ja b u k e .g e t (i)). i d ( ) ;
// Narandza se o tk riv a tek p rilik o m iz vršavan ja
}
} /* (Po k re n ite da b is te v id e li r e z u lta t) * / / / ■ -
p ublic c la s s JabukelNarandzePoinocuGenerickihTipova {
p ub lic s t a t i c void m a in (S trin g [] args) {
ArrayList<Jabuka> jabuke = new A rrayList< Ja b u k a > ();
f o r ( i n t i = 0 ; i < 3 ; i++)
jabuke.add(new Ja b u k a O );
// Greška koja se p r i j a v l j u j e u vreme prevođenja:
// jabuke.add(new N arandzaO );
f o r ( i n t i = 0; i < j a b u k e .s iz e ( ) ; i++)
S y ste m .o u t.p rin tl n (jab uk e.g et ( i ) . i d ( ) ) ;
// Sintaksom foreach:
fo r(Jab u k a c : jabuke)
S y s t e m .o u t .p r in t ln ( c .id ( )) ;
}
} /* Is p is :
0
1
2
0
1
2
* ///:-
Sada će p revo d ilac sprečiti stavljenje o b jek ta tip a N a ra n d /a m eđ u ja b u k e , p a će se ta
greška p rijav iti u v rem e p rev o đ en ja, a ne u v rem e izvršavanja.
O b ra tite p až n ju i na to da p rilik o m v ađ en ja stavki iz Liste vi e nije p o tre b n o svođenje
tip o v a. P ošto Lista zna koji tip sadrži, o n a obavlja svođenje k a<ta vi pozovete g et(). D akle,
ne sa m o da p rev o d ilac zbog gen eričk ih tip o v a p roverava tip o b jek ata koji stavljate u ko n -
tejn er, nego je i sintaksa p rilik o m u p o tre b e objek ata u n jem u ćistija.
P rim e r p o k azu je i sledeće: ako v am ne tre b a in d ek s svakop (’lem en ta, za izb o r svakog
ele m e n ta u Listi m ožete u p o tre b iti sin tak su foreach.
K ada tip o b jek ta u k o n te jn e ru zadate kao g enerički p ara m t ■ir, ne m o ra te u k o n te jn e r
stavljati sa m o taj tip, p o što svođenje naviše rad i i s gen eričk im p o v im a kao i sa o stalim
tip o v im a:
//: cuvanje/GenerickiTipoviISvodjenjeNavise.java
import java.util
Osnovni pojmovi
Kontejnerska b iblioteka Jave 2 obavlja z ad a tak „čuvanja objek ata" i deli ga u dva zasebna
pojm a, izražena u o b lik u o sn o v n ih in terfe jsa bibliote'ke:
1. Collection (kolekcija): g ru p a p o je d in a č n ih e lem en ata na koje je često p rim en jen o
jedno ili više pravila. List (lista) m o ra d a čuva elem ente u o d re đ e n o m redosledu, Set
(skup) ne m ože da sadrži p o n o v ljen e elem ente, a Queue (red) daje elem ente red o m
koji zadaje disciplina čekanja (o b ičn o istim red o m kojim su elem enti u m etan i).
2. Map (mapa): g ru p a p aro v a o b je k a t-k lju č , koja o m o g u ćav a p ro n alažen je objekta
p om oću njegovog ključa. ArrayList o m o g u ćav a p ro n alažen je objekta p o m o ć u nje-
govog indeksa, pa n a neki n ačin p rid ru ž u je brojeve o b jek tim a. M apa om ogućava
pronalaženje objekta p o m o ć u n je m u p rid ru ž e n o g drugog objekta. N ju nazivaju i
asocijativan niz, zato što objekte aso cira (p rid ru ž u je ) d ru g im o b jek tim a, ili rečnik,
zato što objekat traži p o m o ć u ključa, k ao što se u rečniku definicija traži p o m o ć u
reči. Mape su m o ć n e p ro g ra m sk e alatke.
Iako to nije uvek m oguće, bilo bi id ealn o da veći d eo vašeg k oda rad i s tim interfejsim a,
a da jedino m esto na kojem ćete sp o m e n u ti tačan tip koji u p o treb ljav ate b u d e m esto pra-
vljenja instance. Dakle, ovako m o žete n a p ra v iti Listu:
/ / : cu vanje/JednostavnaKolekcij a . ja va
import j a v a . u t i l
P ošto se u o v om p rim e ru u p o treb ljav aju sam o m eto d e interfejsa Collection, o dgova-
rao bi svaki ob jek at klase nasleđ en e o d to g in terfejsa, ali ArrayList predstavlja najosno v-
niji tip sekvence.
Im e m e to d e add() n av o d i na p o m isa o d a o n a u kolekciju (Collection) d o d aje n o v ele-
m e n t. M e đ u tim , u d o k u m e n ta c iji je p recizn o naved en o da add() „čini d a ovaj p rim e ra k
klase C ollection sadrži d a ti e le m e n t“. To je u ra đ e n o zbog sk u p a (potklase Set), koji ele-
m e n t d o d a je kolekciji sam o ukoliko se o n ta m o već n e nalazi. U slučaju klase ArrayList i
svih vrsta Listi, add() uvek znači ,,d o d ati“, p o što Liste dozvoljavaju p o stojan je d u p lik ata.
K roza sve kolekcije m o žete p rolaziti sin ta k so m foreach, kao u p re th o d n o m p rim e ru .
U n astav k u poglavlja n au čićete da u p o treb ljav ate fleksibilniji Iterator.
Vežba 2: (1 ) Izm enite p ro g ra m JednostavnaKolekcija.java tako da se za c upotrebljava Set.
Vežba 3: (2) Izm en ite p ro g ra m unutrasnjeklase/Sekvenca.java tako da m u m ožete d o -
dati p ro izvoljan bro j elem en ata.
//: cuvanje/DodavanjeGrupa.java
// Dodavanje grupa elemenata objektima tip a C o lle c tio n .
import j a v a . u t i 1.* ;
p u b lic c la s s DodavanjeGrupa {
p u b lic s t a t i c void m a in (S trin g [] args) {
C o l1ection<Integer> k o le k c ija =
new A rra y L is t< In te g e r> (A r ra y s .a s L is t(l, 2, 3, 4, 5 )) ;
308 Misliti na Javi
/ / : cuvanje/KaoNekakvaLista.java
// A rr a y s .a s L is t () samo nagađa o kojem se tip u ra d i.
import j a v a .u t il
class Sneg {}
c la s s Prsac extends Sneg {}
c lass Slab extends Prsac {}
c la s s Krupan extends Prsac { }
c la s s Krpe extends Sneg {}
c lass S lo ta extends Sneg { }
pu b lic c la s s KaoNekakvaLista {
p ub lic s t a t ic void m a in (S trin g [] args) {
List<Sneg> snegl = A r r a y s .a s L is t (
new K rp e (), new S l o t a ( ) , new P r s a c ( ) ) ;
// Neće se p r e v e s t i:
// List<Sneg> sneg2 = A r r a y s .a s L is t (
// new S la b ( ) , new K ru p a n (j);
Poglavlje 11: Čuvanje objekata 309
public c la s s Is p is iv a n je K o n te jn e ra {
s t a t ic C o lle c tio n popuni(C ollection< String> k o le k c ija ) {
kolekci ja.add C 'pacov'1) ;
kolekci ja.a d d C 'm a čk a ");
kolekci ja .a d d C 'k u č e ");
k o le k c ija .a d d ("k u č e ");
return k o le k c ija ;
}
s t a t ic Map popuni(Map mapa) {
mapa.put C'pacov'1, "Zb u n jen i" ) ;
mapa.put("mačka“ , "M ica'1) ;
m apa.put("kuče", " L e s i " ) ;
310 Misliti na Javi
m apa.put(''kuče", “ B o b i");
return mapa;
}
p u b lic s t a t ic void m a in (S trin g [] args) {
print(popuni(new A rrayList< Strin g> ( ) ) ) ;
print(popuni(new L in k e d L is t< S tring> ( ) ) ) ;
p rin t(p o p u n i(new HashSet<String> ( ) ) ) ;
print(popuni(new TreeSet<String> ( ) ) ) ;
print(popuni(new LinkedHashSet<String> ( ) ) ) ;
print(popuni(new HashMap<String, String> ( ) ) ) ;
p rin t(p o p u n i(new TreeMap<String, String> ( ) ) ) ;
print(popuni(new LinkedHashMap<String, String> ( ) ) ) ;
}
} /* Is p is :
[pacov, mačka, kuče, kuče]
[pacov, mačka, kuče, kuče]
[kuče, mačka, pacov]
[pacov, mačka, kuče]
{kuče=Bobi, mačka= M ica, pacov=Zbunjeni}
{mačka= Mica, kuče=Bobi, pacov=Zbunjeni}
{pacov=Zbunjeni, mačka= M ica, kuče=Bobi}
* ///:-
b rže vađenje elem enata, iako red o sled njegovog skladištenja izgleda b esm islen (često je
važno sa m o da li je objek at član o d re đ en o g sk u p a iz kategorije Set, d o k redo sled elem en a-
ta u to m sk u p u n em a značaja). A ko je p o re d a k skladištenja važan, m o žete u p o tre b iti
TreeSet koji objekte čuva u rastu će m p o re tk u p o ređ en ja, ili LinkedHashSet koji ih čuva
u re d o sle d u kojim su bili u m etan i.
M ap a (koja se naziva i asocijativan niz) o m o g u ćav a p ro n alažen je o b jek ta p o m o ć u nje-
govog ključa, kao u jed n o stav n o j bazi p o d atak a . O b jek at p rid ru ž e n k lju ču jeste njegova
vrednost. U koliko im ate M a p u u kojoj su im e n a država p rid ru ž e n i im e n im a n jih o v ih
glavn ih g rad ova i želite d a saznate koji je glavni g ra d G ruzije, p retraživ an je ob avljate p o -
m o ću ključa ,,G ruzija“ - gotovo kao da je u p ita n ju indeks niza. Z bog takvog p o n ašan ja
M ap a p rih v ata sam o po jed an p rim e ra k svakog ključa.
Map.put(ključ, vrednost) u m eće u m a p u v re d n o st (o n o što hoćete d a u sk lad ištite) i
p rid ru ž u je je o đ re đ e n o m k lju ču (p o m o ć u kojeg ćete tu v re d n o st p ro n aći).
Map.get(ključ) daje v red n o st p riđ ru ž e n u d a to m ključu. U g o rn je m p rim e ru sa m o su d o -
davan i p aro v i k lju č -v re d n o st, je r p retraživ an ja n ije bilo. To će b iti p o k a zan o kasnije.
Im a jte u v id u da ne m o ra te zad ati veličinu M ap e (n iti se za to u o p šte starajte), je r o n a
svoju veličinu podešava a u to m atsk i. P o red toga, M ap e sam e u m e ju d a isp išu svoj sadržaj,
pri če m u se p rik azu ju p arovi k lju č -v re d n o st. U M a p am a se ključevi i v re d n o sti ne čuvaju
u o n o m p o retk u kojim su u m e ta n i, zato što realizacija tip a H a sh M a p u p o treb ljav a veom a
brz alg o ritam koji o d ređ u je taj p oredak.
U p rim e ru su u p o treb lje n a tri o sn o v n a tip a Mapa: HashMap, TreeMap i Linked-
HashMap. Kao i HashSet, HashMap o b ezb edu je n ajb ržu te h n ik u p retraživ an ja; takođe,
ni ta m a p a ne čuva svoje elem en te u lako razu m ljiv o m p o retk u . U m ap i tip a TreeMap
ključevi su p o ređ a n i u ra stu ćem p o re tk u , a u m a p i tip a LinkedHashMap u p o re tk u u m e-
tan ja, p ri če m u je zad ržan a b rz in a p retraž iv a n ja tip a HashMap.
Vežba 4: (3) N apravite generatorsku klasu koja vraća im ena (kao objekte tipa String) likova
vašeg o m iljeno g film a (ili Snežane i sedam p atuljaka ili Ratova zvezda, nije važno) svaki p u t
kada pozovete next(), i vraća se na p o eeta k liste sa im en im a likova kada d o đ e d o n jen og
kraja. U p o treb ite taj g e n erato r za p o p u n ja v an je o b ičn o g niza i po jed n o g p rim e rk a tipova
ArrayList, LinkedList, HashSet, LinkedHashSet i TreeSet. Z atim ispišite sadržaj svakog
od tih kontejnera.
Liste
Liste čuv aju elem en te u o d re đ e n o m p o retk u . Interfejs List kategoriji C ollection do đ aje
više m e to d a za u m e ta n je i uklan jan je elem enata iz sred ine Liste.
Postoje dva tip a Liste:
• O sn o v n i tip, ArrayList, izv rstan je za p ristu p a n je elem en tim a n a su m ič n im red o-
sled o m , ali je sporiji p rilik o m u m eta n ja i u k lan jan ja elem en ata iz sred in e Liste.
• T ip LinkedList o b ezb eđ u je o p tim a la n sekvencijalni p ristu p , a ni u m e ta n je i ulda-
n jan je elem enata iz sred in e Liste ne košta m n o g o . LinkedList je relativ no sp o r p ri-
likom p ristu p a n ja elem en tim a n a su m ičn im red o sled om , ali su njegove m o g u ć n o sti
veće nego o ne tip a ArrayList.
312 Misliti na Javi
i nakon što je napravljena, p ri čem u au to m atsk i podešava svoju veličinu. To je n jen a osnov-
n a vrednost: sekvenca koja se m ože m odifikovati. R ezultat dodavanja jed n o g objekta tipa
Hamster vidite u red u 2 - ob jek at je d o d a t na kraj liste.
M eto d o m contains() m o žete saz n ati d a li je o d re đ e n i elem en t deo Liste. U koliko o b-
jek at ho ćete d a uk lonite, p ro sled ite njeg o v u referen cu m eto d i remove(). T akođe, ako
im ate referencu nekog objekta, m e to d o m indexOf() m o žete sazn ati njegov in d ek s (red n i
b ro j) u Listi, kao što se vidi iz red a 4 rezu ltata.
M eto d a equals() (d eo k orenske ldase Object) u p o treb ljav a se za ispitivanje da li je
o d red e n i elem en t d eo Liste, te za p ro n ala že n je in d ek sa elem en ta i u k lan jan je e lem en ta iz
Liste na o sn o v u reference. Svaki o b jek at tip a Pet p o definiciji je jed in stv en ; č a k i ako u listi
već p o sto je dva o b jek ta tip a Cymric, ako n a p ra v im n o v takav o b jek at i p ro sled im ga m e-
to d i indexOf(), n jen rezu ltat će b iti -1 (što znači d a taj n o v i nije p ro n a đ e n ), pa će i p o-
kušaji u k lan jan ja tog objekta m e to d o m remove() v ra titi false (što zn ači da u k lan jan je nije
uspelo). Za d ru g e klase, equals() se m o že dru g a čije d efinisati. P rim e ra radi, o bjekti tip a
String (znak o vni nizovi) je d n a k i su u koliko je sadržaj o b a zn ak o v n a niza id en tičan . D ak-
le, da ne bi bilo iznen ađ en ja, p o vedite ra č u n a o to m e da se p o n aša n je Lista m en ja u za-
v isn o sti o d m eto d e equals().
Iz redova 7 i 8 rezu ltata vidi se d a je usp elo u k lan jan je o b jek ta koji tačn o o d g o v ara jed -
n o m o b jek tu u Listi.
E lem en t je m o gu će u m e tn u ti u sred in u Liste, kao što v idite iz reda 9 rezu ltata i koda
koji m u p re th o d i, ali iz toga sledi zaključak: za tip LinkedList, u m e ta n je i u k lan jan je iz
sredin e liste jeftin a je o p eracija (sem u o v o m slučaju , k ad a se u sre d in u liste p ristu p a na-
su m ič n o ), ali za ArrayList to je sku pa o p eracija. Da li to znači da n ik a d a ne treb a u m e tati
elem en te u sred in u ArrayListe i d a treb a p reći na LinkedList čini takvo nešto zatreba? Ne
- o to m e p ro sto tre b a da v o d ite raču n a, pa ako n a p rav ite m n o g o u m e ta n ja u sre d in u Ar-
rayListe ia k o p ro g ram p o č n e d a u spo rav a, m o ž d a je za to kriva vaša realizacija Liste (tak-
va uska grla se najbolje p ro n alaze p ro fajlero m , kao što ćete v ideti u d o d a tk u na lokaciji
http://M indV iew .net/B ooks/B etterJava). O p tim iz acija je složen p o sao i n ajbolje je ne bavi-
ti se n jo m d o k vas p ro g ra m n a to ne n atera (m a d a nije loše zn ati o čem u se rad i).
M eto d a subList() služi za pravljenje isečka veće liste, pa je p riro d n o da se d o b ija rezul-
tat true kada se isečak p rosledi m e to d i containsAll() za tu veću listu. Z an im ljiv o je da pri
to m p o re d ak nije važan - u red o v im a 11 i 12 rezu ltata vid ite da na rezu itat m e to d e con-
tainsAll() ne utiče po zivanje m eto d a C ollections.sort() (za so rtira n je ) i Collec-
tions.shuffle() (za m ešan je e lem en ata) na p o d listi sub. M etoda subList() pro izv o d i listu
povezanu sa o rig in a ln o m listom . Z ato se izm en e u vraćen o j listi od ražav aju na o rig in al-
noj, i o b rn u to .
M eto d a retaiuAll() zapravo spro v o d i o p e ra c iju „preseka skupova". U ovom slučaju,
o na u o b je k tu kopija zadržava sve elem en te koji su i u p o d listi sub. I o p et, rezu ltu ju će po -
našanje se m en ja u zavisnosti o d m eto d e equals().
C etrn a e sti red ispisa p rik a z u je rezu ltat u k lan jan ja elem en ta na o sn o v u njegovog in-
deksa, što je jed n o stav n ije nego u k lan jan je na o sn o v u reference objekta, p o što vas indeksi
oslob adaju brige o p o n a ša n ju m e to d e equals().
I p o n ašan je m eto d e removeAll() zavisi o d m e to d e equals(). Kao što joj i englesko ime
kaže, o n a iz Liste uklanja sve o b jek te koji su joj p ro sleđ en i u a rg u m e n tu sub.
Poglavlje I I : Čuvanje objekata 315
Iteratori
U svakoj kon tejn ersk o j klasi m o ra p o sto jati n ačin d a se elem enti d o d a ju i izvade. O sn o v n i
za d atak k o n te jn e ra i jeste da n ešto sadrži. U klasi List, o b jek ti se u m eć u m eto d o m a d d (),
a m eto d a g e t ( ) je jed an o d n ačin a da se pro čitaju.
Ako p o č n ete da razm išljate na višem niv o u o k o n tejn ersk o j biblioteci, pojavljuje se je-
d a n n eđ o statak : m o ra te ta č n o zn ati tip k o n tejn era d a biste ga koristili. To na prv i po gled
ne izgleda loše, ali šta ako n ap išete k o d za Listu, a kasnije o tk rijete da bi isti k o d treb alo
da p rim e n ite na sk u p (Set)? Ili, p rim e ra radi, želite da nap išete generički kod koji ne zna
(ili m u nije b itn o ) s kojim tip o m k o n tejn era radi, kako biste m ogli da ga koristite s razli-
čitim tip o v im a k o n te jn e ra a d a ga pri to m n e pišete ponovo?
O vakva apstrak cija se p o stiže k o rišćen je m iteratora (i to je p ro je k tn i obrazac). Ite ra to r
je o bjek at čiji je zad atak da se p o m e ra kroz niz o b jek ata i d a izabere svaki o bjek at u to m
n izu, p ri čem u p ro g ra m e r k lijent ne zna (ili m u nije važno) kakva je s tru k tu ra tog niza.
P ored toga, ite ra to r se o b ič n o naz.iva lak o b jek at (engl. light-w eight), je r koristi m alo re-
sursa. Z b o g toga ćete za iterato re često v iđati naizgled ču d n a o g ran ičen ja; na p rim er, Ja-
vin Iterator m ože da se kreće sa m o u je d n o m sm e ru . Sa ite ra to ro m ne m ožete b o gzn a šta
da rad ite, o sim da:
1. Z a tra žite o d k o n te jn e ra da vam m e to d o m iterator( ) p ro sledi Iterator. D obijeni
Iterator je sp re m a n da v ra ti prv i elem en t niza posle prv og poziva njegove m eto d e
n e x t().
2. D obijete sledeći o b jek at u n izu p o m o ć u m e to d e n e x t().
316 Misliti na Javi
3 . U stanovite da li im a j o š ob jekata u n iz u p o m o ć u m e to d e h a s N e x t( ).
4. U k io n ite posled nji elem en t koji je ite ra to r v ra tio p o m o ć u m e to d e r e m o v e ( ).
D a b ism o videli kako o n radi, o p e t će m o u p o tre b iti kJasu P et i n jen e m e to d e , o p isan e
u poglavlju Podaci o tipu:
rem ov e() spada u takozvane o p cio n e m etod e (im a ili još), što znači da je ne m o raju realizovati sve
realizacije Ite ra to ra . O tom e govorim o u poglavlju Detaljno raztnatranje kontejnera. Kako svi stan-
d ard ni kon tejn eri Java biblioteke realizuju rem o v e(), o tom e ne m o rate da b rin ete fdok ne dodete do
sp o m e n u to g poglavlja).
Poglavlje I 1; Čuvanje objekata 317
U oćili ste d a m eto d a ispisiS ve() ništa ne zna o vrsti sekvence kroz koju prolazi, a to po-
kazuje p rav u sn ag u Ite ra to ra : m o g u ć n o st da razdvoji o p eraciju pro laska kroz sekvencu
o d njen e stru k tu re . Z ato se k atk ađ kaže da ite ra to ri objedinjuju pristup kontejneritna.
V ežba 8: (1) P repravite vežbu 1 tako da se za k reta n je kroz L istu u p o tre b lja v a Ite ra to r
kada se pozove skoci().
V ežba 9: (4) P repravite p ro g ram u n u tra sn je k la se /S e k v e n c a .ja v a tako da S ekvenca radi
sa Ite ra to ro m u m esto sa S electo ro m .
V ežba 10: (2) P repravite vežbu 9 iz poglavlja P olim orfizam tako da se G lo d a ri čuvaju u Ar-
ray L isti i da se za k retan je kroz niz G lo d a ra u p o tre b ljav a Ite ra to r.
V ežba 11: (2) N apišite m e to d u koja u p o treb ljav a I te r a to r za p ro lazak kroz k o n tejn er (ob-
jekat tipa C o lle c tio n ). M e to d o m to S trin g O ispišite sadržaj svih o b jekata u k o ntejneru .
P o p u n ite o b je k tim a sve vrste k o n tejn era i n a svaki o d njih p rim e n ite svoju m eto d u .
318 Misliti na Javi
Listlterator
Za liste p o sto ji n a p red n iji iterato r, Listlterator. D ok se Iterator m ože p o m e ra ti sam o
u n a p re d , Listlterator je d v o sm eran . T akode, u m e da pro izv ed e indekse sledećeg i p re t-
h o d n o g elem e n ta u o d n o su n a m esto n a koje ite ra to r p ok azu je u listi, kao i d a m e to d o m
set() z a m e n i p o sled n ji e lem en t koji je p osetio. P ozivom m e to d e listIterator() nap rav ićete
Listlterator koji p ok azu je n a p o č eta k Liste, a zadavanjem arg u m e n ta n (kao u listltera-
tor(n)) nap ra v iće te Listlterator koji p o činje p o k azu ju ći n a in d eks n u listi. Evo p rim e ra
u k o jem su p o k az an e sve n av ed ene m o gu ćn osti:
//: c u v a n je / Ite ra c ij a L i s t e . ja va
import ty p e in fo .p e ts .* ;
import j a v a . u t i l .* ;
p u b lic c la s s It e r a c i ja L is t e {
p u b lic s t a t i c void m a in (S trin g [] args) {
List<Pet> 1jubimci = P e t s .a r r a y L is t ( 8 ) ;
Lis tIte ra to r< P e t> i t = 1ju b im c i.1 i s t I t e r a t o r ( ) ;
w h i1 e (it.h a s N e x t())
S y s t e m .o u t .p r in t(it.n e x t () + " , “ + it.n e x tln d e x () +
" , " + it.p r e v io u s In d e x () + " ; " ) ;
S y s te m .o u t .p r in tln ();
// Unazad:
whi1e ( i t.h a s P re v i ous( ) )
S y s t e m .o u t .p r in t (it .p r e v io u s ( ).id ( ) + " " ) ;
S y s te m .o u t .p r in tln ();
S y s te m .o u t.p rin tln (lju b im c i);
i t = 1ju b im c i.1 i s t I t e r a t o r ( 3 ) ;
w h ile (it.h a s N e x t ()) {
it .n e x t ();
i t . s e t ( P e t s . randomPet( ) ) ;
}
S y s te m .o u t.p rin tln (lju b im c i);
}
} /* Is p is :
Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6,
5;
Pug, 7, 6; Manx, 8, 7;
7 6 5 4 3 2 1 0
[R a t, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
[R a t, Manx, Cymric, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau]
* ///:-
Ulančana lista
I klasa LinkedList realizuje osn o v n i interfejs List kao ArrayList, ali o d re đ e n e o p eracije
(u m e ta n je u sre d in u Liste i uklan jan je iz nje) obavlja efikasnije n ego ArrayList. S d ru g e
stra n e , m an je je efikasna za o p eracije p ristu p a n ja nesekvencijalnim red o sled o m .
LinkedList d o d aje i m e to d e koje o m o g u ćav aju d a je u p o tre b im o kao stek, k ao red za
čekanje (Queue) iii d v o stra n i red za čekanje (engl. double-ended queue, deque).
N eke o d ovih m e to d a su p se u d o n im i (alijasi) ili m e d u so b n e n e z n a tn e varijacije, d a bi
se đ o b ila im en a p o z n ata u k o n tek stu o d red en e u p o tre b e (n a ro čito u re d o v im a ček an ja).
N a p rim er, getFirst() i elem ent() iden tičn e su - o n e vraćaju čelo (p rv i elem en t) liste, a da
ga ne u k lo n e, i gen erišu izuzetak NoSuchElementException ako je Lista p razn a. M eto d a
peek() je n jihov a varijacija koja vraća null ako je lista p razn a.
Id e n tič n e su i m eto d e removeFirst() i remove() —o n e u k lan jaju i v raćaju kao rezu ltat
čelo liste, i g enerišu izuzetak NoSuchElementException ako je lista p raz n a, d o k n jih ov a
varijacija poll() 11 to m slučaju vraća null.
addFirst() um eće elem en t n a p o četak liste.
offer() je ista kao add() i addLast(). Sve o n e d o d aju e lem en t na rep (kraj) liste.
removeLast() uklanja i vraća poslednji elem en t liste.
N ared n i p rim e r p ok azu je o snovne sličnosti sp o m e n u tih m eto d a i razlike iz m e d u njih.
U n je m u se ne ponavlja p o n ašan je p rik azan o u p ro g ra m u MogucnostiListe.java:
p u b lic c la s s Stack<T> {
p riv a te LinkedList<T> s k la d is te = new L i nkedList<T> ();
p u b lic void push(T v) { s k la d is te .a d d F ir s t ( v ) ; }
p u b lic T peek() { return s k l a d i s t e . g e t F i r s t ( ) ; }
p u b lic T pop() { return s k la d is te .re m o v e F irs t( ) ; }
p u b lic boolean empty() { return s k la d is te .is E m p ty ( ) ; }
p u b lic S trin g to S trin g O { return skl adi s te . to S tr i ng ( ) ; }
} ///:-
p u b lic c la s s StekTest {
p u b lic s t a t ic void m a in (S trin g [] args) {
Stack<String> stek = new S tack < Strin g > ();
fo r (S t r in g s : "Moj pas ima buve". s p li t (" “ ) )
s t e k .p u s h (s );
w hi1e ( ! stek.em pty( ) )
System .out. p r i n t ( s t e k .pop() + " " ) ;
}
} /* I spi s :
buve ima pas Moj
* ///:-
U koliko u svom k o d u h o ćete da u p o tre b ite ovu klasu Stack, m o raćete p o tp u n o da spe-
cificiratc n jen paket - ili p ro m e n ite im e klase —kada je b u d e te pravili; u p ro tiv n o m , vero -
v a tn o ćete izazvati suk o b s klasom Stack iz pak eta java.util. P rim e ra radi, ukoliko u g ornji
listing uvezem o ja v a .u til.* , m o ra će m o da u p o treb ljav a m o im en a p aketa kako b ism o
sprečili d v o sm islen o st im en a i sukobe:
//: cuvanje/StekSukob.java
import n e t.m in d v ie w .u til.* ;
p u b lic c la s s StekSukob {
p u b lic s t a t ic void main (S tr in g [] args) {
n e t.m indview .u t i 1. Stack< String> stek =
322 Misliti na Javi
O b e klase Stack im aju isti interfejs, ali u p ak e tu java.util ne p o sto ji zaje dn ički interfejs
Stack - verovatno zato što je o rig in a ln a, loše p ro jek to v an a klasa java.util.Stack u Javi 1.0
usvojila to im e. Iako java.util.Stack p osto ji, LinkedList p ravi bolji stek, p a p re d n o s t treb a
dati p ristu p u o stv aren o m u pak etu net.mindvievv.util.Stack.
Izb o r realizacije klase Stack „kojoj treb a dati p re đ n o s t“ in ožete k o n tro lisa ti eks-
p licitn im uvozom :
Sada će svako upu ćiv an je na klasu Stack izabrati verziju net.m indview .util, a da biste
izabrali java.util.Stack, m o ra te u p o tre b iti p o tp u n o im e koje o b u h v a ta i im e paketa.
Vežba 15: (4) U p ro g ram sk im je z iđ m a stekovi se često u p o treb ljav aju za izračun avan je
v red n o sti izraza. Izraču n ajte sledeći izraz koristeći paket net.m indview.util.Stack, gde +
znači „stavite n a stek sledeće sIovo“, a - zn ači „skinite v rh steka i ispišite ga“:
,,+ U + n + c — t e + r + t — + a -+ i-+ n + t+ y ---- 1- - + r + u —+ l+ e + s— “
Funkcionalnost skupa
Skup (Set) ne m o že da sadrži više o d je d n e in stan ce v re d n o sti objekta. U k oliko p ok u šate
da d o d ate više od je d n e instance ekviv alen tno g objekta, Set će sprečiti d u p liran je . Set se
najčešće u p otreb ljav a za u tv rd iv an je p rip a d n o sti, pa je lako saznati da li je o d re đ e n i o b -
jek at u o d re d e n o m sk u p u . Z ato je za Set pretraživ an je o b ič n o najvažnija o p eracija, pa se
najčešće b ira realizacija HashSet, o p tim iz o v an a za b rz o pretraživ anje.
Set (sku p ) im a isti interfejs kao Collection, što znači da nem a d o d a tn ih fu nk cija kao
što im aju dve različite liste. S kup je isti kao kolekcija, sam o se različito p o n aša. (O vo je
p rim e r savršenog korišćenja n asledivanja i p o lim o rfizm a: za izražavanje d rug ačijeg po-
n ašanja.) Set u tv rđ u je p rip a d n o st na o sn o v u ,,v red n o sti“ objekta. O b jašn je n je o d čega se
sastoji v re d n o st objekta složeno je, kao što ćete v ideti u poglavlju D etaljno razm atranje
kontejnera.
Poglavlje I 1: Čuvanje objekata 323
p ub lic c ia s s SkupCelihBrojeva {
p u b lic s t a t ic void m a in (S trin g [] args) {
Random slu cajan = new Random(47);
Set<Integer> skupcelihb = new H ashSet< Integer> ();
f o r ( i n t i = 0; i < 10000; i++)
sk u p c e lih b .a d d (s lu c a ja n .n e x tln t(3 0 ));
S y ste m .o u t.p rin tln (s k u p c e lih b );
1
} /* Is p is :
[15, 8, 23, 16, 7, 22, 9, 21, 6, 1, 29, 14, 24, 4, 19, 26,
11, 18, 3, 12, 27, 17, 2, 13, 28, 20, 25, 10, 5, 0]
* ///:-
U skup je d o d a to deset hiljada slu čajnih b ro jev a izm eđ u 0 i 29, pa je lako zam isliti kako
svaki o d tih b rojeva im a m n o g o d u p lik ata. Pa ipak, p rilik o m ispisa se vidi d a skup sadrži
sam o po je d n u in stan cu o d ređ e n e v red n o sti.
P rim etili ste da brojevi u rezu ltatu n em aju sm isleni p o red ak . U zrok to m e je tran sfo r-
m isan je ključa (engl. hashing), koje HashSet u p o treb ljav a rad i veće b rzin e - o n o je o b -
jašn jen o u poglavlju D elaljno razm atranje kontejnera. R edosled koji o država HashSet
razlikuje se od redosleda u TreeSet ili LinkedHashSet, p o što svaka realizacija skladišti
elem en te n a različit način. TreeSet ih čuva u ređ en e u c rv e n o -c rn o j s tru k tu ri stabla, d o k
HashSet p rim e n ju je tra n sfo rm isan je ključa, p ro je k to v an o specijalno rad i b rzo g pro-
nalaženja. I LinkedHashSet up o trebljav a je u c rv en o -crn o j s tru k tu ri stabla, d o k HashSet
p rim e n ju je tran sfo rm isan je ključa rad i brzog p ro n ala že n ja , ali n a izg ledp o m o ć u u lan ča n e
Iiste od ržava elem en te u p o re tk u u m etan ja.
A ko ho ćete da rezultati b u d u u ređ en i, m o žete u p o tre b iti TreeSet u m esto HashSeta:
//: cuvanje/UredjenSkupCelihBrojeva.ja va
import j a v a . u t i l .* ;
pu b lic c la s s UredjenSkupCelihBrojeva {
p ub lic s t a t ic void m a in (S trin g [] args) {
Random slu cajan = new Random(47);
SortedSet<Integer> skupcelihb = new T reeSet< In teg er> ();
f o r ( i n t i = 0; i < 10000; i++)
sk u p c e lih b .a d d fs lu c a ja n .n e x tln t(3 0 ));
System .out. pri n tln (s k u p c e lih b );
}
} /* Is p is :
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
* ///:-
324 Misliti na Javi
M e đ u n ajče šćim o p era c ija m a koje ćete obavljati jeste u tv rđ iv an je p rip a d n o sti sk u p u
p o m o ć u m e to d e contains(), ali p o sto je i operacije koje će vas p o d se titi na Venove dija-
g ram e koje ste m o ž d a učili u o sn o v n o j školi:
p u b lic c la s s OperacijeSaSkupovima {
p u b lic s t a t i c void m a in (S trin g [] args) {
Set<String> skupl = new H ashSet< String> ();
C o lle c tio n s .a d d A ll(s k u p l,
" A B C D E F G H I J K L ".s p lit(" " ) ) ;
sk u p l.a d d ("M ");
p r in t (" H : " + s k u p l.c o n ta in s (" H "));
p r in t (" N : " + s k u p l.c o n ta in s (" N "));
Set<String> skup2 = new H ashSet< String> ();
C o lle c tio n s .a d d A ll(s k u p 2 , "H I J K L " . s p l i t ( " " ) ) ;
p rin t("sk u p 2 deo skupal: 11 + sk u p l.co n tain sA U (skup2)) ;
sk u p l.rem oveC 'H ");
p r in t("s k u p l: " + s k u p l);
p rin t("sk u p 2 deo skupal: " + sk u p l.c o n ta in sA ll (skup2)) ;
sk u p l.rem o veA ll(sk u p 2 );
p rin t("sk u p 2 uklonjen iz skupal: " + sk u p l);
C o lle c tio n s .a d d A ll(s k u p l, "X V Z " . s p l i t ( " " ) ) ;
p r i n t ( " 'X Y Z' dodati skupul: " + skupl) ;
}
} /* Is p is :
H: true
N: fa ls e
skup2 deo skupal: true
sk u p l: [D, K, C, B, L, G, I , M, A, F, J , E]
skup2 deo skupal: fa ls e
skup2 uklonjen iz skupal: [D, C, B, G, M, A, F, E]
'X Y Z' dodati skupul: [Z, D, C, B, G, M, A, F, Y, X, E]
* ///:-
p u b lic c la s s JedinstveneReciAbecedno {
p u b lic s t a t ic void m a in (S trin g [] args) {
Set<String> reci =
new T reeSet< Stri n g > (S tring.CASE_INSENSITIVE_ORDER);
r e c i . addAl1(
new T e x tFile ("O p e ra c ije Sa Sk u p o vim a .ja va ", "\W + "));
S y s t e m .o u t .p r in t ln (r e c i);
}
} /* Is p is :
[A, add, ad d A ll, args, B, C, c la s s , C o lle c tio n s , con tain s, c o n ta in s A ll,
cuvanje, D, d o d a ti, E, F, f a ls e , G, H, HashSet, I , import, iz , J , ja v a ,
K, L, M, main, mindview, N, n et, new, OperacijeSaSkupovima, Is p is ,
P r in t , p u b lic , remove, rem oveAll, S e t, skupl, skupal, skupul, skup2,
s p l i t , s t a t i c , S tr in g , tru e , uklonjen, u t i l , void, X, V, Z]
* ///:-
Funkcionalnost mape
M ap iran je je d n ih objek ata n a d ru g e m o že biti izu zetn o m o ć a n n ačin rešavanja p ro -
g ram ersk ih zadataka. P rim e ra rad i, u z m im o p ro g ra m za ispitivanje slučajnosti brojeva iz
Javine klase R a n d o m . U id ea ln o m slučaju, R a n d o m bi davala savršeno raspodeljene
slučajne brojeve, ali da biste to ispitali, m o ra te d a generišete više slu čajn ih brojeva i da iz-
b ro jite o n e koji p rip a d a ju različitim opsezim a. M a p a rešava taj zad atak s lakoćom ; u
ovom slučaju, ključ je bro j koji p ro izv o d i klasa R a n d o m , a v re d n o st je bro j p u ta koliko se
taj broj pojavio:
U m eto d i m a in (), a u to m atsk o pak o v an je p retv ara slu čajn o g en erisan in t u referencu
objekta tip a In te g e r koja se m ože staviti u m a p u tipa H ash M a p . (U k o n tejn e r ne m ožete
da stavite v re d n o st p ro sto g tip a, već sam o referencu na objekat.) M eto d a g et() vraća n u ll
ukoliko takvog ključa nije b ilo u k o n te jn e ru (što znači da je taj broj prv i p u t p ro n a đ e n ).
U p ro tiv n o m , m eto d a vraća p rid ru ž e n u In te g e r v re d n o s t ključa koja se povećava za jedan
(a u to m a tsk o pakovanje p o n o v o p o jed n o stav lju je izraz, ali se obavlja p retv aran je u In te-
g e r i iz njega).
Evo p rim e ra u kojem se o b jek ti tip a P et p ro n a la ze p o m o ć u tek stu aln o g opisa, tj. obje-
k ata tip a S trin g . P okazan o je i kako se m e to d a m a c o n tain sK e y () i c o n tain sV a lu e () ispi-
tu je da li M ap a sađrži o d ređ e n i ključ, o đ n o sn o v red n o st:
p u b lic c la s s Mapaljubimaca {
p u b lic s t a t i c void m a in (S trin g [] args) {
Map<String,Pet> mapaljubimaca = new HashMap<String,Pet>();
mapaljub im aca.put("M oja mačka", new C a t (" M ic a " )) ;
mapaljub im aca.put("M oj pas", new D o g ("B ru n o "));
m apaljubim aca.put("Moj h rčak ", new H am ste r("Bo ško "));
p rin t(m ap alju b im aca);
Pet pas = m apaljubim aca.get("Moj p a s ");
p r in t ( p a s ) ;
print(m apaljubim aca.containsKey("M oj p a s " ) ) ;
p rin t(m a p alju b im a c a .c o n ta in sV a lu e (p a s));
}
} /* Is p is :
{Moja mačka=Cat Mica, Moj hrčak=Hamster Boško, Moj pas=Dog Bruno}
Dog Bruno
true
true
* ///:-
p ub lic c la s s MapaLista {
pu b lic s t a t ic Map<Person, List< ? extends P e t »
1jud iSljub im cim a = new HashMap<Person, L ist< ? extends P e t » ( ) ;
s t a t ic {
1jud iSljub im cim a.pu t(new P e r s o n ("Z o r a "),
A rra y s .a s L is t(n e w Cymric("Mol i “ ) ,new M u t t("Ž a r k o '')));
1ju d iS l jubimcima.put(new P e rs o n (''K a ta "),
A rra y s .a s L is t(n e w C a t (“ Ž e l j k a " ) ,
new C a t ( " E lz a " ) , new D o g C 'M a rg a re ta ")));
1ju d iS1 jub im cim a.put(new Person( " M ir n a " ) ,
A r r a y s .a s L is t (
new PugC'Lui i l i Luis D is a lić Meh"),
new C a t("S ta n e i l i Smradić el Negro"),
new C a t( "P i nkola " ) ) ) ;
1jud iS1 jub im cim a.put(new Person( “ L u k a "),
A r r a y s .a s L is t (new R a t ( " F a z i" ) , new R a t ( " F i z i " ) ) ) ;
1ju d iS l jubimcima.put(new P e r s o n (" Is a k " ),
A rra y s .a s L is t(n e w R a t("P e g a v i“ ) ) ) ;
328 Misliti na Javi
) /* Is p is :
l j u d i : [Person Luka, Person M irna, Person Is a k , Person Zora, Person Kata]
lju b im c i: [[R a t Fa z i, Rat F i z i ] , [Pug Lui i l i Luis D is a lić Meh, Cat Stane
i l i Smradić el Negro, Cat P in k o la ], [Rat P e g a v i], [Cymric M o li, Mutt Ž a rk o ],
[Cat Ž e ljk a , Cat E lz a , Dog M argareta]]
Person Luka ima:
Rat Fazi
Rat F iz i
Person Mirna ima:
Pug Lui i l i Luis D is a lić Meh
Cat Stane i l i Smradić el Negro
Cat Pinkola
Person Isak ima:
Rat Pegavi
Person Zora ima:
Cymric Moli
Mutt Žarko
Person Kate ima:
Cat Ž e ljk a
Cat Elza
Dog Margareta
* ///:-
Mapa m ože da vrati skup svojih ključeva, kolekciju svojih v re d n o sti ili sk u p svojih
p arova. M etoda keySet() p rav i sk u p svih ključeva u m ap i ljudiSljubimcima, koja se u
foreach n ared b i u p o treb ljav a za ite rira n je k ro z Mapu.
Vežba 17: (2) K lasu MorskoPrase iz vežbe 1 stavite u m a p u tak o da im e o b jek ta klase
MorskoPrase kao String b u d e ključ za o b jek at koji stavljate u tab elu . U zm ite Iterator za
sk u p koji daje m e to d a keySet( ) i u p o tre b ite ga za k retan je k ro z m a p u i p ro n alažen je
o b jek ta klase MorskoPrase za svaki ključ, p o to m ispišite svaki ključ i p o zo v ite m e to d u
sk a ce( ) za svaki objekat.
Vežba 18: (3) P o p u n ite m a p u HashMap p a ro v im a k lju č -v re d n o st. Ispišite rezu ltate tako
da po k ažete u re đ e n je p o heš k o d u . Izvadite p arove i u red ite ih p o k ljuču, a rez u ltat stavite
u m a p u LinkedHashMap. Pokažite d a je red o sled u m e ta n ja zad ržan .
Vežba 19: (2) P o n o v ite p re th o d n u vežbu sa sk u p o v im a HashSet i LinkedHashSet.
Vežba 20: (3) P rep rav ite vežbu 16 tako d a se b ro ji učestalo st pojave svakog sam oglasnika.
Poglavije 11: Čuvanje objekata 329
//: cuvanje/QueueDemo.java
// P r a v lje n je reda za čekanje od klase L in k e d L ist svođenjem naviše
// na Queue.
import ja v a . u t i l
p u b lic c la s s QueueDemo {
p u b lic s t a t ic void printQ(Queue redZaCekanje) {
w hile(redZaC ekanje.peek() != n u ll)
System .out.print(redZaC ekanje.rem ove() + " " ) ;
S y s te m .o u t .p r in tln ();
}
p u b lic s t a t ic void m a in (S trin g [] args) {
Queue<Integer> redZaCekanje = new L in k e d L ist< In te g e r> ();
330 Misliti na Javi
p u b lic c la s s PriorityQueueDemo {
p u b lic s t a t ic void m a in (S trin g [] args) {
PriorityQueue<Integer> p rio rite tn iR e d =
new PriorityQ u eu e< Integer> ();
Random slu cajan = new Random(47);
f o r ( i n t i = 0; i < 10; i++)
p r io r ite tn iR e d .o ff e r (s lu c a ja n .n e x tIn t(i + 1 0 ));
QueueDemo.pri ntQ (pri o ri te tn i Red);
new PriorityQueue<Character>(skiipZnakova);
QueueDemo.printQ(characterPQ);
}
} /* Is p is :
0 1 1 1 1 1 3 5 8 14
1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25
25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1
A A B C C C D D E E E F H H I I L N N 0 0 0 0 S S S T T U U U W
W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A
A B C D E F H I L N O S T U W
* ///:-
p u b lic c la s s P o r e d je n je ln t e r fe js a iIt e r a t o r a {
p u b lic s t a t ic void is p is i(Ite r a to r < P e t> i t ) {
w h ile (it.h a s N e x t ()) {
Pet p = i t . n e x t ( ) ;
S y s te m .o u t .p r in t(p .id () + + p + " ");
}
S y s te m .o u t .p r in tln ();
}
p u b lic s t a t ic void is p is i(C o lle c tio n < P e t> lju b im c i)
fo r (P e t p : lju b im c i)
S y s te m .o u t.p rin t(p .id () + + p + " ");
S y s te m .o u t .p r in tln ();
}
p u b lic s t a t i c void m a in (S trin g [] args) {
List<Pet> lis ta lju b im a c a = P e t s .a r r a y L is t ( 8 ) ;
Set<Pet> skupljubimaca = new HashSet<Pet>( lis t a lju b im a c a );
Map<String,Pet> mapaljubimaca =
new LinkedHashM ap<String,Pet>();
S t r in g [] imena = ( " R a lf , E r ik , Robin, L e js i, " +
" B r i t n i , Sima, Tufna, P a p e r j a s t " ) . s p l i t ( " , " ) ;
f o r ( i n t i = 0; i < im ena.length; i++)
m ap alju b im aca.p u t(im en a[i], 1i s ta lju b im a c a .g e t( i ) ) ;
i s p i s i (1i s t a lju b i m aca);
is p is i(s k u p lju b im a c a );
i spi s i (1 i s ta lju b im a c a .i t e r a t o r ( ) ) ;
i s p is i(s k u p ljub im aca.i t e r a t o r ( ) ) ;
S y s te m .o u t.p rin tln(m apaljubim aca);
System .o ut.p ri n tln(m apalju bim aca.keySet( ) ) ;
i s p i s i (mapaljubim aca.v a l ues( ) ) ;
is p is i(m a p a lju b im a ca .v a lu e s( ) . i t e r a t o r ( ) ) ;
}
} /* Is p is :
0 :Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
4:Pug 6:Pug 3:Mutt l:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat
0 :Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
4:Pug 6:Pug 3:Mutt l:Manx 5:Cymric 7:Manx 2:Cymric 0 : Rf! 1
{R alf= R at, Erik=Manx, Robin=Cymric, Lejsi= M utt, Britni= Pu g, Sima=Cymric,
Tufna=Pug, Paperjast=Manx}
334 Misliti na Javi
public c la s s SekvencaKolekcija
extends A bstractC o l1ection<Pet> {
p riv a te P e t[] ljubim ci = P e ts .c r e a te A r r a y (8 );
p ublic in t s iz e () { return 1ju b im c i.le n g th ; }
public Iterator<Pet> it e r a t o r ( ) {
return new Iterato r< P et> () {
p riv a te in t indeks = 0;
public boolean hasNext() {
return indeks < 1ju b im c i.le n g th ;
}
p ublic Pet n ex t() { return 1jub im ci[index+ + ]; }
p ublic void remove() { // N ije realizovano
throw new UnsupportedO perationException();
}
};
}
public s t a t ic void m a in (S trin g [] args) {
SekvencaKolekcija c = new S e k v e n c a K o le k c ija ();
P o r e d je n je ln t e r fe js a iIt e r a t o r a .i s p is i ( c ) ;
P o r e d j e n j e ln t e r f e j s a il t e r a t o r a . is p is i( c . it e r a t o r ( ) ) ;
}
} /* Is p is :
0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
* ///= -
Poglav[je 11: Čuvanje objekata 33 5
//: cu vanje/SekvencaBezKolekcije.java
import ty p e in fo .p e ts .* ;
import j a v a . u t i l . * ;
c la s s Sekvencaljubim aca (
protected P e t [] lju b im ci = P e t s .c r e a te A r r a y (8 );
}
Pravljenje Iteratora je n ajrazd v o jen iji način povezivanja sekvence i m eto d e koja p rim a
tu sekvencu, a pri to m se klasa sekvence o g ran ičav a z n a tn o m anje nego kada se im ple-
m e n tira klasa Collection.
Vežba 30: (5) Izm en ite p ro g ra m ColIectionSequence.java tako d a ne nasleđ uje Abstract-
Collection, nego da realizuje Collection.
336 Misliti na Javi
Foreach i iteratori
D o sad sm o foreach sin tak su u g lav n o m up otreb ljav ali s nizovim a, ali o n a rad i i sa svim
o b jek tim a Collection. Z apravo, već ste videli nekoliko p rim e ra za to u k o jim a se u p o tre -
bljavala ArrayList, ali ovo je o p šti dokaz:
p u b lic c la s s PromenljiveOkruzenja {
p u b lic s t a t ic void m a in (S trin g [] args) {
fo r(M ap .En try stavka: S y s te m .g e te n v ().e n try S e t()) {
S y s te m .o u t.p rin tln (s ta v k a .g e tK e y () + " : " +
s ta v k a .g e tV a lu e O );
}
}
} /* (Po k re n ite da b is te v id e li re z u lta te ) *///:-
System .getenv( )' vraća Mapu; en tryS et( ) p rav i sk u p (Set) e lem en ata Map.Entry, a
p o što je sk u p iterab ilan , m ože se u p o treb iti u fo reach petlji.
F oreach n ared b a rad i s nizovim a i sa svim što je iterab iln o , ali to ne znači d a je svaki niz
au to m a tsk i iterab ilan , niti da se obavlja ikakvo a u to m atsk o pakovanje:
Ova metoda nije postojala pre Jave SE5, pošto se smatralo da bi bila previše usko povezana sa opera-
tivnim sistemom, pa bi se kršilo pravilo„napiši jednom, izvršavaj bilo gde“. Činjenica da metoda sada
postoji k.izuje da su projektanti Jave nakon pojave .NET-a postali pragmaličniji.
338 Misliti na Javi
}
} /* Is p is :
1 2 3 A B C
* ///:-
Pokušaj da se niz p ro sle d i kao ite rab ila n arg u m e n t, n e uspeva. N e p o sto ji a u to m atsk a
konverzija u Iterable; m o ra te je o bav iti ru čn o .
Vežba 31: (3) Izm en ite polimorfizam/oblik/GeneratorSlucajnihOblika.java tako da
p o sta n e iterab ilan . M orate d o d a ti k o n s tru k to r koji p rim a broj elem en ata koji ite ra to r tre-
b a d a n ap ra v i p re zaustavljanja. D o kažite d a to radi.
Adapterska metoda
Šta ako im a te ite ra b iln u klasu i h teli b iste d a d o d a te je d a n ili više n o v ih n ačin a u p o tre b e
te klase u fo reach naredbi? P rim e ra rad i, p re tp o sta v im o d a b iste hteli d a b irate h o ćete li
k roz listu reči ite rira ti u n a p re d ili u n azad . U koliko sam o n asledite tu klasu i redefinišete
m e to d u iterator( ), zam en ićete p o sto je ću m e to d u i nećete m o ći d a b irate.
Jed n o rešenje sam n azvao A dapterska m etoda. „A dapterski“ d eo p o tiče iz p ro je k tn ih
o b razaca, p o što m o rate o b ezb ed iti o d re đ e n i interfejs d a biste zadovoljili foreach n ared b u .
K ada im ate je d a n interfejs a tre b a v am d ru g i, p ro b le m ćete rešiti u koliko napišete adapter.
O vde h o ć u da dodam m o g u ć n o st pravljenja iterato ra u n azad p o d ra z u m e v a n o m ite rato ru
u n a p re d , p a ne m o g u da ga redefinišem . U m esto toga, d o d ać u m e to d u koja proizvodi ite-
ra b ila n o b jek at koji se p o to m m ože u p o tre b iti u foreach naredb i. Kao što ćete videti, tim e
d o b ija m o više n ačin a u p o tre b e fo reach sintakse:
//: cuvanje/AdapterskaMetoda.ja va
// "Adapterska metoda" omogućava upotrebu foreach
// sin tak se s dodatnim vrstama it e r a b iln ih objekata.
import j a v a . u t i l .* ;
p u b lic c la s s AdapterskaMetoda {
p u b lic s t a t ic void m a in (S trin g [] args) {
R eversib leA rrayList< Strin g > ra l =
new R eversib leA rrayList< Strin g > (
A rra y s .a s L is t("T o be or not to b e " . s p l i t ( " " ) ) ) ;
// Metodom it e r a t o r ( ) grabi se običan it e r a t o r :
fo r (S t r in g s : r a l)
S y s te m .o u t.p rin t(s + " " ) ;
S y s te m .o u t .p r in tln ();
// D ajte ga iterab iln om objektu po svom izboru
f o r (S t r in g s : ra l .obrn utoO )
Sy ste m .o u t.p rin t(s + " " ) ;
}
} /* Is p is :
To be or not to be
be to not or be To
* ///:-
} /* Is p is :
Pre mešanja: [1 , 2, 3, 4, 5, 6, 7, 8, 9, 10]
Nakon mešanja: [4, 6, 3, 1, 8, 7, 2, b, 10, 9]
n iz : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Pre mešanja: [1 , 2, 3, 4, 5, 6, '/, 8, 9, 10]
Nakon mešanja: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8j
n iz : [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
* ///:-
Sažetak
Java im a više n a čin a za ču v an je objekata:
1 . N iz povezuje n u m e rič k e in d ek se sa o b jek tim a. O n sad o b jek te p o z n ato g tip a, pa
n ak o n u zim a n ja o b jek ta iz niza ne m o ra te da v ršite k rziju tip o v a. N iz m ože da
b u d e v iše d im e n z io n ala n i da sadrži p ro ste tipove. M e li m , ne m o žete d a m u p ro -
m en ite veličinu.
2. K olekcija sadrži p o jed in ač n e elem en te, d o k m a p a sad rži povezane parove. P o m o ču
Javinih g en eričk ih tip o v a zadajete tip objekta koji ćc u v ati u k o n te jn e ru , p a u
njega ne m o žete staviti p o g rešan tip i ne m o ra te da i iijate tip elem en ata kada ih
izvadite iz njega. I kolekcije i M ap e a u to m atsk i : rii; lavaju svoju veličinu n akon
d o d a v a n ja i u ld an jan ja elem en ata. K o n tejn er r»e uiože ad rži p ro ste tipove, ali se
a u to m a tsk o p akov an je stara za p rev o đ en je p ii, :tih i u r . i o m o tačk e tipove koji se
čuvaju u k o n te jn e ru , i o b rn u to .
3. P o p u t niza, lista tak o đ e povezuje n u m eričk f u. * o b jek tim a; nizovi i liste
m o g u se sm a tra ti u re đ e n im k o n te jn erim a .
4. U p o treb ite listu ArrayList ako često k o risfite a ristu p , a LinkedList ako
često um ečete i u k lan jate elem en te n sredini
5 . R edovi za ček an je i stekovi p rav e se p o m o ć u l c i , ; .in 'L ist.
6. Mapa je način za povezivanje objekai; ■ ,, ... >je d ru g im objek tim a. Klasa
HashMap je p ro jek to v an a za b rz p ristu p , dok ite e M a p cuva ključeve u u re đ e n o m
red o sledu i zato nije ta k o b rz a krtO ria sh M .,| '<' HashMap čuva svoje ele-
m en te u u re d e n o m red o sled u , ali tra n sfo rm ' ■ ev o b ezbeđ u je b rz p ristu p .
342 Misliti na Javi
7. Set čuva sam o objekte različitih v red n o sti. Klasa HashSet o b e z b e đ u je p ro n alažen je
m a k sim a ln o m b rz in o m , d o k TreeSet čuva e lem en te u u re đ e n o m red o sled u . Klasa
LinkedHashSet čuva svoje elem en te u u re đ e n o m red o sled u .
8. N em a p o tre b e da se u n o v im p ro g ra m im a k o riste stare klase Vector, Hashtable i
Stack.
Pogledajte p o jed n o stav ljen d ijag ram Javinih k o n te jn e ra (bez a p stra k tn ih klasa i starih
k o m p o n e n a ta ). U n jem u su sa m o interfejsi i klase koje ćete red o v n o sretati.
i 1 f
LinkedHashMap
ArrayList LinkedList PriorityQueue
Alatke
►i Comparator ! Arrays
IjnkedHashSet
V idite d a p o sto je sam o četiri k o n tejn ersk e k o m p o n e n te : Map, List, Set i Queue, i
sam o dve ili tri realizacije svake o d njih (rea liza đ je klase Queue iz pak eta java.util.con-
current nisu p rik aza n e n a d ija g ra m u ). N ajčešće ćete u p o tre b lja v ati k o n te jn e re oivičene
d eb e lo m p u n o m lin ijo m .
T ačkastim o k v irim a obeleženi su interfejsi, a o k v irim a s p u n o m lin ijo m o b ičn e (k o n -
kre tn e ) klase. Isp rek id an e p ra zn e strelice u k azu ju da o d re đ e n a klasa realizuje interfejs.
Strelice s p u n o m lin ijo m u k azu ju da klasa m ože da prav i o b jek te klase na koju po k azu je
strelica. Na p rim er, bilo koja kolekcija m ože da pravi Iterator, d o k List m o že da n ap rav i
Listlterator (kao i ob ičan Iterator, p o što je List izvedena iz Collection).
Evo p rim e ra u kojem se vidi razlika izm eđ u m e to d a razn ih klasa. K od je naveden u
poglavlju G enerički tipovi; ovde ga sam o p ozivam d a b ih d o b io rezu ltate. U n jim a se vide
i interfejsi koje realizuje p o je d in a klasa ili interfejs:
public c la s s KontejnerskeMetode {
p ub lic s t a t ic void m a in (S trin g [] args) {
R azlikeKon tejnerskih M etoda.m ain (args);
1
} /* Is p is : (prim er)
C o lle c tio n : [add, ad d A ll, c le a r , con tain s, c o n ta in s A ll, equals, hashCode,
isEmpty, it e r a t o r , remove, rem oveAll, r e t a in A U , s iz e , toA rray]
Poglavlje 11: Čuvanje objekata 343
In t e r f e j s i u k la s i C o lle c tio n : [ It e r a b le ]
Set extends C o lle c tio n , dodaje: []
In t e r f e j s i u k la s i S e t: [C o lle c tio n ]
HashSet extends S e t, dodaje: []
In t e r f e j s i u k la si HashSet: [S e t, C loneable, S e r ia liz a b le ]
LinkedHashSet extends HashSet, dodaje: []
In t e r f e j s i u k la s i LinkedHashSet: [S e t, C loneable, S e r ia liz a b le ]
TreeSet extends S e t, dodaje: [ p o llL a s t , navigableHeadSet,
d escen d in g lte rato r, lovver, headSet, c e ilin g , p o l lF i r s t , subSet,
n a v ig a b le T a ilS e t, comparator, f i r s t , f lo o r , la s t , navigableSubSet,
h igher, t a il S e t ]
In t e r f e j s i u k la s i T reeSet: [N avig a b leS et, C loneable, S e r ia liz a b le ]
L is t extends C o lle c tio n , dodaje: [1 i s t l t e r a t o r , indexOf, g et, s u b L is t,
s e t, la stln d ex 0 f]
In t e r f e j s i u k la s i L is t : [C o lle c tio n ]
A rra y L is t extends L is t , dodaje: [ensu reC apacity, trim ToSize]
In t e r f e j s i u k la s i A rra y L is t: [ L i s t , RandomAccess, Cloneable,
S e r ia liz a b le ]
Lin k e d L ist extends L is t , dodaje: [p o llL a s t, o f f e r , d escen d in g lterato r,
a d d F irs t, peekLast, rem oveFirst, p e e k F irs t, removeLast, g e tLa st,
p o l lF i r s t , pop, p o l l , addLast, rem oveFirstO ccurrence, g e t F ir s t ,
element, peek, o ffe rL a s t, push, o f f e r F i r s t , removeLastOccurrence]
In t e r f e j s i u k la s i L in k e d L ist: [ L i s t , Deque, C loneable, S e r ia liz a b le ]
Queue extends C o lle c tio n , dodaje: [o f f e r , element, peek, p o ll]
In t e r f e j s i u k la s i Queue: [C o lle c tio n ]
PriorityQ ueue extends Queue, dodaje: [comparator]
In t e r f e j s i u k la s i Prio rityQ u eu e: [S e r ia liz a b le ]
Map: [ c le a r , containsKey, con tain sV alu e, e n try S e t, equals, g et, hashCode,
isEmpty, keySet, put, p u tA ll, remove, s iz e , values]
HashMap extends Map, dodaje: []
In t e r f e j s i u k la s i HashMap: [Map, Cloneable, S e r ia liz a b le ]
LinkedHashMap extends HashMap, dodaje: []
In t e r f e j s i u k la s i LinkedHashMap: [Map]
SortedMap extends Map, dodaje: [subMap, comparator, f ir s t K e y , la stK e y ,
headMap, tailM ap]
In t e r f e js i u k la s i SortedMap: [Map]
TreeMap extends Map, dodaje: [descendingEntrySet, subMap, p o l1L a s tE n try ,
la stK e y , flo o rE n tr y , la s t E n t r y , lowerKey, navigableHeadMap,
n avig ab leTaiIMap, descendingKeySet, ta ilM a p , c e ilin g E n tr y , higherKey,
p ol1F i rs tE n try , comparator, f ir s t K e y , flo o rK e y, h ig h erEn try,
f i r s t E n t r y , navigableSubMap, headMap, lo w erEn try, c e ilin g K e y]
In t e r f e js i u k la s i TreeMap: [NavigableMap, C loneable, S e r ia liz a b le ]
* ///:-
V idite da svi sku p ovi (o b jek at tip a Set) sem tip a TreeSet im a ju p o tp u n o isti interfejs
kao Collection. List i Collection se z n a tn o razlik u ju , iako List zahteva m e to d e koje su u
interfejsu Collection. S d ru g e stran e , m eto d e in terfe jsa Queue su sam o staln e; za p ra-
vljenje fu n k cio n aln e realizacije reda za čekanje (Queue), m e to d e interfejsa Collection
nisu p o treb n e. N ajzad, jed in a d o d irn a tačk a klasa Map i C ollection jeste činjenica da
Map m ože da pravi kolekcije p o m o ć u m eto d a en tryS et( ) i v a lu es( ).
344 Misliti na Javi
Koncepti
C i d ru g i stariji jezici često su im ali i po nekoliko šem a za o b ra d u grešaka koje su p o p ra -
vilu bile u tv rd e n e d o g o v o ro m , o d n o sn o nisu bile deo p ro g ram sk o g jezika. U glavn om se
vraćala p o seb n a v red n o st ili se p o stavljao in d ik ato r, a p rim alac bi p o sm a tra o v red n o st,
o d n o s n o in d ik a to r i odlu čiv ao d a li je sve u red u. M eđ u tim , to k o m g o d in a o tk riv e n o je da
p ro g ra m e ri koji koriste b ib lio tek u o b ič n o sebe sm a traju n epogrešivim i razm išljaju u sti-
lu: ,,Da, greške se m ožd a dešavaju d ru g im a, ali u m om k od u ih n e m a “. Z b o g toga ne izne-
n a đ u je što n isu proveravali uslove p o d ko jim a je nastajala greška (a p o n e k a d su uzroci
greške bili previše glupi d a bi se p ro v erav ali).1 Kada biste bili d o v oljn o pažljivi d a tražite
grešku posle svake m eto d e, vaš k o d bi se p retv o rio u nečitljivu n o ć n u m o ru . P ošto su p ro -
gram e.ri i dalje m ogli da o d ržav aju sistem e n ap isan e o p isa n o m te h n ik o m , o d b ijali su da
p riz n a ju istinu: ovakav p ristu p isp rav ljan ju grešaka u m n o g o m e je og ran ičav ao pisanje
velikih, sn a ž n ih p ro g ra m a koji bi se lako održavali.
R ešenje je d a se p o stu p k u o b ra d e grešaka o d u z m e p riro d n o st i da se ojača fo rm aln o st.
O vaj način , zapravo, im a d u g u isto riju , je r se obrada izuzetaka (engl. exception handling)
sreće jo š u o p e ra tiv n im sistem im a iz šezdesetih g o d in a p ro šlo g veka, čak i u n a re d b i on
error goto iz p ro g ra m sk o g jezika BASIC. Tako se o b ra d a grešaka u jeziku C + + zasnivala
na jeziku A da, a Javina p retež n o n a C + + - U (iako više podseća n a o b jek tn i Pascal).
Reč ,,izuzetak“ se k o risti u sm islu „o g rađ u jem se o d tog a“. K ada se pojavi p ro b lem , m o -
žda n ećete zn ati kako d a ga o tk lo n ite , ali ćete sig u rn o zn a ti d a p ro g ra m ne m ože tek tako
da nastav i rad . M o ra te se zau sta v iti i n ek o n egde m o ra sm isliti šta d a se rad i. M eđ u tim ,
m o žd a u to m tr e n u tk u n e m a te d o v o ljn o in fo rm ac ija d a biste rešili p ro b lem . Z bog tog a ga
p ren o site n a viši nivo i p re d a je te n e k o m e ko je d o v o ljn o stru č a n da ga reši.
Još je d n a p riličn o k o risn a o so b in a izuzetaka jeste da o n i o b ičn o p o jed n o stav lju ju kod
za o b ra d u grešaka. D a n e m a izuzetaka, m o ra li b iste d a tra ž ite o d re đ e n u g rešku i d a se s
n jo m b o rite n a nekoliko m esta u p ro g ra m u . Sa izuzecim a, više n e m o rate da p rov erav ate
greške p rilik o m p oziva m e to d a , p o što izuzetak g a ra n tu je d a će ih neko uh v atiti. P rob lem
rešavate na je d n o m m estu , a to je tzv. blok za obradu izuzetaka (engl. exception handler).
O n skraću je pisan je k od a i o d v aja k o d k oji o p isu je šta želite d a u ra d ite to k o m n o rm a ln o g
izvršavanja, o d k o d a koji se izvršava k a d a n ešto k ren e n aop ak o. O b ičn o je k o d za u p isi-
vanje, čitanje i p ro n a la že n je grešaka m n o g o p reg led niji kad a se k oriste izuzeci nego kada
se p rim e n ju je sta ri n a č in ra d a s greškam a.
Osnovni izuzeci
Vanredno stanje (engl. exceptional condition) jeste p ro b le m koji sprečava n astavak ra d a te-
kuće m e to d e ili p ro g ra m sk o g blo k a. V ažno je razlikovati v a n re d n o stanje o d u o b ičajen og
p ro b lem a, s kojim m o žete d a se izb o rite u tek u ćem o k ru že n ju . Kada n astu p i v an re d n o
stanje, n e m o žete nastav iti o b ra d u zato što n e m a te n e o p h o d n e in fo rm acije da biste se iz-
b o rili s p ro b le m o m u tekućem okruženju. M o žete sam o d a iskočite iz tekućeg o k ru ž en ja i
da p ren esete p ro b le m u više o k ru žen je. To se dešava kada se p o jav i izuzetak.
Jednostavan p rim e r je deljenje. D a b i se izbeglo d eljenje n u lo m , d o b ro je prov eriti de-
lilac u n ap red . Ipak, šta zaista zn ači k ad a je delilac nula? M ožda znate, u k o n tek stu p ro b le-
m a koji p o k ušavate da rešite o d re đ e n o m m e to d o m , kako ćete se izb oriti s d eliocem koji
je n u la. M e đ u tim , ako je to n eo ček iv an a v re d n o st, s n jo m ne m o žete d a se izbo rite i zbog
toga m o ra te da generišete izu zetak u m e sto da nastavite s to m p u ta n jo m izvršavanja.
K ada n astan e izuzetak, dešava se n ekoliko stvari o d je d n o m . Prvo, p rav i se objekat izu-
zetka na isti n ačin kao svi d ru g i Javini o bjekti: u d in am ičk o j m em o riji, o p e ra to ro m new .
P o tom se ak tu eln a p u ta n ja izvršavanja (o n a koja se ne m ože n astav iti) zaustavlja, a refe-
renca n a o bjekat izuzetka izbacuje se iz tre n u tn o g ok ru žen ja. U to m tre n u tk u , k o n tro lu
izvršavanja p re u z im a m e h a n iz am o b ra d e izuzetaka i p o čin je da traž i odg ov arajuće m esto
s kog bi se nastav ilo izvršavanje p ro g ra m a . To o dg o v araju će m esto je blok za obradu izu-
zetaka (engl. exception handler), čiji je z ad a ta k d a reši p ro b lem p rim e n o m d rugačijeg p ri-
stu p a p ro b le m u , ili d a p ro sto nastav i izvršavanje o d neke d ru g e tačke.
Kao jed n o sta v a n p rim e r g en erisan ja izuzetka, p o sm a tra jm o referencu na objekat koja
se zove t. M ože se desiti d a v am b u d e p ro sleđ e n a referenca koja nije inicijalizovana, što
ćete h teti d a p ro v erite p re n eg o što p rek o te reference p o k u šate da pozovete neku m eto d u .
M ožete da pošaljete in fo rm ac ije o grešci u više o k ru žen je ako na p rav ite o bjekat koji p red -
stavlja te in fo rm acije i zatim ga ,,izbacite“ iz tek u ćeg k on tek sta. To se zove generisanje izu-
zetka (engl. throw ing an exception). Evo k ako izgleda:
i f ( t == n u ll)
throw new Nul1P o in te rE x c e p tio n ();
Poglavlje 12: Obrada grešaka pomoću izuzetaka 347
Argumenti izuzetka
P o p u t b ilo kog Javinog objekta, izuzeci se uvek prave u dinam ičk o j m em o riji p o m o ć u
o p e ra to ra new . Taj o p e ra to r zau zim a p ro s to r u m em o riji i poziva k o n stru k to r. U svim
sta n d a rd n im izu zecim a p o sto je dva k o n stru k to ra: jed a n je p o d razu m ev an i, a d ru g i im a
arg u m e n t tip a zn ako v nog niza u koji se m o g u sm estiti važne in fo rm acije o izuzetku:
Z n ak ovn i niz se k asnije inože izdvojiti p o m o ć u razn ih m eto d a, kao što će u sk o ro biti
p ok azan o .
R ezervisana reč th ro w p ro u zro k u je brojna zanim ljiva dešavanja. N akon što o p e ra to ro m
new n ap ra v ite objekat koji pređstavlja izuzetak, d o b ijen u referencu ćete p roslediti rezervi-
sanoj reči th ro w . T im e se postiže da se taj objekat v rati iz m etode, iako o n a o b ičn o nije p ro -
jek to vana da vraća taj tip objekta. O b rad u izuzetaka p ojednostavljeno m ožete shvatiti kao
drugačiji m eh a n iza m vraćanja rezultata, m ad a ovu analogiju n e bi trebalo da shvatate su-
više doslovno. I o b ičn i p ro g ram sk i blokovi m o g u se p rek in u ti generisanjem izuzetka.
U svakom slučaju, vraća se objekat izuzetka, a m eto d a ili p rogram ski b lo k se n ap u štaju .
Svaka sličn o st sa o b ič n im p o v ra tk o in iz m e to d e tu se i završava, p o što se m e sto p o -
vratk a p o tp u n o razlikuje od m esta p o v ratk a iz u o b ičajen o g poziva m eto d e. (O brešćete se
u o d g o v araju ćem b lo k u za o b ra d u izuzetaka koji n a steku s p o ziv im a p ro c e đ u ra m ože da
b u d e u daljen više nivoa o d m esta gde je izuzetak nastao.)
Pored toga, m ožete d a generišete proizvoljan objek at tip a T h ro w a b le , što je korenska
klasa svih izuzetaka. O b ič n o ćete generisati d ru g ačiju klasu izuzetaka za svaki tip greške.
Hvatanje izuzetka
D a b iste videli kako se hvata izuzetak, p rv o m o ra te da ra z u m e te p o ja m čuvane oblasti
(engl. guarded region). To je d eo k o d a koji m o že d a g en eriše izuzetke, iza koga sledi k o d
koji o b ra đ u je te izuzetke.
Blok try
A ko se n alazite u n u ta r m eto d e i g en erišete izu zetak (ili neka d ru g a m e to d a k o ju p ozivate
iz te m e to d e generiše izuzetak), izu zetak će p ro u z ro k o v a ti n a p u šta n je m eto d e. A ko ne že-
lite da th r o w izazove izlazak iz m e to d e, m o žete d a uv ed ete sp ecijalan b lo k u n u ta r te m e-
to d e koji će h vatati izuzetak. To se zove ispitni blok (engl. try block), z ato što isp ro b av ate
razn e pozive m eto d a. Isp itn i b lo k je o b iča n p ro g ra m sk i b lo k isp red koga se nalazi rezer-
visana reč try :
tr y {
// Kod k o ji može da generiše izuzetke
)
tr y {
// Kod k o ji može da generiše izuzetke
} c a tc h (T ip l id l) {
// Obrada izuzetaka tip a 1
} catch(Tip2 id2)
// Obrada izuzetaka tip a 2
} catch(Tip3 id3) {
// Obrada izuzetaka tip a 3
}
// i t d . ..
Poglavlje 12: Obrada grešaka pomoću izuzetaka 349
Svaka o d red n ica catch (b lo k za o b ra d u izu zetak a) n alik je m aloj m e to d i koja p rih v a ta
sam o je d a n a rg u m e n t o d re d e n o g tip a. Id en tifik ato r (id l, id2 itd.) m o že se k o ristiti u n u -
ta r p ro ced u re, p o p u t a rg u m e n ta m eto d e. Id e n tifik a to r se p o n e k a d n e k o risti je r tip izu -
zetka p ru ža d o v oljn o in fo rm ac ija za o b ra d u , ali a rg u m e n t ip ak m o ra d a postoji.
Blokovi za o b ra d u izuzetaka m o ra ju da slede o d m a h posle isp itn o g bloka. Ako n asta n e
izuzetak, m e h an iza m njegove o b ra d e tra ž i p rv i b lo k čiji a rg u m e n t o d g o v a ra tip u izu zet-
ka. P o to m se izvršava taj b lo k , n a k o n čega se sm a tra d a je izu ze tak o b ra đ e n . T raženje blo -
kova za o b ra d u izuzetak a zaustavlja se p o izv ršav an ju to g b loka. Izvršava se sam o p rv i
o d go v araju ći blok; p o stu p a k se. razlikuje o d n a re d b e Switch u kojoj je n a k o n svake o d -
re d b e case p o tre b n a n are d b a break da b i se sprečilo izvršavanje o stalih grana.
O b ra tite p a ž n ju n a to d a u n u ta r isp itn o g b lo k a n ekoliko poziv a različitih m eto d a
m o ž e da generiše isti izuzetak, ali v a m za o b ra d u sv ih tre b a sa m o je d a n blok.
Vašu klasu izuzetka m o ra te d a izvedete iz neke o d po sto jećih klasa izuzetaka, ako je
m og uće iz o n e ćije je značenje blisko sm islu novog izuzetka (m a d a to često nije m o gu će).
N ajp rostiji način za pisanje n ovog tip a izuzetka jeste p re p u s titi p re v o d io c u d a n a p ra v i
p o d ra z u m e v a n i k o n stru k to r, što g otovo da ne zahteva p isan je koda:
p ublic c la s s Nasleđivanjelzuzetaka {
p ub lic void f ( ) throvvs Jednostavanlzuzetak {
System .o u t.prin tln("Bacam Jednostavanlzuzetak iz f ( ) " ) ;
throw new Jednostavanlzuzetak ( ) ;
}
p u b lic s t a t ic void m a in (S trin g [] args) {
Nasleđivanjelzuzetaka sed = new N a sle đ iv a n je lz u z e ta k a ();
try {
s e d .f ( ) ;
} catch(JednostavanIzuzetak e) {
S y s te m .e rr.p rin tln (''U h v a tio sam ga! " ) ;
}
}
} /* Is p is :
Bacam Jednostavanlzuzetak iz f ( )
Uhvatio sam ga!
* ///:-
D o d a ti kod je k ratak i sadrži sam o dva k o n stru k to ra koji d efin išu n ačin p rav ljen ja izu-
zetka tip a Mojlzuzetak. U d ru g o m k o n stru k to ru se, p o m o ć u rezervisane reči super, eks-
p lic itn o poziva k o n stru k to r o sn o v n e klase sa a rg u m e n to m tip a String.
U blok ov im a za o b ra d u izuzetaka poziva se jed n a o d Throvvable m eto d a: printStack-
Trace( ). Kao što vidite iz rezultata, tim e se d o b ijaju in fo rm acije o n iz u m e to d a koje su bile
pozivane pre dolaska na m esto gde se desio izuzetak. O vde se in fo rm ac ije o p o lo žaju na
steku izvršavanja šalju u to k po d atak a System.out i au to m atsk i hvataju i isp isu ju na izlazu.
M ed u tim , ako pozovete p o d ra z u m e v a n u verziju:
e .p rin tS ta c k T ra c e ( ) ;
Izuzeci i zapisivanje
Izlaz biste m o gli i da zapišete (engl. log) p o m o ć u m e to d a klase java.util.logging. Iako je
zapisivanje d etaljn o o b jašn je n o u d o d a tk u n a lokaciji h ttp ://M indV iew .net/B ooks/B ettcr-
Java, e le m e n ta rn o zapisivanje je to lik o je d n o sta v n o da ga već ovde m o žem o u p o treb iti.
Uhvatio LoggingException
Aug 30, 2005 4:02:31 PM Z ap isivan jelzu zetk a <init>
SEVERE: Z ap isivan jelz u z etk a
a t Z ap isiva n je Iz u z e ta k a .m a in (Z a p isiva n je Iz u z e ta k a .ja va :2 4 )
S tatičn a m e to d a Logger.getLogger( ) p ra v i zap isn ik (engl. logger), tj. ob jek at tip a Log-
ger sa a rg u m e n to m tip a String (o b ičn o im e n o m p ak eta i klase u k o jim a je n asta la greška)
koji svoj izlaz šalje u System.err. U za p isn ik se najlakše u p isu je ta k o d a se pozove m e to d a
p rid ru ž e n a niv o u p o ru k e k o ju treb a zapisati; ovde je u p o tre b ljen a m e to d a sev ere( ). Za
prav ljen je zn a k o v n o g niza p o ru k e za zapisnik, hteli b ism o d a u p o tre b im o ispis steka
(engl. stack trace) na m e stu gde je izu zetak bačen , ali m eto d a printStackTrace( ) p o d ra -
z u m e v a n o n e daje String. D a b ism o d o b ili String, m o ra m o u p o tre b iti p rek lo p ljen u
printStackTrace( ) koja kao a rg u m e n t p rim a o b jek at java.io.PrintWriter. (Sve će to b iti
d e taljn o o b ja šn je n o u poglavlju Javin ulazn o -izla zn i sistem .) U koliko PrintWriter k o n -
s tru k to r u p re d a m o o b jek at tip a java.io.StringWriter, izlaz se m ože p re tv o riti u String
m e to d o m to S trin g ( ).
Iako je p ris tu p u p o tre b lje n u klasi Zapisivanjelzuzetka veo m a k o m fo ra n , zato što svu
in fra s tru k tu ru zapisivanja u g ra đ u je u sam izuzetak i stoga rad i a u to m a tsk i b ez ikakve in-
terven cije p ro g ra m e ra klijenta, češće se dešava d a hvatate i zapisujete tu đ e izuzetke, pa
p o ru k u za zap isn ik m o ra te d a generišete u b lo k u za o b ra d u izuzetaka:
p u b lic c la s s DodatniElementi {
p ub lic s t a t ic void f ( ) throws MojIzuzetak2 {
p rin t("Iz b acu jem MojIzuzetak2 iz metode f ( ) 11) ;
throw new M o jIzu zeta k2 ();
}
pu b lic s t a t ic void g () throws MojIzuzetak2 {
p rin t("Iz b acu jem MojIzuzetak2 iz metode g( ) " ) ;
throw new M ojIzuzetak2("Nastao u metodi g ( ) " ) ;
}
p ub lic s t a t ic void h () throws MojIzuzetak2 {
p rin t("Iz b acu jem MojIzuzetak2 iz metode h ( ) " ) ;
throw new M ojIzuzetak2("Nastao u metodi h ( ) " , 47);
}
pu b lic s t a t ic void m a in (S trin g [] args) {
tr y {
f0;
} catch(M ojIzuzetak2 e) {
e.p rin tS ta c k T ra c e (S y s te m .o u t);
}
try {
g();
} catch(M ojIzuzetak2 e) {
e .p rin tS ta c k T ra c e (S y s te m .o u t);
}
try {
h () ;
Poglavlje 12: Obrada grešaka pomoću izuzetaka 355
} catch(M ojIzuzetak2 e) {
e .p rin tS ta c k T ra c e (S y s te m .o u t);
S y s te m .e r r.p r in tln ("e .v re d n o s t() = " + e .v r e d n o s t ());
}
}
} /* Is p is :
Izbacujem MojIzuzetak2 iz metode f ( )
MojIzuzetak2: D etaljna poruka: 0 null
at D o d atn iElem en ti.f(D o d atn iElem en ti.java :2 2 )
at Dodatni E lem e n ti. mai n(DodatniE1em enti. java :3 4 )
Izbacujem MojIzuzetak2 iz metode g ()
MojIzuzetak2: D etaljna poruka: 0 Nastao u metodi g ()
a t D od atn iElem en ti.g(D odatniElem en ti. java :2 6 )
a t DodatniElem enti.m ain(Dodatni Elem en ti. java :3 9 )
Izbacujem MojIzuzetak2 iz metode h ()
M ojIzuzetak2: D etaljna poruka: 47 Nastao u metodi h ()
at D odatn iElem enti.h (D o datn iElem enti.java:30 )
at D odatniElem enti,m ain(D odatniElem enti.java:44)
e .v re d n o s t() = 47
* ///:-
Specifikacija izuzetaka
U Javi bi tre b alo da o bav estite p ro g ra m e ra k lijen ta koji poziva vašu m e to d u o izuzecim a
koje o n a m o že da generiše. To je učtivo, p o što o n aj koji poziva m e to d u ta d a ta č n o zna
kako d a u h vati sve m ogu će izuzetke. N aravno, ako je d o stu p a n izvorni k o d , p ro g ra m e r
k lijent bi m ogao da ga pregleda i p ro n a đ e sve n a red b e throw, ali se b iblioteke često ne
isp o ru č u ju sa izv o rn im k o d o m . Da bi se sprečilo pojavljivanje p ro b lem a , Java o b ezb eđ u je
sin ta k su (i prim orava vas da je k o ristite) koja o m o g u ću je da učtivo kažete p ro g ra m e ru
k lijen tu koje izuzetke g eneriše o d re đ e n a m eto d a. To je specifikacija izuzetaka i pojavljuje
se n ak o n spiska arg u m e n a ta , u deklaraciji m etode.
Specifikacija izuzetaka k o risti d o d a tn u rezerv isan u reč throws, iza koje sledi spisak
svih m o g u ć ih tipov a izuzetaka. D efinicija vaše m e to d e m ogla b i d a izgleda ovako:
void f () { / / . . .
N a ovaj n a č in biće uhvaćen svaki izuzetak, pa bi ovakav blok tre b a lo staviti na kraj liste
za o b ra d u . T im e se izbegava p re u z im a n je n ad ležn o sti o d blok o v a za o b ra d u k o n k re tn ih
izuzetaka, koji b i ev en tu a ln o m ogli da slede.
P ošto je klasa E x c e p tio n o sn o v a svih klasa izuzetaka koje su p ro g ra m e ru važne, ovim
p o stu p k o m ne d o b ija te m n o g o o d re đ en ijih in fo rm ac ija o izuzetku, ali m o žete da p o zo -
vete m e to d e njene natklase T h ro w ab Ie :
S trin g getM essage( )
S trin g g etL o caIized M essag e()
O n e d aju d e ta ljn u p o ru k u ili p o ru k u koja je p rilag o đ e n a o d re d e n o m lo k aln o m geo-
grafsk o m p o d ru č ju .
S trin g to S trin g ( )
Poglavlje 12: Obrada grešaka pomoću izuzetaka 357
p u b lic c la s s MetodeKlaseException (
p ub lic s t a t ic void main (S t r in g [] args) {
try {
throw new Exception("Evo iz u z e tk a '');
} catch(Exception e) {
p rin t("U h v a tio sam iz u z e ta k ");
p r i n t ( "getM essage(): " + e.g etM essag e()) ;
p rin t("g e tLo c a liz e d M e s sa g e (): " +
e .g etL o ca lized M essa g ef));
p r in t("to S tr in g (): " + e ) ;
p r in t (" p r in t S t a c k T r a c e (): " ) ;
e .p rin tS ta c k T ra c e (S y ste m .o u t);
}
}
}/ * Is p is :
Uhvatio sam izuzetak
getMessage( ) : Evo izuzetka
getLo calized M essag e(): Evo izuzetka
t o S t r in g ( ) : j a v a . 1ang.Exception: Evo izuzetka
p rin tS ta c k T r a c e ():
j a v a .1ang. Exception: Evo izuzetka
at MetodeKlaseException.m ain(M etodeKlaseException.ja v a :8 )
* ///:-
358 Misliti na Javi
//: izuzeci/KoJeZvao.java
// Programski pristup podacima o položaju nastanka izuzetka
// na steku izvršavanja.
p u b lic c la s s KoJeZvao {
s t a t i c void f ( ) {
// G eneriši izuzetak da bi na stek iz vrš a v a n ja upisao njegov
// položaj nastanka
try {
throw new E x ce p tio n ();
) catch (Exception e) {
for(StackTraceElem ent ste : e .g e tS ta c k T ra ce O )
System .ou t.p rintln (ste.getM etho dN am e()) ;
)
}
s t a t i c void g () { f ( ) ; }
s t a t ic void h () { g ( ) ; }
p u b lic s t a t ic void main ( S t r i ng[] args) {
f 0;
System.out.printl n ("------------------------------------- " ) ;
g();
S y s te m .o u t.p rin tln ("-------------------------------- " ) ;
h ();
}
} /* Is p is :
f
mai n
f
9
mai n
f
9
Poglavlje 12: Obrada grešaka pomoću izuzetaka 359
h
mai n
* ///:-
catch (Exception e) (
System.out.println("Generisan je izuzetak");
throw e;
}
/ / : izuzeci/Ponovnogenerisanje.java
// Prikaz metode f i 11In Stack T race()
Red gde se poziva m eto d a fillIn S ta c k T ra c e () p o staje novo m esto p o rek la izuzetka.
M oguće je p o n o v o gen erisati izu zetak koji je različit o d o n o g koji je uhvaćen. To p ro -
uzro k u je isti efekat k ao k o rišćen je m e to d e fillIn S ta ck T rac e ( ): g u b e se in fo rm acije o
izv o rn o m p o rek lu izuzetka, a p re o sta ju in fo rm a c ije koje p rip a d aju n o v o m generisanju:
Poglavlje 12: Obrada grešak<i, oomoću izuzetaka 361
//: izuzeci/PonovnogenerisanjeNovog.java
// Ponovno generisanje objekta koji se razlikuje or5 '^ivaćenog.
Ulančavanje izuzetaka
Č esto je p o tre b n o u h v atiti je d a n izu zetak i g en erisati d ru g i, ne izgubivši p o d a tk e o p rv o m
- to se naziva ulančavanje izuzetaka. P re objavljivanja JD K -a 1.4, p ro g ra m e ri su sam i m o -
rali da p išu k o d koji će očuvati p o d a tk e o p rv o b itn o m izu zetk u , ali sada sve p o tk lase o d
Throvvable im aju o p ciju d a u k o n s tru k to ru p rim e o b jek at uzrok (engl. cause). Predvide-
n o je d a uzrok b u d e p rv o b itn i izuzetak, čijim p ro sleđ iv an jem zadržavate položaj prv o g
izuzetka na steku, iako p rav ite i g en erišete d ru g i izuzetak.
Z anim ljivo je p rim e titi d a jed in e p o tk la se o d Throwable koje u k o n s tru k to ru p rim a ju
a rg u m e n t uzrok jesu tr i o sn o v n e klase izuzetaka: E rror (p o m o ć u koje JVM prijavljuje si-
stem ske greške), Exception i RuntimeException. A ko h o ćete da u lan čite d ru g e tipove
izuzetaka, u ra d ite to m e to d o m in itC au se( ), a n e k o n stru k to ro m .
Evo p rim e ra koji o m o g u ćav a d in am ič k o d o d av an je p o lja o b je k tu tip a Dinamicka-
Polja u v rem e izvršavanja:
try {
d p .s e tF ie ld ("d " , "Neka vrednost za d " ) ;
d p .s e t F ie ld (" b r o j" , 47);
d p .s e tF ie ld ("b r o j2 “ , 48);
p r in t ( d p ) ;
d p .s e tF ie ld ("d " , "Nova vrednost za d " ) ;
d p .s e tF ie ld ("b r o j3 ", 11);
p rin t("d p : " + d p );
p rin tC 'd p .g e tF ie ld (\ ''d \ ") : 11 + d p . g e t F ie ld ( " d " )) ;
Object p o lje = d p .s e t F ie ld (" d " , n u l l ) ; // Izuzetak
( catch(NoSuchFieldException e) {
e .p rin tS ta c k T ra c e (S y s te m .o u t);
} catch(Izuzetak D in am ick ihPolja e) {
e .p rin tS ta c k T ra c e (S y s te m .o u t);
}
}
} /* Is p is :
n u l l : n ull
n u ll: n u ll
n u l l : n ull
d: Neka vrednost za d
b ro j: 47
broj2 : 48
Svaki o b jek at tip a DinamickaPolja sađrži niz p arova Object-Object. Prvi o b jek at je
id e n tifik a to r p o lja (tip a String), a d ru g i v re d n o st polja koja m ože b iti bilo kojeg tip a sem
p ro sto g (bez o m o ta ča ). Kada p rav ite taj objekat, p ravite zasn o v an u p retp o stav k u o to m e
koliko v am p o lja treba. Č im pozovete setFie!d( ), o n a če p ro n ači p ostojeće, tako nazv an o
p olje ili će n a p ra v iti novo, i staviti u njega vašu v red n o st. U koliko joj p o n e sta n e p ro sto ra ,
d o d aće ga p rav ljen jem niza d u žeg za je d a n i k o p iran jem starih elem en ata u njega. Ako
p o k u šate d a u n u tr a stavite v re d n o st null, generisaće IzuzetakDinamickihPolja tako što
će ga n ap rav iti i m e to d o m in itC au se( ) u m e tn u ti NullPointerException kao uzrok.
Kao p o v ra tn u v re d n o st, m e to d a setField( ) pribavlja i sta ru v red n o st na m estu tog
polja m e to d o m getField( ) koja m o že generisati izuzetak NoSuchFieIdException. Ako
Poglavlje 12: Obrada grešaka pomoću izuzetaka 365
i f ( t == n u l1)
throvv new Nul 1P o in te rE x c e p tio n ();
//: izuzeci/NeHvataSe.java
// Zanemarivanje izuzetaka tipa RuntimeExceptions.
// {ThrowsException}
Već v id ite da je izuzetak tip a RuntimeException (i sve što je iz njega izvedeno) speci-
jalan slučaj, p ošto prevodilac ne zahteva specifikaciju ovakvih vrsta izuzetaka. Izlaz u si-
stem skom to k u grešaka (System.err) izgleda ovako:
D akle, odg o vor glasi: ako izuzetak tipa RuntimeException stigne sve d o m eto de m a in ()
a da ne b u d e uhvaćen, p rilik o m izlaska iz p ro g ram a poziva se m eto d a printStackTrace( ) za
taj izuzetak.
Im ajte n a u m u da u svom k o d u sm ete d a zan e m a ru jete sam o izuzetke tip a Runtime-
Exception, p o što za o stale izuzetke prevo dilac zahteva o b ra d u . Izuzetak tip a RuntimeEx-
ception z an e m a ru je te zato što p redstavlja grešku u p ro g ra m ira n ju :
1 . G reška k o ju n e m o žete da o b ra d ite (p rim e ra rad i, p rim a n je reference null k o ju m e-
to d i p ro sleđ u je p ro g ra m e r k lijent).
2. G reška k oju bi, kao p ro g ra m er, treb alo da p ro v erite u svom k o d u (np r. izuzetak
tip a ArrayIndexOutOfBoundsException znači d a je treb alo o b ra titi p a ž n ju n a d u -
ž in u n iza). Izu zetak koji n astaje na m estu 1, često p o staje p ro b le m n a m e stu 2.
Sada v am je ja sn o koliko je k o risn o p o sto jan je izuzetaka u o vo m slučaju - olakšavaju
p o s tu p a k p ro n a la žen ja i o tk la n jan ja grešaka.
Z anim ljivo je p rim e titi d a se Javina teh nik a o b rad e izuzetaka ne m ože klasifikovati kao
alatka specifične n am en e . O n a je projek to vana za rad s n ezgo dn im greškam a to k o m izvr-
šavanja koje se dešavaju usled delovanja činilaca van d om ašaja vašeg koda, ali je o d presu d -
n e važn o sti i za o d re đ e n e vrste p ro g ram ersk ih grešaka koje prevodilac n e m ože da otkrije.
Vežba 12: (3) Izm en ite p ro g ra m unutrasnjeklase/Sekvenca.java tako d a generiše od g o -
v araju ći izu ze tak ako p o k u ša te d a u m e tn e te previše elem enata.
Try {
// Čuvana o b la s t: Opasne ak tivn o s ti
// koje mogu da generišu A, B i l i C
} catch(A a l ) {
// Procedura za obradu s it u a c ije A
} catch (B b l)
// Procedura za obradu s it u a c ije B
} catch (C c l)
// Procedura za obradu s it u a c ije C
} fin a lly {
// A k tiv n o sti koje se uvek dešavaju
}
D a biste se uverili d a se o d re d b a fin a lly uvek izvršava, p o k re n ite sledeći p rog ram :
5 D estru k to r je funkcija koja se poziva kad god neki objekat prestan e da se koristi. Uvek se zna tačno
gde i kada d estru k to r biva pozvan. U jeziku C + + d e stru k to r se poziva autom atski, a i C# (koji je
m no g o sličniji Javi) u m e da au to m atsk i un išti n ep o treb n e objekte.
Poglavlje 12: Obrada grešaka pomoću izuzetaka 369
gotovo n ik ad nije pro b lem . Uz to, n e m a d e stru k to re koji b i bili pozivani. D akle, k ad a u Javi
treb a k o ristiti o d re d b u finally?
O d re d b a lin a lly je n e o p h o d n a k ad a u p rv o b itn o stan je m o ra te d a v ra tite n ešto što nije
m e m o rija . To m ože b iti zatv aran je o tv o ren e d ato tek e ili rask id veze s m režo m , b risan je
nečega što ste n acrta li n a e k ra n u , ili čak p ritisk a n je n ek o g prek id ača iz sp o ljn o g sveta, kao
što je m o d e lo v an o u sledečem p rim e ru :
p u b lic c la s s Prekidac {
p riv a te boolean s ta n je = f a ls e ;
p u b lic boolean re a d () { retu rn s ta n je ; }
p u b lic void u k lju c e n () { s ta n je = tru e ; p r i n t ( t h i s ) ;}
p u b lic void is k lju c e n () { s ta n je = f a ls e ; p r i n t ( t h i s ) ; }
p u b lic S trin g to S trin g O { retu rn s ta n je ? "u k lju ce n " : " is k lju c e n " ; }
} III--
/ / : iz u z e c i/ Iz u z e ta k P a liG a s i1.ja va
p u b lic c la s s Iz u z e ta k P a liG a s i1 extends Exception { } / / / :—
//: iz u z e c i/ P re k id a c P a liG a s i. ja va
// Čemu slu ž i odredba f i n a l l y ?
// : iz u z e c i/ U z F in a lly .ja v a
// F in a lly garantuje č iš ć e n je .
pu b lic c la s s U z F in a lly {
s t a t ic Prekidac pr = new P r e k id a c ();
p ub lic s t a t ic void m a in (S trin g [] args) {
try {
pr.u kl ju c e n ( ) ;
// Kod k o ji može da generiše iz u z e tk e ...
P r e k id a c P a liG a s i.f () ;
} c a tc h (Iz u z e ta k P a liG a s i1 e) {
S y s te m .o u t.p rin tln ("Iz u z e ta k P a liG a s i1 ");
} catc h (Iz u z e ta k P a liG a si2 e) {
S y s te m .o u t.p rin tln ("Iz u z e ta k P a liG a s i2 ");
} fin a lly {
p r . is k l ju c e n ( ) ;
;
} ‘
} /* Is p is :
ukl jucen
is k lju c e n
* ///:-
Iz re z u ltata v idite da nije v ažn o gde se v raćate iz klase koja sadrži o d re d b u finally.
Vežba 16: (2) P rep rav ite p ro g ra m ponovnaupotreba/CADSystem.java tako da pokažete
da se p ra v iln o čišćenje jem či čak i kada se p o v ra ta k rezerv isan o m rečju return obavi iz
sred in e b lo k a try-finally.
Vežba 17: (3) P reprav ite p ro g ra m polimorfizam/Zaba.java tako d a p rav iln o čišćenje
jem či b lo k o m try-finally, i p o k ažite d a to rad i čak i kada se rezerv isan o m rečju return
v ratite iz sre d in e b lok a try-finally.
p u b lic c la s s ZagubljenaPoruka {
void f ( ) throws VeomaVazanlzuzetak {
throw new VeomaVazanlzuzetakO;
}
void p o c is t iO throws T riv ija la n lz u z e ta k {
throw new T r iv ija la n lz u z e t a k ( ) ;
}
p u b lic s t a t ic void m a in (S trin g [] args) {
try {
ZagubljenaPoruka lm = new Z agu bljenaPoru ka();
try {
lm .f ( ) ;
} fin a lly {
lm .p o c is ti ( ) ;
}
} catch (Exception e) {
System .out .pri n t l n ( e ) ;
}
}
} /* Is p is :
T r iv ij a l a n izuzetak
* III--
V idite d a n e m a n azn ak a o p risu stv u izuzetka tip a VeomaVazanlzuzetak koji je zam e-
njen izuze tkom tip a Trivijalanlzuzetak u o d red b i finally. To je p rilićn o o zb iljan n e d o sta -
tak, je r znači da izuzetak m ože p o tp u n o da se izgubi i to na n ačin koji je m n o g o teže
o tk riti nego u g o rn je m p rim e ru . N a su p ro t to m e, u jeziku C + + se situacija, kad a se d ru g i
izuzetak generiše pre nego što je prvi o b ra đ e n , sm a tra velikom greškom u p ro g ra m ira n ju .
M o žd a će u b u d u ć im verzijam a Jave ovaj p ro b lem b iti ispravljen (s d ru g e stra n e , m e to d u
koja generiše izuzetak, kao što je p o cisti( ) u g o rn jem p rim e ru , o b ič n o ćete sm estiti u n u -
ta r o d re d b e try-catch).
374 Misliti na Javi
Još jed n o sta v n iji n ač in d a izgu b ite izu ze tak jeste o b ičan p o v ra ta k (p o m o ću rezervisa-
ne reči return) iz b lo k a finally:
Ograničenja izuzetaka
Kada redefinišete m e to d u , m o žete da generišete sam o izuzetke koji su zadati u verziji m e-
to d e iz o sn o v n e klase. To je k o risn o o g ran ičen je, p o što znači d a će k o d koji ra d i sa osnov-
n o m klaso m a u to m a tsk i ra d iti i sa sv ak om klasom izved en o m iz nje (što je, naravno,
o snov n i k o n c e p t o b jek tn o o rije n tisa n o g p ro g ra m ira n ja ), u k lju ču ju ći i izuzetke.
O vaj p rim e r ilu stru je v rste o g ra n ič e n ja koja se u v rem e p rev o đ en ja n am eć u izuzecim a:
ab s tra ct c la s s Inning {
p u b lic In n in g () throws Basebal1Exception {}
p u b lic void event () throws B asebal1Exception {
// Zapravo ne mora da generiše nikakav izuzetak
}
p ub lic a b s tra ct void a t B a t () throws S t r ik e , F o u l;
p ub lic void w alk () { } // Ne generiše izuzetke koje prevodilac proverava
Poglavlje 12: Obrada grešaka pomoću izuzetaka 375
in te rfa c e Storm {
p u b lic void e v e n t() throws RainedOut;
p u b lic void ra in H ard () throws RainedOut;
}
Poslednje što nas z a n im a je m eto d a m a in ( ). U njoj m o žete d a vidite sledeće: ako rad ite
sa o b je k to m tip a Storm yInning, p rev o d ilac vas p rim o ra v a d a hvatate sam o izuzetke koji
su specifični za tu klasu, ali ako k o n v ertu jete o b je k at u p ro s t tip , p rev od ilac vas s p ra v o m
p rim o ra v a da hvatate izuzetke o sn o v n o g tip a. Sva ova o g ran iče n ja čine te h n ik u za o b ra d u
izuzetaka m n o g o sn a ž n ijo m .6
K orisno je u v id eti sledeće: iako p rev od ilac to k o m n asleđ iv an ja p o d rž av a specifikacije
izuzetaka, o n e n isu d eo p o tp isa m eto d e koji se sastoji sam o o d im e n a m eto d e i tip o v a ar-
g u m e n a ta. Stoga ne m o žete d a p rek lo p ite m e to d e p re m a s p e đ fik a đ ja m a izuzetaka. P ored
toga, sam o zato što specifikacija izuzetka p o sto ji u verziji m e to d e iz o sn o v n e klase, ne
znači d a o n a m o ra d a p o sto ji i u verziji m eto d e u izvedenoj klasi. O vo se p rilič n o razlikuje
o d p rav ila nasleđivanja, k ad a m e to d a o sn o v n e klase m o ra d a p o sto ji i u izvedenoj klasi.
D ru g im rečim a, „interfejs specifikacije izuzetka" za o d re d e n u m e to d u m ože d a se suzi to -
k o m nasleđivanja i redefin isan ja, ali se neće p ro širiti, što je u p rav o su p ro tn o p ra v ilu za in -
terfejs klase to k o m nasleđivanja.
Vežba 20: (3) P ro m e n ite Storm yInning.java d o d a v a n je m izu zetk a tip a UmpireArgu-
m ent i m e to d a koji ga gen erišu . T estirajte iz m e n je n u h ijerarh iju .
Konstruktori
P rilikom pisanja koda sa izuzecim a, p o se b n o je v ažn o d a se uvek pitate: „Ako n a sta n e izu-
zetak, hoće li o b jek at biti p ra v iln o počišćen?“ U većini slučajeva sve je u red u , ali se u k o n -
stru k to rim a pojavljuje p ro b lem . K o n stru k to r stavlja o b jek at u b e z b e d n o p o č e tn o stanje,
ali m ože d a izvodi neke o p eracije, npr. o tv a ran je d ato te k a , koje se n e b rišu sve d o k kori-
sn ik ne završi rad sa o b je k to m i pozove sp ecijaln u m e to d u za čišćenje. A ko generišete izu-
zetak u n u ta r k o n stru k to ra , čišćenje se m o žd a neće p ra v iln o izvršiti. To znači da m o ra te
biti p o seb n o o b azriv i d o k sastavljate k o n stru k to r.
Pošto ste u p rav o savladali o d re d b u finally, m o ž d a m islite d a je o n a pravo rešenje. Ipak,
nije toliko jed n o sta v n o , p o što fin ally svaki p u t izvršava k o d za čišćenje. U koliko k o n -
s tru k to r b u d e p re k in u t to k o m svog izvršavanja, to m ože zn ačiti da nije usp eo d a n apravi
deo o b jek ta koji će biti počišćen u b lo k u finally.
U sledećem p rim e ru pravi se klasa U la z n a D a to te k a koja o tv a ra d a to te k u i o m o g u ću je
čitan je p o je d n o g reda. O n a k oristi klase F ile R ea d er i B u ffe re d R e a d e r iz sta n d a rd n e Ja-
vine U /I b iblioteke. O n jim a će biti reči u poglavlju Javin ula zn o -izla zn i sistem , ali o n e su
toliko jed n o sta v n e da ćete bez p ro b lem a ra z u m e ti n jih o v o o sn o v n o korišćenje:
p u b lic c la s s UlaznaDatoteka {
p riv a te BufferedReader in ;
U ISO sta n d a rd za C + + d o d ata su slična ograničenja kojim a se zahteva d a izuzeci izvedenih m eto da
b u d u isti kao izuzeci koje generiše m etoda osnovne klase, ili izvedeni iz njih. To je jed ini slučaj kada
je C + + zaista u stan ju da proverava specifikacije izuzetaka toko m prevođenja.
378 Misliti na Javi
U to m slučaju, m ožd a ćete p o d e liti k o d u nekoliko blokova try.) M eto d a close( ) m ogla bi
d a g eneriše izuzetak, p a se o n isp itu je i h vata čak i ako je g en erisan u n u ta r b lo k a d ru g e
o d re d b e catch - za Javinog p rev o d io ca, to je jo š sam o je d a n p a r v itičastih zagrada. N ako n
izvođenja lo k aln ih o peracija, p o n o v o se generiše izuzetak. To je u red u , po što k o n stru k to r
nije b io uspešan, a m e to d a k oja poziva n e bi tre b alo da p re tp o sta v i kako je ob jek at p ra -
v iln o n ap rav ljen i ispravan.
U o v o m p rim e ru , u k o m e se n e ko risti p re th o d n o p o m e n u ta te h n ik a in d ik ato ra , o d -
re d b a finally sasvim sig u rn o ttije m esto za zatv aranje d ato tek e m e to d o m close( ), p o što bi
se u to m slučaju zatvarala k ad g o d se izvrši k o n stru k to r. Ž elim o d a d ato tek a b u d e otvo -
re n a to k o m k o risn o g živ o tn o g veka o b jek ta ldase UlaznaDatoteka.
M eto d a citajRed( ) v raća objek at klase String koji sadrži sledeći red datoteke. O n a p o -
ziva m e to d u readLine( ) koja m ože d a generiše izuzetak, ali se taj izuzetak hvata, pa citaj-
R e d ( ) n e generiše nikakav izuzetak. P ri p ro jek to v an ju izuzetaka, p ro b lem je to što se ne
zna da li izuzetak treba p o tp u n o o b rad iti n a ov o m niv o u, d a li ga treb a o b ra d iti sam o de-
lim ičn o i isti (ili drugačiji) izuzetak p ro sled iti dalje, ili ga valja p ro sle d iti bez ikakve o brade.
Prosleđivanje, kada je odgovarajuće, sig u rn o m o že da p o jed n o sta v i pisan je koda. U ovoj
situaciji, m e to d a citajRed( ) pretvara izuzetak u RuntimeException d a bi ukazala n a p ro -
g ra m sk u grešku.
K orisnik m o ra da pozove m e to d u ciscenje( ) k ad a završi s ko rišćen jem o bjekta klase
UlaznaDatoteka. N a taj n ačin se m o g u o slo b o d iti sistem ski resu rsi (npr. id en tifik ato ri
d a to te k a ) koje koriste o b jek ti tip a BufferedReader i/ili FileReader. To n e bi treb a lo da ra-
dite d o k ne završite s k o rišćen jem objekta H ase UlaznaDatoteka. M o žd a n am erav ate da
stav ite tu fu nkciju u m e to d u finalize( ), ali kao što je p o m e n u to u poglavlju Inicijalizacija
i čišćenje, ne m ožete uvek b iti sig u rn i da će m eto d a finalize( ) biti p o zv an a (čak i ako ste
sig u rn i da će biti pozvana, n e zn ate kada će se to d esiti). To je je d a n o d n e d o sta ta k a Jave:
n ije d n o čišćenje, osim b risan ja m em o rije, ne izvršava se a u to m a tsk i. Z ato p ro g ra m e ra
klijenta m o rate obavestiti d a je o n o d g o v o ran za čišćenje.
N ajbezbedniji način korišćenja klase koja m o že g en erisati izuzetak to k o m k o n stru k c i-
je i koja zahteva čišćenje jeste u p o tre b a u g n ežđ en ih b lokova try:
p ub lic c la s s C iscenje (
pu b lic s t a t ic void m a in (S trin g [] args) {
try {
UlaznaDatoteka in = new U la z n a D a to te k a ("C is c e n je .ja v a ");
try {
S trin g s;
in t i = 1;
w h ile ((s = in .g e tl_ in e ()) != n u ll)
; // Ovde obradi red po r e d ...
} catch(Exception e) {
S y ste m .o u t.p rin tln ("U h va tio izuzetak u metodi m ain ");
e .p rin tS ta c k T ra c e (S y ste m .o u t);
380 Misliti na Javi
} finally {
in.ciscenjeO;
}
} catch(Exception e) {
S y s te m .o u t.p rin tln ("N ije uspela k o n stru k cija U lazneD atoteke");
}
}
} /* Ispis:
ciscenje() uspešno
* ///:-
Pažljivo ra z m o trite u p o tre b lje n u logiku: k o n stru k cija o b jek ta tip a UlaznaDatoteka
su štin sk i je u so p stv en o m b lo k u try. A ko ta k o n s tru k đ ja ne u sp e, ulazi se u sp oljni b lo k
catch, a ciscenje( ) se n e poziva. M e đ u tim , uk o lik o k o n stru k c ija usp e, m o ra te se p o sta ra ti
da o b jek at b u d e počišćen, p a n e p o sre d n o n ak o n k o n stru k c ije p rav ite n o v b lo k try. O d -
re d b a finally koja obavlja čišćenje p rid ru ž e n a je u n u tra šn je m b lo k u try; zato se b lo k fi-
nally neće izvršiti ako k o n stru k c ija n e u sp e, a izvršava se uvek k ad a k o n stru k c ija uspe.
O vaj o pšti n ač in čišćenja treb a p rim e n jiv a ti i k ad a k o n s tru k to r u o p šte ne generiše izu-
zetke. O sn o v n o pravilo glasi: n e p o sre d n o n a k o n p rav ljen ja o b jek ta koji zahteva čišćenje,
o tv o rite b lo k try-finally:
p u b lic c la s s N acinCiscenja {
p u b lic s t a t i c void m a in (S trin g [] args) {
// Deo 1:
T r e b a Je P o c is titi ncl = new T r e b a Je P o c is titi ( ) ;
try {
/ / •••
} fin a lly {
n c l. c i s c e n j e ( ) ;
}
Poglavlje 12: Obrada grešaka pomoću izuzetaka 381
// Deo 2:
// Ako k o n stru k cija ne može da ne uspe, objekte možete da grup išete:
T r e b a Je P o c is titi nc2 = new T r e b a Je P o c is t it i( ) ;
T r e b a Je P o c is titi nc3 = new T r e b a Je P o c is t it i( ) ;
try (
//
} fin a lly (
n c 3 .c is c e n je (); // Obrnut redosled u odnosu na konstru kciju
n c 2 .c is c e n je ();
}
// Deo 3:
// Ako k o n stru k cija može da ne uspe, morate ču vati svaku od n jih :
try {
T r e b a Je P o c is titi2 nc4 = new T r e b a J e P o c is t it i2 () ;
try {
T re b a Je P o c is titi2 nc5 = new T r e b a Je P o c is t it i2 ();
tr y {
/ / •••
} fin a lly {
n c 5 .c is c e n je ();
}
} catch(IzuzetakTokom Konstrukcije e) { // konstruktor nc5
S y s te m .o u t .p r in tln (e );
} fin a lly {
nc4 .ci s c e n j e () ;
}
} catch(IzuzetakTokom Konstrukcije e) { // konstruktor nc4
S y s te m .o u t .p r in tln (e );
}
}
} /* Is p is :
T r e b a Je P o c is titi 1 počišćena
T r e b a Je P o c is titi 3 počišćena
T r e b a Je P o c is titi 2 počišćena
T r e b a Je P o c is titi 5 počišćena
T r e b a Je P o c is titi 4 počišćena
* ///:-
U m e to d i m a in ( ), d eo 1 je lako razu m eti: iza o b jek ta koji treb a p o č istiti sledi b lo k try-
finally. U k o lik o k o n stru k cija o b jek ta ne m o že da ne uspe, b lo k catch nije p o tre b a n .
U d elu 2, v id ite da ob jek ti čija k o n stru k c ija ne m ože da ne u sp e m o g u b iti g ru p isan i i za
k o n stru k c iju i za čiščenje.
D eo 3 p o k a z u je kako se rad i sa o b jek tim a čija k o n stru k c ija m ože d a n e u sp e i koji zah-
tevaju čiščenje. P raviln a o b ra d a ove situacije znači pravljenje krša, je r svaku k o n stru k ciju
m o ra te da o b u h v a tite so p stv en im b lo k o m try-catch, a n ak o n njega m o ra da sledi blok
try-finally za zaje m čen o čišćenje.
R užnoća k od a za o b ra d u izuzetaka u o v o m slučaju, p redstavlja ja k a rg u m e n t za p ra-
vljenje k o n s tru k to ra koji ne m o g u da ne usp eju , m ad a to nije uvek m oguće.
382 Misliti na Javi
p u b lic c la s s Covek {
p ublic s t a t ic void m a in (S trin g [] args) (
// Uhvati tačan tip
try {
throw new K i j a n j e ( ) ;
} c a tc h (K ija n je s) {
S y s te m .o u t.p rin tln ("U h v a tio K i j a n j e " ) ;
} catch(Dosada a) {
System .out. pri n tln ("U h v a ti o Dosadu");
}
}
} /* Is p is :
Caught Sneeze
Caught Annoyance
) III--
Poglavlje 12: Obrada grešaka pomoću izuzetaka 383
Izu zetak tip a Kijanje uhv atiće p rv a o d re d b a catch kojoj odg ov ara, a to je, n arav n o , p r-
va. M e đ u tim , ako u k lo n ite p rv u o d re d b u catch i ostavite sam o o d re d b u catch za klasu
Dosada, k o d će i dalje rad iti, je r b lo k hvata o sn o v n u klasu klase Kijanje. D ru g im rečim a,
catch (Dosada e) u h vatiće izu ze tak tip a Dosada i sve klase koje su izvedene iz tog tipa. To
je k o risn o : ako o d lu č ite d a m eto d i d o d a te više izv edenih izuzetaka, k o d p ro g ra m e ra kli-
je n ta neće m o ra ti da se m en ja sve d o k klijen t o b ra đ u je izuzetke o sno v n e klase.
A ko p o k u ša te d a ,,m askirate“ izuzetke izvedene klase ta k o što n a prvo m esto stavite o d -
re d b u catch za h v atan je o sn o v n e klase, kao ovde:
try {
throw new K i j a n j e ( ) ;
} catch(Dosada a) {
/ / •••
} c a tc h (K ija n je s) {
/ / •••
}
p rev o d ilac će p rik a zati grešku, p o što vid i d a o d re d b a za h vatanje Kijanje nije d o stu p n a .
Vežba 25: (2) N ap rav ite h ije ra rh iju izu zetaka u tr i nivoa. P o to m n ap rav ite o sn o v n u klasu
A s m e to d o m koja g eneriše izuzetak u o sn o v i h ijerarhije. Izvedite klasu B iz A i redefini-
šite m e to d u tako d a g eneriše izu zetak u d ru g o m n iv o u h ijerarh ije. P ono vite p o stu p a k i iz-
vedite klasu C iz klase B. U m eto d i m a in () n ap rav ite o b jek at klase C i svedite ga naviše n a
tip A, a zatim p o zo v ite m e to d u .
Drugi pristupi
Sistem za o b ra d u izuzetaka predstavlja skrivena v rata koja p ro g ra m u o m ogućavaju da na-
p u sti izvršavanje n o rm a ln e sekvence n ared be. Skrivena v rata se up otrebljavaju kad a n astu -
pi „izuzetan uslov“, p o p u t o n o g a kada n o rm a ln o izvršavanje više nije m oguće ili poželjno.
Izuzeci predstavljaju uslove koje teku ća m eto d a ne m o že d a o b rad i. Sistem i za o b ra d u izu-
zetaka su razvijeni zato što je p ristu p koji je p o d ra z u m e v a o o b ra d u svih m og ućih greška
izazvanih svakim p ozivom svake funkcije bio previše teg o ban , pa ga p ro g ram eri n isu sle-
đili. Stoga su greške ignorisali. Vredi p rim etiti da je olakšavanje p ro g ram ira n ja o b ra d e
grešaka bilo glavna m otivacija za izum izuzetaka.
Jedna o d v ažnih sm e rn ic a u o b ra d i izuzetaka glasi: ,,Ne hvataj izuzetak s kojim n e znaš
šta da u ra d iš“. U stvari, je d a n o d važn ih cilje v a o b ra d e izuzetaka bio je d a s e k o d za o b ra d u
grešaka o d m a k n e o d tačke gde je greška n astala. T im e d o b ijate m o g u ć n o st da se u je d n o m
delu svog k oda u sred sred ite na o n o što h o ćete da u rad ite , a d a se u d ru g o m , zasebn om
d elu bavite p ro b le m im a . Stoga glavni deo vašeg k o d a neće b iti z atrp a n logikom za o b ra d u
grešaka, pa će b iti m n o g o razum ljiviji i lakše će se održavati. O b ra d a izuzetaka o b ičn o
sm a n ju je o b im koda za o b ra d u grešaka, je r je d a n b lo k za o b ra d u m ože d a o b ra d i greške
nastale na m n o g o m esta.
384 Misliti na Javi
Proveren i izuzeci donekle k o m p lik u ju ovaj scenario, p o što vas p rim o ra v a ju da stavlja-
te o d re d b e c a tc h i n a m esta na k o jim a n iste sp re m n i da o b ra d ite o d re đ e n u grešku. To
p ro u z ro k u je p ro b le m „izaziva oštećenja ako se p ro g u ta “:
try {
II . . . radi se nešto korisno
} catch(0bavezanlzuzetak e) { } // Progutano!
Istorija
O b ra d a izuzetaka je n astala u sistem im a PL/1 i M esa. K asnije se pojavljivala u jezicim a
CLU, Sm alltalk, M o d u la-3 , Ada, Eiffel, C + + , P y th o n , Java, kao i u jezicim a R uby i C # na-
stalim n a k o n Jave. U to m p o g led u Java je slična C ++-U , sem na m e stim a gde su pro jek -
ta n ti Jave sm atra li da p ristu p iz C + + -a p ro u z ro k u je p ro b lem e.
D a bi se p ro g ra m e rim a p ru ž io sistem za o b ra d u i o p o ra v a k o d grešaka koji će im ati
više izgleda da b u d e u p o treb ljav an , jeziku C + + je o b ra d a izuzetaka d o d a ta relativ n o ka-
sno u p ro cesu stand arđizacije. N ju je zagovarao B jarne S tro u stru p , a u to r jezika. M odel
izuzetaka u jezik u C + + p rv en stv en o p o tiče iz C LU -a. M e đ u tim , tad a su posto jali i d ru g i
jezici koji su p o držav ali o b ra d u izuzetaka: A da, Sm alltalk (im ali su izuzetke, ali ne i spe-
cifikacije izuzetaka) i M o d u la-3 (koja je im ala i izuzetke i specifikacije).
Poglavlje 12: Obrada grešaka pomoću izuzetaka 385
Perspektive
Prvo, vredi primetiti da su tvorci Jave zapravo izumeli proverene izuzetke (očigledno in-
spirisani specifikacijama izuzetaka u C++-u i činjenicom da se C++ programeri obično
ne bakću s njima). Međutim, taj eksperiment nije ponovljen ni u jednom kasnijem jeziku.
Drugo, provereni izuzeci izgledaju kao očito korisni u uvodnim primerima i malim
programima. Rečeno je da se suptilne razlike pojavljuju kada programi postanu veliki.
Naravno, do tolike veličine obično ne dolazi preko noći; ona nastaje neosetno. Jezici koji
nisu podesni za velike projekte, upotrebljavaju se za male. Ti mali rastu i od nekog trenut-
ka shvatamo da su od ,,upravljivih“ postali „teško upravljivi". Smatram da to može biti
posledica i preteranog proveravanja tipova; konkretno, provere izuzetaka.
Izgleda da je veličina programa značajna. To je problem, pošto se u većini rasprava za
ilustraciju upotrebljavaju mali programi. Jedan od projektanata C#-a kazao je sledeće:
„Ispitivanje malih programa navodi na zaključak da zahtevanje specifikacija izuzetaka
može povećati produktivnostprogramera i kvalitet koda, ali iskustvo s velikim softverskim
projektima nameće drugačiji zaključak - smanjuje se produktivnost a kvalitet koda se
povećava malo ili nimalo.“8
Autori CLU-a su kazali sledeće o neuhvaćenim izuzecima:
„Stnatrali smo da tiije realno zahtevati od programera da piše blok za obradu u situaci-
jam a kada se ne m ožepreduzeti ništa smisleno.“9
Kada objašnjava zašto deklaracija funkcije bez specifikacije znači da ona može generi-
sati bilo koji izuzetak, a ne da uopšte ne može generisati izuzetak, Stroustrup kaže:
,,To bi zahtevalo specifikacije izuzetaka za gotovo sve funkcije, predstavljalo bi značajan
uzrok ponovnog prevođenja i sprečilo bi saradnju sa softverom napisanim na drugitn
jezicima. To bi navclo programere da sabotiraju tnehanizme za obradu izuzetaka i da pišu
maskirni kdd koji sakriva izuzetke. Stoga bi Ijudi koji nisu uočili izuzetak stekli lažno
osećanje bezbcdnosti “w
Vidimo da se baš takvo ponašanje - sabotiranje izuzetaka - dešava s proverenim izu-
zecima u Javi.
Martin Fovvler (autor knjiga UML Distilled , Refactoringi Analysis Patterns) napisao mi
je sledeće:
,,...sve u svemu, smatram da su izuzeci korisni, ali Javini provereni izuzeci više koštaju
nego što vrede."
Sada smatram da je važan doprinos Jave bio to što je objedinila model prijavljivanja
grešaka, tako da se sve greške prijavljuju pomoću izuzetaka. Toga nije bilo u C++-u, zato što
je, zbog kompatibilnosti sa C-om, još uvek bio na raspolaganju stari model prostog igno-
risanja grešaka. Ali ako imate dosledno prijavljivanje pomoću izuzetaka, možete ih koristiti
ako hoćete, a ako ne, oni će se popeti na najviši nivo (konzolu ili drugi kontejnerski
saznati u poglavlju Javin u la zno -izla zn i sistem ), m o ra te o tv o riti i zatv o riti Filelnput-
Stream koji generiše izuzetke. U je d n o s ta v n o m p ro g ra m u m o žete u ra d iti ovo (isti p ristu p
je u p o treb ljen u m n o g im p rim e rim a u ovoj knjizi):
p u b lic c la s s M ainlzuzetak {
// Prosledićemo sve izuzetke na konzolu:
p ub lic s t a t ic void m a in (S trin g [] args) throws Exception {
// O tvaranje datoteke:
Filelnp u tStream datoteka =
new F ile In p u tS tre a m ("M a in Iz u z e ta k .ja v a ");
// Upotreba datoteke . . .
// Z atvo ri datoteku:
d a to te k a .c lo s e ();
}
} lll- ~
tr y {
// __ ra d ite korisne s tv a ri
} catch(NeZnamStaDaRadimSaOvimproverenim Izuzetkom e) {
throw new Runtim eException(e);
}
// : iz u z e c i/ Is k lju c iv a n je P ro v e r e .ja v a
// " I s k lju č iv a n je " provere izuzetaka.
import j a v a . io . * ;
import s t a t ic rie t.m ind view .u til . P r i n t . * ;
c la s s Omotajproverenlzuzetak {
void throwRuntim eException(int t i p ) {
try {
swi t c h ( t i p) {
case 0: throw new FileN o tFo u n d Ex cep tio n ();
case 1: throw new I0 E x c e p tio n ();
case 2: throw new RuntimeException("Gde sam?11) ;
d e fa u lt: re tu rn ;
}
} catch(Ex ception e) { // Prilag o đ e n je za neproverene:
throw new Runtim eException(e);
}
}
pu b lic c la s s Is k lju c iv a n je P r o v e re {
p u b lic s t a t i c void m a in (S trin g [] args) {
Omotajproverenlzuzetak opi = new O m otajproverenlzuzetak();
// Možete pozvati throwRuntimeException() bez bloka t r y
// i o s t a v it i RuntimeException da napusti metodu:
o p i.throw Runtim eException(3);
// A možete o d lu č iti i da hvatate izuzetke:
f o r ( i n t i = 0 ; i < 4 ; i++)
tr y {
i f ( i < 3)
o p i.th ro w R u n tim eE x cep tio n (i);
el se
throw new N e k iD ru g iIz u z e ta k ();
} catch(N ekiD rugiIzuzetak e) {
p rin t("N e k iD ru g iIz u z e ta k : " + e );
} catch(Runtim eException re) {
try {
throw re .g e tC a u s e O ;
} catch(FileN otFoundException e) {
p r i n t ( " F i 1eNotFoundException: " + e ) ;
} catch(IO Exception e) {
p rin t("IO E x c e p tio n : " + e ) ;
390 Misliti na Javi
} catch(Throwable e) {
p rin t("T h ro w ab le: 11 + e ) ;
}
}
}
} /* Is p is :
F i 1eNotFoundException: j a v a . i o . F i 1eNotFoundException
IOException: ja v a .io .IO E x c e p tio n
Throwable: j a v a . 1ang.Runtim eException: Gde sam?
NekiDrugiIzuzetak: Neki DrugiIzuzetak
* ///:-
Uputstva za izuzetke
K oristite izuzetke da:
1. O b ra d ite p ro b lem e na o d g o v araju ćem nivou. (Izbegavajte h v atan je izuzetaka s ko-
jim a ne zn ate šta da rad ite.)
2. Rešite p ro b lem i p o n o v o po zo v ete m e to d u koja je p ro u zro k o v ala izuzetak.
Poglav|je 12: Obrada grešaka pomoću izuzetaka 391
Sažetak
Izuzeci su n ezao bilazan d eo p ro g ra m ira n ja n a Javi; ako n e zn ate d a rad ite s n jim a, nećete
baš m n o g o postići. Z ato sm o izuzetke p red stav ili n a ovom m estu - im a m n o g o bib lio tek a
(k ao ran ije sp o m e n u ta b ib lio tek a U /I) koje u o p šte n e m o žete d a u p o treb ljav ate b ez p o -
znav an ja izuzetaka.
Jed n a o d p re d n o sti o b ra d e izuzetaka jeste sledeća: om o g u ćav a d a se n a je d n o m m estu
u sred sred ite n a p ro b lem koji p o k u šav ate d a rešite, a d a se n a d ru g o m m e stu bavite gre-
šk am a u to m k o du. I m a d a se izuzeci p o p rav ilu sm atraju alatk am a koje om o g u ćav a ju p ri-
javljiva nje i oporavak od grešaka u v re m e izvršavanja, p ita m se koliko često se aspekat
„oporavka" realizuje, pa čak i koliko često je to u o p šte m oguće. Po m o m osećaju, to je
m an je o d 10 p osto v rem en a, p a čak i tad a se v ero v atn o svodi na otpetljav an je steka d o
p o z n a to g stab iln o g stan ja, a ne d o stv arn o g nastavljanja rada. Bila to istina ili ne, sm a tra m
da o sn o v n a v re d n o st izuzetaka leži u njih o v o j fu n k ciji ,,prijavljivanja“. Č in jen ica d a u Javi
svegrešk e valjap rijav ljiv ati u o b lik u izuzetaka d a je jo j veliku p re d n o st n ad jezicim a p o p u t
C + + -a , koji dozvoljavaju prijavljivanje grešaka n a više različitih načina, a m o žete i da ih
u o p šte ne prijavite. D osledan sistem za prijavljivanje grešaka znači da se više ne m o rate
pitati: „Da li su m i negde p ro cu rile greške?“ n a k o n pisanja svakog parčeta k o d a (ukoliko
ne „ p ro g u ta te “ izuzetke, narav n o !).
Kao što ćete videti u sledećim p oglavljim a, u koliko rešite ovo p itan je - čak i ako to u ra-
d ite g en erišući R u n tim e E x c e p tio n — svoj tr u d p ri p ro jek to v an ju i realizaciji m ožete
u sred sred iti na zanim ljivije i teže p ro b lem e.
R ešenja o d a b r a n ih vežbi d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in Java Annotated Solu-
tion Guide , koji se m o ž e k u p iti n a lokaciji www.BruceEckel.com.
Znakovni nizovi
T vrd i se d a o b ra d a z n a k o v n ih n iz o v a sp ad a m eđu n a ič e š ć e a k t i v n o s t i u
p ro g ra m ira n ju . To je p o g o to v o ta č n o u W eb sistem im a, gde se Java m n o g o k oristi.
U o v o m po g lavlju detaljn ije ćem o ra z m o triti najčešće k o rišćen u klasu to g jezika, String,
k ao i neke n je n e p rateće i p o m o ć n e m eto d e.
p u b lic c la s s Nepromenljiv {
p u b lic s t a t i c S trin g v e ls lo v a (S t r in g s) {
return s . tollpperCase ( ) ;
}
p u b lic s t a t i c void m a in (S trin g [] args) {
S trin g q = "zd ravo";
p r i n t ( q ) ; // zdravo
S trin g qq = u p case(q );
p ri n t (q q ); // ZDRAVO
p r i n t ( q ) ; // zdravo
}
} /* Is p is :
zdravo
ZDRAVO
zdravo
* ///:-
// : znakovninizovi/N adovezivanje.java
p u b lic c la s s Nadovezivanje {
p u b lic s t a t ic void m a in (S trin g [] args) {
S trin g mango = "mango";
S trin g s = "abc" + mango + "d e f" + 47;
S y s te m .o u t .p r in tln (s );
)
} /* Is p is :
abcmangodef47
* ///:-
javap -c Nadovezivanje
U koliko ste rad ili sa asem b le ro m , ovo b i treb alo da vam izgleda p o zn ato —n a re d b e p o -
p u t dup i invokevirtual p rip a d a ju a sem b leru Javine v irtu e ln e m ašine (JV M ). Ako prv i
p u t u živ otu v id ite asem bler, ne u p u šta jte se - važno je u očiti d a p revodilac uvodi klasu ja-
va.lang.StringBuilder. U izv o rn o m k o d u se StringBuilder nije sp o m in jao , ali je prevo-
dilac o d lu čio d a ga ipak u p o tre b i, zato što je m n o g o efikasniji.
U ovom slučaju, p revodilac pravi o bjek at tip a StringBuilđer da bi n apravio znakovni
niz s i četiri p u ta poziva a p p e n d ( ), p o je d n o m za svaki o d delova. N ajzad, poziva
to S tring ( ) da bi nap rav io rezultat, koji sp rem a (asem blerskom n ared b o m astore_2) kao s.
Pre nego što zaključite d a je d o v o ljno svu da u p o treb ljavati objekte tipa String i p rep u -
stiti p rev o d io cu da se sta ra za efikasnost, p o g led ajm o šta rad i prevodilac. Evo p rim e ra
koji p rav i String na dva n ačin a : k oristeći objekte tip a String, o d n o sn o ru č n im p ro g ram i-
ran je m p o m o ć u klase StringBuilder:
//: zn ak ov ni ni zo vi/KudaStringBuilder.java
public class Ku da St ri ng Bu il de r {
public String implicitno(String[] polja) {
String rezultat =
for(int i = 0; i < pol j a . 1ength; i++)
rezultat += pol j a [ i ] ;
return rezultat;
1
Poglavlje 13: Znakovni nizovi 395
O b ra tite p ažn ju na redove 8: i 35: koji zajedno čine petlju. Red 8: sadrži n are d b u „integer
co m p are g reater th an o r equal to “ (celobrojnog p o red en ja veče ili jed n ak o ) o p eran a d a na
steku i skače na 38: kada petlja završi rad. Red 35: (n ared b a goto) je v raćanje n a p o četa k
petlje, red 5:. Važno je p rim etiti da se konstrukcija StringBuildera odigrava u n u ta rte petlje,
što znači da ćete d o b iti n o v objekat tip a StringBuilder svaki p u t kada p ro đ ete kroz petlju.
O vo su b ajtk o d m e to d e eksplicitno():
8: iconst_0
9: isto re _3
10: iload_3
11: alo ad _l
12: arraylength
13: if_icm pge 30
16: aload_2
17: alo ad _l
18: iload_3
19: aaload
20: in v o k e virtu a l #5; // S trin g B u ild e r.a p p e n d :()
23: pop
24: iin c 3, 1
27: goto 10
30: aload_2
31: in v o k e virtu a l #6; // S t r in g B u ild e r . t o S t r in g :()
34: areturn
N e sam o d a je p etlja k raća i jed n o stav n ija, n eg o m eto d a p rav i sa m o jed a n o b jek at tip a
StringBuilder. E ksplicitno p rav ljen je StringBuildera o m o g u ćav a d a u n a p re d zadate nje-
govu veličinu u koliko zn ate koliki treb a d a b u d e, tak o da ne m o ra uvek iznova da pravi
sebi bafer.
D akle, kada pravite m e to d u to S trin g ( ), ako su o p eracije jed n o stav n e pa prevodilac
m o že sam d a ih shvati, p o p ra v ilu p re p u stite n jem u d a rezu ltat izraču n a na razb o rit
način . Ali ako p ro g ra m o b u h v a ta k ru že n je k ro z p etlju , u m e to d i to S trin g ( ) treb a ekspli-
citn o da n avedete StringBuilder, ovako:
Nenamerna rekurzija
Pošto su Javini sta n d a rd n i k o n tejn eri, kao i sve d ru g e klase, nasleđeni iz klase Object, oni
sadrže m e to d u to S trin g ( ). O n a je redefinisana d a b i k o n tejn eri, kao i o bjekti koje sadrže,
m o g li d a se predstav e u oblik u zn akovnog niza, tj. tip a String. U n u ta r klase ArrayList, n a
p rim er, m e to d a to S trin g ( ) prolazi kroz elem en te k o n tejn era i poziva m e to d u toS tring( )
svakog elem en ta.
p u b lic c la s s Is p is iv a n je A rr a y L is te (
p u b lic s t a t i c void m a in (S trin g [] args) {
ArrayList< Coffee> kafe = new A rra y List< C o ffe e > ();
fo r(C o ffe e c : new C o ffe eG en erato r(lO ))
k a fe .a d d (c );
System .out. p r in t l n ( k a f e ) ;
}
} /* Is p is :
[Americano 0, L a tte 1, Americano 2, Mocha 3, Mocha 4, Breve 5,
Americano 6, L a tte 7, Cappuccino 8, Cappuccino 9]
* ///:-
//: znakovni_nizovi/BeskonacnaRekurzija.ja va
// S lu ča jn a re k u rz ija .
// {RunByHand}
398 Misliti na Javi
import j a v a . u t i l .* ;
pu b lic c la s s BeskonacnaRekurzija {
p ub lic S trin g to S trin g O {
return " Adresa objekta klase BeskonacnaRekurzija: 11
+ th is + "\ n ";
}
public s t a t ic void m a in (S trin g [] args) {
List<BeskonacnaRekurzija> v = new A rrayList< Beskon acn aRekurzija> ();
f o r ( i n t i = 0; i < 10; i++)
v.add(new B eskon acn aReku rzijaO );
S y s te m .o u t.p rin tln (v );
}
} III-.-
A ko n ap ra v ite objek at klase BeskonacnaRekurzija i zatim ga ispišete, d o b ićete bes-
k raja n n iz izuzetaka. To će se d esiti i ako stavite o b jek te klase BeskonacnaRekurzija u
n ek i k o n tejn er klase ArrayList i ispišete taj k o n te jn e r k ao što je o vde p rik a z an o . Z apravo
se dešava au to m a tsk a konverzija tip a u String. K ada kažete:
prevodilac vidi String za kojim sledi + i n ešto što nije String, p a p o k u šav a d a konvertuje
this u String. Konverzija se obavlja pozivom m eto d e toS tring( ) koja daje rekurzivan poziv.
A ko zaista želite da ispišete ad resu o b jek ta u o v o m slučaju, rešenje je da se pozove m e-
to d a O bject.toString( ), koja to radi. D akle, u m esto this, treb a n a p isa ti super.toString( ).
V ežba 2: (1) Popravite program B eskonacnaR ekurzija.java.
Formatiranje izlaza
Jed n a o d d u g o priželjkivanih m o g u ć n o sti koja se n ajzad pojavila u Javi SE5 jeste fo rm a-
tira n je izlaza u stilu C -ove n a re d b e p r in tf( ). N e sam o d a to p o jed n o stav lju je izlazni k o d ,
neg o p ro g ra m e rim a u Javi daje m o ć n u alatk u za o d ređ iv an je fo rm a tira n ja i p o rav n av an ja
izlaza.2
Metoda printff)
C -o v a fun k cija p rin tf( ) n e sastavlja nizove kao Java, n ego p rim a zn a k o v n i n iz za fo r m a ti-
ranje i u njega um eće v red n o sti, fo rm a tira ju ć i u h o d u . U m esto d a za n ad o v eziv an je teksta
i p ro m e n ljiv ih u n u ta r n av o d n ik a u p o treb lja v a p rek lo p ljen o p e ra to r + (koji u C -u nije
p re k lo p ljen ), p rin tf ( ) u p o tre b lja v a p o se b n e o znake koje p o k a z u ju g d e tre b a u m e tn u ti
p o d a tk e . Iza n jih sledi lista a rg u m e n a ta koji se u m eću u zn ak o v n i n iz za fo rm a tira n je ,
m e đ u s o b n o od v ojenih zarezim a.
Na p rim er:
System.out.format()
U Javu SE5 je uv ed en a m eto d a fo rm a t( ), d o stu p n a o b je k tim a tip a P rin tS tre a m ili P rin t-
W riter (o kojim a ćete više saznati u poglavlju Javin ulazno-izlazni sistem ), koja o b u h v ata
sta n d a rd n i izlazni to k System.out. M eto d a fo rm a t( ) je n ap rav ljen a p o u z o ru na C -o v u
fu n k ciju p rin tf( ). Za nostalgičare su n ap rav ili čak i m e to d u p rin tf ( ) koja sam o poziva
m e to d u fo rm a t( ). Evo je d n o sta v n o g p rim era :
/ / : zn ak ovninizovi/JednostavnoForm atiranje.java
// S t a r i način:
Syste m .o u t.p rin tln ("R e d 1: [" + x + " 11 + y +
// Novi način:
System .out.form at("Red 1: [%d % f]\ n ", x, y ) ;
// i l i
Sy ste m .o u t.p rin tf("R e d 1: [%d % f]\ n ", x, y ) ;
}
} /* Is p is :
Red 1: [5 5.332542]
Red 1: [5 5.332542]
Red 1: [5 5.332542]
* ///:-
Klasa Formatter
Svu n o v u Javinu fu n k cio n a ln o st za fo rm a tira n je sadrži klasa Form atter iz p ak e ta ja-
va.util. Klasu Form atter m o žete sm a tra ti prev o d io cem koji zn ak o v n i n iz za fo rm a tira n je
i p o d a tk e p retv ara u željeni rezu ltat. Kada p rav ite o b jek at tip a Formatter, n jeg o v o m k o n -
s tru k to r u prosledite p o d a ta k gde da pošalje rezultat:
} /* Is p is :
Kornjača Tom je na (0,0)
Kornjača Teo je na (4,8)
Kornjača Tom je na (3,4)
Kornjača Teo je na (2,5)
Kornjača Tom je na (3,3)
Kornjača Teo je na (3,3)
* ///:-
Specifikatori formata
Za o dredivan je razm aka i p o rav n av anja p rilik o m u m e ta n ja p o d a ta k a p o tre b n i su slože-
niji specifikatori form ata . O vo je n jiho va o p šta sintaksa:
N ajm an ju veličinu polja zadajete p a ra m e tro m širin a. Form atter jem či da če p o lje biti
široko n ajm an je o đ ređ en bro j zn akova tak o što p o p o tre b i d o d a je razm ake. Podaci se
p o d ra zu m ev an o poravnavaju ud esn o, ali to m o žete redefin isati stav ljan jem - u odeljak
indikatora.
P aram e ta r preciznost je su p ro ta n širini, je r zadaje m a k sim u m . Za razliku o d širine koja
je p rim enljiv a na sve tipove konverzije p o d a ta k a i jed n a k o rad i za sve, preciznost im a razna
značenja za različite tipove. Za znako vn e nizove, sp ecifik ato r preciznost zadaje največi
broj znakova objekta tip a String koje treb a ispisati. Z a b rojeve s p o k re tn im zarezom , pre-
ciznost zađaje bro j d ecim aln ih m esta koje treb a ispisati (p o d ra z u m e v a se 6); ako decim ala
im a previše, Java će rezu ltat zao k ru žiti, a ako ih im a p rem alo , d o d ać e p rateće nule. Pošto
celi brojevi n em aju razlom ljen deo, preciznost se n a njih ne m ože p rim e n iti, i đob ićete
izuzetak ukoliko u p o tre b ite p recizn o st s k o n v erzijo m c elo b ro jn o g tipa.
U n a re d n o m p rim e ru , specifikatori fo rm a ta su u p o treb lje n i za ispisivanje ra č u n a za
kup ov inu :
//: znakovninizovi/Racun.java
import j a v a . u t i l .* ;
f.form at("%-15s %5s %10s\n", " A r t ik a l " , "K o l", "C en a");
f.form at("%-15s %5s %10s\n", " --- " , " — " ......... " ) ;
}
pu b lic void p r in t(S tr in g ime, in t k o l, double cena) {
f.form at("%-15.15s %5d % 10.2f\n", ime, k o l, c e n a );
ukupno += cena;
}
p u b lic void p r in tT o ta l() {
f.form at("%-15s %5s %10.2f\n", "P o re z ", ukupno*0.06);
f.fo r m a t(“ %-15s %5s %10s\n", " ---- " ) ;
f.form at("%-15s %5s % 10.2f\n", "Ukupno",
ukupno * 1.06);
}
p u b lic s t a t ic void m a in (S trin g [] args) {
Racun racun = new R acun();
r a c u n .p r in t T it le ( ) ;
racu n.p rint("D žeko v čarobni p a s u lj" , 4, 4 .2 5 );
ra c u n .p r in t("P rin c e z in g rašak", 3, 5 .1 );
ra c u n .p rin t("K a ša t r i medveda", 1, 14.29);
ra c u n .p r in tT o ta l( ) ;
}
} /* Is p is :
A rti kal Koi Cena
Ukupno 25.06
*///:-
Kao što vidite, F o rm a tte r je m o ć n a alatka za o d ređ iv an je razm aka i po rav n av an ja, a
im a p riličn o sažetu n otaciju. O vde su znakovni nizovi za fo rm a tira n je p ro sto k o p iran i da
bi se do bili o d g ovarajući razm aci.
V ežba 4: (3) P repravite p ro g ra m R a c u n .jav a tak o d a sve širin e o d re đ u je isti sk u p k o n -
sta n tn ih v red n o sti. Cilj je o m o g u ćiti laku p ro m e n u širin e tak o što će se izm eniti sam o je-
d an broj na je d n o m m estu.
Znakovi konverzije
d Celi broj (napisan decimalno)
c Unicode znak
b Logička vrednost (tipa Boolean)
40 4 Misliti na Javi
Znakovi konverzije
s Znakovni niz
f Broj s pokretnim zarezom (napisan decimalno)
e Broj s pokretnim zarezom (napisan u naučnoj notaciji)
X Celi broj (napisan heksadecimalno)
h K[juč za heširanje (napisan heksadecimalno)
% Literal %
p u b lic c la s s Konverzija {
p u b lic s t a t i c void m a in (S trin g [] args) {
Form atter f = new Fo rm atter(System .o u t);
in t v = 121;
S y s te m .o u t.p rin tln ("v = 121");
f.fo r m a t("d : %d\n", v ) ;
f.fo r m a t(" c : %c\n", v ) ;
f,fo r m a t("b : %b\n", v ) ;
f .form atC 's: %s\n", v ) ;
// f . f o r m a t ( " f : % f\ n ", v ) ;
// f.fo r m a t("e : %e\n", v ) ;
f.fo r m a t("x : %x\n", v ) ;
f.fo r m a t("h : %h\n", v ) ;
B ig ln te g e r w = new Biglnteger("50000000000000");
S y s te m .o u t.p rin tln (
"w = new B ig In te g e r( \ " 50000000000000\" ) " ) ;
f.fo r m a t("d : %d\n", w );
// f.fo r m a t(" c : %c\n", w );
f.fo r m a t("b : %b\n", w );
f.fo r m a t(“ s: %s\n", w );
// f .f o r m a t (" f : % f\n ", w );
Poglavlje 13: Znakovni nizovi 405
double x = 179.543;
S y s te m .o u t.p rin tln ("x = 179.543");
// f.fo r m a t("d : %d\n", x );
// f.fo rm a tC 'c : %c\n", x );
f.fo r m a t("b : %b\n", x );
f .fo r m a t (" s : % s\n", x ) ;
f . f o r m a t ( " f : % f\ n ", x ) ;
f.fo r m a t(" e : %e\n", x ) ;
// f.fo r m a t("x : %x\n", x );
f.fo r m a t(" h : %h\n“ , x );
d: 50000000000000
b: true
s: 50000000000000
x: 2d79883d2000
h: 8842ala7
x = 179.543
b: true
s: 179.543
f : 179.543000
e: 1.795430e+02
h: lef462c
y = nova Konverzija()
b: true
s: Konverzija@9cabl6
h: 9cabl6
z = false
b: false
s: false
h: 4d5
* ///:-
R edovi p retv o ren i u k o m e n ta re n isu valid n i za taj tip pro m en ljiv e; n jih o v im izvrša-
v an jem biste izazvali izuzetak.
O b ra tite p a ž n ju n a ko nverziju b, koja ra d i za sve g o rn je pro m en ljiv e. Iako je validna za
sve tipove a rg u m en a ta, ne p o n a ša se k ao što m o žd a pretp o stav ljate. Za p ro ste logičke
tipove (boolean) i o b jek at tip a Boolean, n jen rezu ltat je true ili false, u zavisnosti o d
v red n o sti. M eđ u tim , za sve d ru g ačije arg u m e n te , rezu ltat je uvek true ukoliko tip argu-
m e n ta nije null. Č ak i n u m eričk a v re d n o st nula, koja je u m n o g im jezicim a (i C -u ) sino-
n im za false, daje true, p a m o ra te b iti o p re zn i kada tu konverziju u p o treb ljav ate s
tip o v im a koji n isu logički.
Im a jo š tipo v a konverzija i d ru g ih o p cija sp ecifik ato ra fo rm a ta . To je o p isa n o u d o k u -
m en taciji razvojnog o k ru žen ja JDK za klasu Formatter.
V e ž b a 5 : (5) Za sve o sn o v n e tip o v e konverzija iz g o rn je tabele, nap išite najsloženiji m o -
gući izraz za fo rm atiran je, tj. u p o tre b ite sve m o g u će specifikatore fo rm a ta d o stu p n e za taj
tip konverzije.
String.format()
Java SE5 se ugledala i n a C -o v u fu n k ciju s p rin tf( ) k ojom se prave znakovni nizovi.
S tring.form at( ) je statičn a m eto d a koja p rim a iste arg u m e n te kao Form atterova m etoda
fo rm a t( ), ali vraća ob jek at tip a String. P o d esn a je kada m e to d u fo rm a t( ) treb a da zovete
sam o je d a n p u t:
// : znakovninizovi/IzuzetakBazePodataka.java
S trin g .fo rm at( ) sam o n a p ra v i o b jek at tip a Form atter i p ro sledi m u vaše arg u m en te,
ali k o risteći ovu m e to d u d o b ija te jasn iji i razu m ljiv iji k o d nego da sve to rad ite ru č n o .
p u b lic c la s s Hex {
p u b lic s t a t ic S trin g fo rm a t(b yte [] podaci) {
S t r in g B u i1der re z u lta t = new S t r in g B u ild e r ();
in t n = 0;
fo r (b y te b : podaci) {
i f ( n % 16 == 0)
re z u lta t.a p p en d (S trin g .fo rm at("% 0 5 X : " , n )) ;
r e z u lt a t . append(String.form at("%02X " , b ) );
n++;
i f ( n % 16 == 0) re z u lta t.a p p e n d ("\ n ");
}
re z u lta t.a p p e n d ("\ n ");
return r e z u lt a t .t o S t r in g O ;
}
p u b lic s t a t ic void mai n (S t r in g [] args) throws Exception {
i f ( a r g s . 1ength == 0)
// T e s tira n je isp isivan jem datoteke ove klase:
S y s te m .o u t.p rin tln (
fo r m a t(B in a r y F ile .r e a d ("H e x .c la s s " )));
el se
Sy stem .o u t.pri n t l n (
fo rm a t(B in a ryF ile .re a d (n e w F i le ( a r g s [ 0 ] ) ) ) ) ;
}
408 Misliti na Javi
} /* Is p is : (prim er)
00000: CA FE BA BE 00 00 00 31 00 52 0A 00 05 00 22 07
00010: 00 23 OA 00 02 00 22 08 00 24 07 00 25 OA 00 26
00020: 00 27 OA 00 28 00 29 OA 00 02 00 2A 08 00 2B OA
00030: 00 2C 00 2D 08 00 2E OA 00 02 00 2F 09 00 30 00
00040: 31 08 00 32 OA 00 33 00 34 OA 00 15 00 35 OA 00
00050: 36 00 37 07 00 38 OA 00 12 00 39 OA 00 33 00 3A
* ///:-
Regulami izrazi
Regularni izrazi su već d u g o sastavni deo sta n d a rd n ih U nixovih alatki sed i awk, i jezika Py-
th o n i Perl (neki tvrde da su o n i glavni razlog što je Perl tako uspešan). U Javi su alatke za
o b ra d u znakovnih nizova ranije bile delegirane u klase String, StringBuffer i StringToke-
nizer. U p o ređ e n ju s reg u larn im izrazim a, te alatke su im ale relativno sk ro m n e m ogućnosti.
R egularni izrazi su m o ć n e i fleksibilne alatke za o b ra d u teksta. O n e o m o g u ćav a ju p ro -
gram sk o zadavanje složenih u zo rak a teksta koji m o g u biti p ro n a đ e n i u u la z n o m znakov-
n o m n izu. K ada te uzorke p ro n a đ e te , m o žete da reagujete kako god hoćete. Iako je
sin taksa re g u larn ih izraza isp rv a teška, n jih o v sažet d in a m ič k i jezik m o žete u p o tre b iti za
rešavanje svih vrsta zadataka u o b ra d i, p ro n a laž en ju , izb o ru , u re đ iv a n ju i p ro v eri znakov-
n ih nizova, n a p o tp u n o u o p šte n način.
Osnove
R egularan izraz je način o p isivanja zn ak o v n ih nizova na u o p šte n n ačin , ta k o d a m ožete
reći: ,A ko znakovni niz im a ovo u sebi, o n d a od govara o n o m e što tra ž im “. Na p rim er,
kako b iste saopštili da isp red b ro ja m o že, ali n e m o ra biti znak m in u s, pišete m in u s i iza
njega zn ak pitanja:
_?
D a b iste opisali celi broj, kažete d a je to je d n a ili više cifara. U re g u la rn im izrazim a, ci-
fra (engl. digit) opisuje se sa \d. U k oliko ste im ali posla s re g u larn im izrazim a u d ru g im
jezicim a, o d m a h ćete u o čiti razlik u u o b ra d i o b rn u tih kosih crta. U d ru g im jezicim a \\
znači: „ H o ću d a u m e tn e m o b ič n u (d o slo v n u , literaln u ) kosu c rtu u re g u laran izraz. Ne
tre b a jo j p rid a v ati specijalno značenje.“ U Javi, \\ znači: „U m ećem k osu c rtu u regularan
izraz, p a sledeći znak im a sp ecijaln o zn ačen je“. N a prim er, re g u laran izraz za cifru je \\d.
A ko ho ćete da u m e tn e te stv arn u k o su c rtu , pišete \\\\. M eđ u tim , za prelazak u novi red i
ta b u la to r piše se sam o jedna kosa crta: \n \t.
Poglavjje 13: Znakovni nizovi 409
-?\\d+
p u b lic c la s s PodudaranjeCelihBrojeva {
p ub lic s t a t ic void m a in (S trin g [] args) {
Sy ste m .o u t.p rin tln ("- 1 2 3 4 ".m a tc h e s("- ?\\ d + "));
S y ste m .o u t.p rin tln ("5 6 7 8 ".m a tc h e s("- ?\\ d + "));
System .o u t.p rin tln ("+ 9 1 1 ".m atch es(“ - ?\ \ d + "));
Sy ste m .o u t.p rin tl n("+ 9 11 ".m atches("( - 1\\+)?\\d+")) ;
}
} /* Is p is :
true
tru e
fa l se
true
* ///:-
Prva dva izraza o d go v araju , ali treći p o čin je zn ak o m +, k jji e sam p o sebi leg itim an ,
ali ne o d g ovara našem re g u la rn o m izrazu. Z ato n a m treb a n a a ii d a k a že m o :„ m o že početi
z n ak o m + ili U reg u larn im izrazim a, zag rad e im a ju efekat g a ip is a n ja izraza, a vertik al-
na crta I znači logičko ili (O R ). D akle izraz
(-|W + )?
znači da ovaj d eo znak o v n o g niza m ože b iti - ili + ili (zbog zn ak a ?) n i je d n o ni dru go .
Pošto u reg u larn im izrazim a zn ak + im a sp ecijaln o značenje, rn o ra se p o m o ć u \\ p retv o -
riti u iziaznu sekvencu, da bi se u izrazu pojavio k ao ob ičan znak.
U klasu S trin g ugraden a je m e to d a s p l i t ( ), ko risn a alatka za reg ularn e izraze koja znači:
„Iscepaj ovaj znakovni niz n a delove koji se p o d u d a ra ju s datirn reg u larn im izrazom .“
p ub lic c la s s Cepanje {
p ub lic s t a t ic Strin g vite z o v i =
"Then, when you have found the shrubbery, you must " +
"cu t down the m ightiest tre e in the f o r e s t . . . " +
" w i t h . . . a h e rrin g !";
pu b lic s t a t ic void s p li t ( S t r in g re g iz ) {
System .out. pri ntl n(
A rra y s .to S tr i ng(vi te z o v i. s p li t ( r e g i z ) ) ) ;
410 Misliti na Javi
Prvo, im ajte u v id u d a o b ičn e znakove m o žete u p o tre b lja v a ti k ao re g u larn e izraze - re-
g u lara n izraz n e m o ra sad ržati specijalne znakove, k ao što v idite u p rv o m p o ziv u m e to d e
sp lit( ), koji tek st cepa n a svakom razm ak u .
D ru g i i tre ć i p oziv m e to d e s p lit( ) sadrže \W , izraz za zn a k koji n e m o že biti d eo reči
(verzija n a p isa n a m alim slovom , \w , obeležava z n a k koji m ože b iti d eo reči). U o čite d a su
u d ru g o m slučaju u k lo n jen i znakovi in terp u n k cije. Treći p o ziv m e to d e sp lit( ) kazuje:
„slovo n i je d a n ili više zn akova koji ne m o g u b iti d e o reči“. Iz rezu lta ta v id ite da se u
n je m u n e p o jav lju ju uzorci d efin isan i u re g u la rn o m izrazu.
Preklo pljena verzija m e to d e String.split( ) o m o g u ćav a zadavanje najvećeg đozvolje-
n o g b ro ja cepanja.
Poslednja alatka re g u la rn ih izraza u g rađ en a u klasu String jeste zam en a. M ožete za-
m e n iti p rv i p o d u d a rn i d eo ili sve njih:
//: znakovninizovi/Zamena.java
import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ;
p ub lic c la s s Zamena {
s t a t ic S trin g s = S p li t t in g . v i t e z o v i;
p u b lic s t a t i c void m a in (S trin g [] args) {
p r in t(s .r e p la c e F ir s t (" f\ \ w + ", " l o c a t e d " ) ) ;
p ri n t ( s . re p la c e A l1("s h ru b b e ry |tre e |h e rri n g ", "banana")) ;
}
} /* Is p is :
Then, when you have located the shrubbery, you must cut down the
m ightiest tre e in the f o r e s t . . . w it h ... a herrin g!
Then, when you have found the banana, you must cut down the m ig h tiest banana
in the f o r e s t . . . w it h . . . a banana!
* ///:-
P rv o m izrazu o d g o v ara slovo f i je d a n ili više zn akova koji m o g u b iti d eo reči (povedite
ra č u n a o to m e d a je ovoga p u ta w m alo slovo). Z am en ju je se sam o p rv i p ro n a đ e n i p o d u -
d a rn i deo, pa reč ,,fo u n d “ biva zam e n jen a rečju ,,located.“
D ru g o m izrazu od g o v ara bilo koja o d tri reči koje su razdvojene v ertik aln im c rta m a
(logičkim O R ), i zam e n ju ju se svi p ro n a đ e n i p o d u d a rn i delovi.
Poglavlje 13: Znakovni nizovi 411
V idećete d a još m o ćn ije alatke za z a m e n u im aju re g u larn i izrazi koji se ne u p o treb lja-
vaju za znakov n e nizove - n a p rim er, m ožete p o zivati m e to d e d a o n e obavljaju zam ene.
U slučajevim a kada se reg u larn i izraz u p o treb ljav a više p u ta , z n a tn o su efikasniji reg ular-
n i izrazi koji se n e u p otreb ljav aju za znakovne nizove.
Vežba 7: (5) K oristeći d o k u m e n tac iju klase java.util.regex.Pattern kao resu rs, n ap išite i
testirajte reg u la ra n izraz koji proverava d a li rečenica p o čin je velikim slovom i završava se
tačkom .
Vežba 8: (2) Pocepajte znakovni niz Cepanje.vitezovi ta m o gde se n a đ u reči ,,the“ ili ,,you.“
Vežba 9: (4) K oristeći d o k u m e n taciju klase java.util.regex.Pattern kao resu rs, sve sa-
m oglasnike u Cepanje.vitezovi zam en ite p o d crta m a .
Znakovi
B Znak B
\xhh Znak čiji je heksadecimalni kod oxhh
Nuhhhh Unicode znak čiji je heksadecimalni kod oxhhhh
\t Tabulator
\n Prelazak u novi red
V Vraćanje na početak tekućeg reda
\f Prelazak na novi list
\e Znak Esc (izlaz)
Snaga reg u la rn ih izraza najbolje se vidi u d efin isan ju klasa znakova. Evo n ek ih tip ič n ih
način a p ravljenja klasa znakova i n ekih u n a p red d efin isan ih klasa:
Znakovi
Bilo koji znak
fabc] Bilo koji od znakova a. b ili c (isto što i alblc)
["abc] Bilo koji znak osim a, b ili c (negacija)
[a-zA-Z] Bilo koji znak od a do z ili od A do Z (opseg)
[abcfhijj] Bilo koji od znakova a,b,c, h,i.j (isto što i alblclhlilj) (unija)
[a-z&&[hij]J Bilo koji od znakova h, i ili j (presek)
\s Bilo koji od znakova za belinu (razmak, tabulator, prelazak u novi red, prelazak
na novi list, vraćanje na početak tekućeg reda)
\S Bilo koji znak sem znakova za belinu (fA\s])
412 Misliti na Javi
Znakovi
\d Numerička cifra [0-9J
\D Bilo koji znak sem znakova za cifre [A0-9]
\w Bilo koji znak koji može biti deo reči [a-zA-Z_0-9]
\W Bilo koji znak sem znakova koji mogu biti deo reči [A\w]
O vde sm o pok azali sam o m ali d eo m o g u ćih re g u la rn ih izraza; da b iste lako p ristu p ali
svim m o g u ćim u zo rc im a re g u la rn ih izraza, stra n ic u JD K d o k u m e n ta c ije za ja -
v a .u til.re g e x .P a tte rn p re tv o rite u obeleživač.
Logički operatori
XY X, a potom Y
XIY X ili Y
(X) Grupa koju treba pronači. U nastavku izraza, /tu grupu za hvatanje označavate sa \i.
Izrazi za granice
A
Početak reda
$ Kraj reda
\b Granica reči
\B Granica koja ne može biti deo reči
\G Kraj prethodno pronadene podudarnosti
//: znakovninizovi/Rudolph.java
p ublic c la s s Rudolph {
pu b lic s t a t i c void m a in (S trin g [] args) {
fo r (S tr in g uzorak : new S t r i n g [ ] { "Rudolph",
" [rR ]u d o lp h ", " [ r R ] [ a e i o u ] [ a - z ] o l , "R .* " ))
Syste m .o u t.p rin tln ("R u d o lp h ".m a tc h e s(u z o rak )) ;
}
} /* Is p is :
true
true
true
true
* ///:-
N aravno, n e bi tre b a lo pisati najteže shvatljiv reg u laran izraz, nego najjednostavniji
koji „završava p o sao “. K ada p o č n e te d a pišete nove reg u larn e izraze, vero v atn o ćete često
u p o treb ljav ati svoj k o d kao referen cu .
Poglavlje 13: Znakovni nizovi 413
Kvantifikatori
K vantifikator o p isu je n a č in n a koji u z o ra k a p so rb u je u lazn i tekst:
• Pohlepno: „ukoliko se d ru g a čije ne n a re d i“, k v an tifik ato ri su p o h le p n i (engl. greedy).
P o h le p an izraz p ro n alaz i sve m o g u če p o d u d a rn o s ti sa u zo rk o m . T ip ičan u z ro k p ro -
b lem a jeste p re tp o sta v k a d a će se izraz p o d u d a riti sam o s p rv o m p o d u d a rn o m g ru -
p o m znakova, a o n je zap rav o p o h le p a n i nastaviće d a tra ž i sve d o k n e p ro n a đ e
najveći m o g u ći p o d u d a rn i zn ak o v n i niz.
• Rezervisano: O vaj k v a n tifik a to r se obeležava z n ak o m p ita n ja, a p ro n a laz i m in im a -
lan b ro j znakova p o tre b a n za p o d u d a ra n je sa u zo rk o m . N aziva se i lenj, tn inim alno
podudarajući, nepohlepan.
• Posesivno: tr e n u tn o d o stu p a n sam o u Javi (u d ru g im jezicim a ne) i n a p re d n iji, p a ga
vero v atn o n ećete o d m a h u p o treb ljav ati. Kada se re g u laran izraz p rim e n i n a zna-
kovni niz, o n g en eriše m n o g o stan ja d a bi m o g ao d a se v ra ti ako n e u sp e d a nađe
p o d u d a rn o s t. P osesivni k v an tifik ato ri n e čuv aju ta m e đ u sta n ja i tim e sprečavaju
vraćanje. M ožete ih u p o tre b iti k ako b iste sprečili reg u laran izraz da p odivlja, a i d a
bi rad io efikasnije.
Im ajte u v id u d a izraz X često treb a da zatv o rite u zag rad e d a bi rad io o n a k o kako
hoćete. N a p rim e r:
abc+
(abc)+
R eg u larn i izrazi će vas Iako p rev ariti; u p o ređ en ju s Javom , to je sasvim d ru g ačiji jezik.
414 Misliti na Javi
CharSequence
Interfejs CharSequence u tv rđ u je o p štu defin iciju niza zn ak o v a a p stra h o v a n u iz klasa
CharBufFer, String, StringBuffer ili StringBuilder:
in te rfa c e CharSequence {
c h a rA t(in t i ) ;
le n g th ();
subSequence(int pocetak, in t k r a j ) ;
t o S t r in g O ;
}
O b je k at tip a P attern pred stav lja p re v e d e n u v erziju reg u larn o g izraza. Kao što v id ite u
p re th o d n o m p rim e ru , za pravljenje o b jek ta tip a M atcher o d p rev ed en o g o b jek ta tip a
Pattern m o žete u p o tre b iti m e to d u m a tc h e r( ) i ulazni znak o v n i niz. I klasa Pattern im a
sta tič n u m eto d u :
koja isp itu je da li regiz o d g o v ara celom u la z n o m nizu znakova (o b jek tu tip a CharSe-
quence), i m e to d u s p lit( ) koja p ro izv o d i niz o b jek ata tip a String koji se p o d u d a ra ju s re-
g u la rn im izrazom regiz.
O b je k a t tipa Matcher g en erišete p o ziv o m m eto d e Pattern.m atcher( ) uz ulazni zna-
kovni niz kao arg u m e n t. Z a tim se o b je k a t tip a Matcher u p o treb ljav a za p ristu p a n je re-
z u ltatim a, je r se za u tv rđ iv an je u sp ešn o sti ili n eu p e šn o sti različitih vrsta p o d u d a ra n ja
u p o treb ljav aju njegove m eto d e:
boolean matches()
boolean loo k in g A t()
boolean fin d ()
boolean f in d ( in t s t a r t )
Vežba 10: (2) Za rečenicu „Java now has regular expressions“ odredite da li će sledeći iz-
razi pronaći neko podudaranje:
''Ja v a
\B reg .*
n.w \s+ h (a|i)s
s?
s*
s+
S {4}
S {1 ).
s{0 ,3 )
na
"A r lin e ate eig ht apples and one orange w hile A nita hadn't any"
Metoda find()
M etoda M atcher.find( ) može se upotrebiti za pronalaženje p o dudaranja više uzoraka
u znakovnom nizu na koji je prim enjena. Na prim er:
p u b lic c la s s Pronalazenje {
p u b lic s t a t ic void m a in (S trin g [] args) {
Matcher m = Pattern .com pile("\\w + ")
.m atcher("Evening is f u l l of the lin n e t 's wings" ) ;
whi 1e(m .fi nd ( ) )
printnb(m .group() + 11 " ) ;
p r in t();
in t i = 0;
whi 1e (m .f in d (i) ) {
printnb(m .group() + 11 " ) ;
i++;
}
}
} /* Is p is :
Evening is f u l l of the lin n e t s wings
Evening vening ening ning ing ng g is is s f u l l f u ll u ll 11 1 of o f f the the
he e lin n e t lin n e t innet nnet net et t s s wings wings ings ngs gs s
* ///.-
Poglavlje 13: Znakovni nizovi 417
Uzorak \\w+ cepa ulaz na reči. M etoda fin d () radi kao iterator, pošto se kroz ulazni
znakovni niz kreče unapred. M eđutim , drugoj verziji m etode fin d ( ) možete dati celobroj-
ni argum ent koji joj kazuje redni broj znaka za početak pretraživanja - ta verzija zadaje
vrednost argum enta kao mesto pretraživanja, kao što se vidi iz rezultata.
Grupe
G rupe su regularni izrazi navedeni u zagradam a koji se naknadno m ogu pozivati preko
broja grupe. G rupa 0 je ceo izraz, grupa 1 je prva grupa u zagradi itd. Stoga u
A (B (C ))D
//: znakovninizovi/Grupe.java
import j a v a . u t i l .re g e x .*;
import s t a t ic n et.m in d vie w .u ti1. P r in t .* ;
p u b lic c la s s Grupe (
s t a t i c p ub lic fin a l S trin g PESMA =
"Twas b r i l l i g , and the s lit h y toves\n" +
"Did gyre and gimble in the wabe.\n" +
"A ll mimsy were the borogoves,\n" +
"And the mome raths outg rabe.\n\n" +
"Beware the Jabberwock, my son,\n" +
"The jaws th at b it e , the claws th at catch .\ n " +
"Beware the Jubjub b ird , and shun\n" +
"The frumious Bandersnatch.";
p u b lic s t a t ic void m a in (S trin g [] args) {
Matcher m =
Pattern.com pi1e("(?m )(\\S+ )\\s+ ((\\S+ )\\s+ (\\S+ ))$ ")
.matcher(PESMA);
418 Misliti na Javi
Ovo je početak pesme ,,Jabberwocky“ Luisa Kerola, iz knjige Alisa iza ogledala. Uzorak u
regularnom izrazu im a više grupa u zagradam a koje se sastoje od proizvoljnog broja znako-
va koji nisu beline (\S+) praćenog proizvoljnim brojem znakova koji jesu beline (\s+). Cilj
je uhvatiti tri poslednje reči u svakom redu; kraj reda označava $. M eđutim, uobičajeno po-
našanje je da se $ poredi s krajem celog ulaznog niza, pa regularnom izrazu m orate ekspli-
citno reći da obrati pažnju na znakove za prelazak u novi red u ulaznom nizu. To se postiže
indikatorom uzorka (?m) na početku niza (ubrzo ćemo razm otriti indikatore uzoraka).
Vežba 12: (5) Prepravite program Grupe.java tako da broji sve različite reči koje ne
počinju velikim slovom.
/ / : zn ak o vn in iz o vi/StartE n d .java
import j a v a . u t i 1. regex.*;
import s t a t ic n e t.m in d view .u ti1 .P r in t . *;
p ub lic c la s s StartEnd {
p u b lic s t a t i c S trin g ulaz =
"As long as there is in ju s t ic e , whenever a\n" +
"Targathian baby c rie s out, wherever a d istre ss\ n " +
"sig n al sounds among the s ta rs . . . W e 'll be th e re.\n " +
"This fin e ship, and th is fin e crew . . . \ n " +
"Never g ive up! Never su rre n d e r!";
p riv a te s t a t i c c la s s Is p is i {
p riv a te boolean regizOdstampan = f a ls e ;
p riv a te S trin g re g iz ;
Is p is i( S t r in g reg iz ) { t h is .r e g iz = re g iz ; }
void Is p is i( S t r in g poruka) {
if(!regizO dstam pan) {
p r in t(r e g iz );
regizOdstampan = tru e ;
}
p rin t(p o ru k a );
}
}
s t a t ic void is p it a j ( S t r i n g s, S trin g re g iz ) {
Is p is i d = new I s p i s i ( r e g i z ) ;
Pa ttern p = P a tte rn .c o m p ile (re g iz );
Matcher m = p .m a tc h e r(s );
whi 1e (m .fi n d ())
d .is p is i ("fin d () + m.groupO +
početak = "+ m .s ta rt() + " k raj = " + m .e n d ());
if(m .lo o k in g A t()) // r e s e t() n ije potrebna
d .is p is i(" lo o k in g A t ( ) početak = "
+ m .startO + " kraj = " + m .end());
if(m .m atches()) // re se t() n ije potrebna
d .is p is i ("matchesO početak = 11
+ m .sta rt() + " kraj = " + m .end());
}
p u b lic s t a t ic void m a in (S trin g [] args) {
f o r (S t r in g in : u l a z . s p l i t ( " \ n " ) ) {
p r in t (" u la z : " + in ) ;
f o r (S t r in g regiz : new S t r i n g [ ] { "\\w *ere\\w*",
"\\w *e v e r", "T\\w+", " N e v e r .* ? !" } )
is p it a j ( in , re g iz );
}
}
} /* Is p is :
ulaz : As long as th ere is in ju s t ic e , whenever a
\w*ere\w*
f in d () 'th e r e ' početak = 11 kraj = 16
\w*ever
fin d () 'whenever' početak = 31 k raj = 39
ulaz : Targathian baby c rie s out, wherever a d is tre s s
\w*ere\w*
f in d () ’wherever' početak = 27 kraj = 35
\w*ever
f in d () ’w herever‘ početak = 27 k raj = 35
T\w+
f in d () 'T a rg a th ia n ' početak = 0 kraj = 10
lo o k in g A t() početak = 0 kraj = 10
ulaz : signal sounds among the s ta rs . . . W e 'll be th ere.
\w*ere\w*
420 Misliti na Javi
Vodite računa o tom e da će m etoda fin d ( ) pronaći regularan izraz bilo gđe u ulazu, ali
lookingA t( ) i m atch es( ) uspevaju sam o ako se regularan izraz p o d udara od samog
početka ulaza. D ok m etoda m atch es( ) uspeva sam o ukoliko se ceo ulaz p o d u d ara s regu-
larnim izrazom , lookingAt( )4 uspeva ako se pod u dara sam o prvi deo ulaza.
Vežba 13: (2) Prepravite program StartEnd.java tako da upotrebljava Grupe.PESMA kao
ulaz, ali da ipak proizvodi pozitivne rezultate za m etode fin d ( ), lookingA t( ) i m atches( ).
p u b lic c la s s O ln d ikatorima {
p u b lic s t a t i c void m a in (S trin g [] args) {
Pa tte rn p = P a tte rn . compi 1e ( " /\ ja v a ",
P a tte r n . CASE _ INSENSIT IVE | Pa tte rn .M U L T IL IN E ).
Matcher m = p.m atcher(
"ja v a ima re g iz\n ja va ima reg iz\n" +
"JAVA ima p r ilič n o dobre regularne izraze\n"
"U je z ik u Ja va postoje reg u larn i i z r a z i " ) ;
whi1e (m .fi n d ( ) )
System .out. p ri n t l n(m.group( ) ) ;
}
) /* Is p is :
ja va
Java
JAVA
* ///:-
422 Misliti na Javi
Napravljen je uzorak koji se podudara s redovim a koji počinju rečima java, Java, JAVA
itd. Podudaranje se ispituje za svaki red višerednog skupa (podudaranje delova koji
počinju na početku niza znakova i slede iza graničnika svakog reda u nizu znakova).
O bratite pažnju na to da m etoda g ro u p ( ) daje sam o deo koji se podudario.
Metoda splitf)
M etoda s p lit( ) cepa ulazni znakovni niz na niz String objekata, razgraničenih datim re-
gularnim izrazom.
S t r in g [] split(CharSequence ulaz)
S t r in g [] split(CharSequence u la z, in t n ajvišePod nizova)
Ovo je podesno za cepanje ulaznog teksta n a delove koji im aju zajednički graničnik:
p ub lic c la s s PrimerZaSpl i t {
p u b lic s t a t ic void m a in (S trin g [] args) {
S trin g ulaz =
"T h is! lunusual use! !o f exclam ation! Ip o in ts '1;
pri n t(A rra y s . to S tr i ng(
P a tte rn .c o m p ile ("! ! " ) . s p l i t ( u l a z ) ) ) ;
// Uradi samo prva t r i :
p rin t(A r ra y s .to S trin g (
Pattern.com pi1e ( " ! ! " ) . s p l i t ( u l a z , 3 ) ) ) ;
}
} /* Is p is :
[T h is, unusual use, of exclam ation, points]
[T h is, unusual use, of e x clam atio n !Ip o in ts]
* ///:-
Operacije zamene
Regularni izrazi su posebno podesni za zam enu teksta. Na raspolaganju su ove metode:
replaceFirst(String zamena) zam enjuje prvi p odudarni deo ulaznog znakovnog niza
zamenom.
replaceAll(String zamena) zam enjuje svaki p odudarni deo ulaznog znakovnog niza
zamenom.
Poglavlje 13: Znakovni nizovi 423
p u b lic c la s s Zamene {
p ublic s t a t ic void m a in (S trin g [] args) throws Exception {
S trin g s = T e x tF ile .re a d (''Z a m e n e .ja v a ");
// Pronađi g o rn ji blok te k sta n aro čito pretvoren u komentar:
Matcher ulaztlM =
Pattern.com pi1e ( " / \ \ * ! ( . * ) !\ \ * / ", Pattern.DOTALL)
.m a tc h e r(s );
if(u la z U M .fin d ())
s = u lazU M .g ro u p (l); // Ono što hvataju zagrade
// Dva i v iš e razmaka zameni jednim:
s = s .re p la c e A l1 (" { 2 , } " , " " ) ;
// Jedan i l i v iš e razmaka na početku svakog reda
// zameni s nula razmaka. Mora se u k lj u č i t i režim MULTILINE:
s = s .re p la c e A ll ("(? m )/' +", " " ) ;
p r in t(s );
s = s .r e p la c e F ir s t ( " [a e io u ] " , " ( V0WEL1)" ) ;
S trin g B u ffe r sbaf = new S t r in g B u f f e r ( ) ;
Pattern p = P a tte r n .c o m p ile ("[a e io u ]" ) ;
Matcher m = p .m a tc h e r(s );
// Obradi podatke metode f in d () dok
// o b avljaš zamene:
w h ile (m .fin d ())
m.appendReplacement (s b a f, m.groupO . toUpperC ase()) ;
// Umetni ostatak te k s ta :
m.appendTai1( s b a f ) ;
p r in t ( s b a f ) ;
}
424 Misliti na Javi
} /* Is p is :
H ere's a block of te x t to use as input to the reg u lar
expression matcher. Note th a t w e 'll f i r s t ex tract
the block of te x t by looking fo r the sp ecial d e lim ite rs ,
then process the ex tracted block.
H (VO W ELl)rE's A blOck Of tE x t tO UsE As InpUt tO
thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt w E 'll
f l r s t ExtrA ct thE blOck Of tE x t by lOOklng fOr
thE spEcIAl d E lIm ltE rs , thEn prOcEss thE
ExtrActEd blOck.
* ///:-
Metoda reset()
Postojeći objekat tipa Matcher m etodam a re se t( ) možete prim eniti na nov niz znakova:
p u b lic c la s s Resetovanje {
p u b lic s t a t ic void m a in (S trin g [] args) throws Exception {
Matcher m = Pattern .com pi1e ( " [fr b ] [a iu ] [g x ]“ )
,m a tc h e r("fix the rug w ith b ag s");
Poglav[je 13: Znakovni nizovi 425
re se t( ) bez argum enata prim enjuje objekat tipa M atcher na početak tekućeg niza.
p u b lic c la s s JGrep {
p u b lic s t a t ic void m a in (S trin g [] args) throws Exception {
i f (a rg s . 1ength < 2) {
S y ste m .o u t.p rin tln ("U p o treb a : ja va JGrep regiz d a to te k e ");
S y s te m .e x it(0 );
}
Pa ttern p = P a tte r n .c o m p ile (a r g s [l]);
// It e r a c ij a kroz redove ulazne datoteke:
in t indeks = 0;
Matcher m = p .m a tc h e r ("");
fo r (S t r in g red : new T e x tF ile ( a r g s [ 0 ] ) ) {
m .re s e t(re d );
whi1e (m .fi nd( ) )
System .out.println(indeks+ + + " : " +
m.groupO + " : " + m .s t a r t ( ) ) ;
}
}
} /* Is p is : (prim er)
0: s trin g s : 4
1: sim ple: 10
2: the: 28
426 Misliti na Javi
3: S s ct: 26
4: c la s s : 7
5: s ta tic : 9
6: S trin g : 26
7: throws: 41
8: System: 6
9: System: 6
10: compile: 24
jll: through: 15
12: the: 23
13: the: 36
14: S trin g : 8
15: System: 8
16: s t a r t : 31
* ///:-
Vežba 17: (8) Napišite program koji učitava datoteku Javinog izvornog koda (čije ime
zadajete na kom andnoj liniji) i prikazuje sve njene kom entare.
Vežba 18: (8) Napišite program koji učitava datoteku Javinog izvornog koda (čije ime
zadajete na kom andnoj liniji) i prikazuje sve doslovno (Iiteralno) navedene znakovne ni-
zove u kodu.
Vežba 19: (8) Na osnovu preth od ne dve vežbe, napišite program koji pretražuje Javin iz-
vorni kod i ispisuje im ena svih klasa koje se spom inju u tom program u.
Poglavlje 13: Znakovni nizovi 427
//: znakovninizovi/ProstoCitanje.java
import ja v a .io .* ;
} /* Is p is :
Kako se zoveš?
Hajduk Veljko
Koliko godina imaš? Koji je tvoj omiljeni broj tipa double?
(ulaz: <starost> <double>)
22 1.61803
Zdravo Hajduk Veljko.
Za 5 godina imaćeš 27.
Moj omiljeni broj tipa double je 0.809015.
* ///.-
U polju ulaz koriste se klase iz paketa java.io koji neće biti zvanično predstavljen do
poglavlja Javin ulazno-izlazni sistem. Objekat tipa StringR eader pretvara znakovni niz u
tok podataka (engl. stream) koji se može čitati, i taj objekat se upotrebljava za pravljenje
428 Misliti na Javi
objekta tipa BufferedReader, pošto BufferedReader im a m etodu readL ine( ). Zato se ob-
jekat ulaz može čitati red po red, kao da je standardni ulaz s konzole.
readL ine( ) pribavlja String za svaki red ulaza. Jednostavno je ako svaki red podataka
hoćete da pretvorite u jedan ulaz, ali ukoliko su dve ulazne vrednosti u istom redu, stvari
se kom plikuju —da bism o svaki ulaz mogli da analiziram o zasebno, red se m o ra podeliti
na dva dela. Ovde se podela obavlja kada se pravi num N iz, ali pošto je m etoda sp lit( )
uvedena tek u J2SE1.4, pre se to m oralo raditi drugačije.
Klasa Scanner se koristi od Jave SE5. O na obavlja glavni deo posla oko leksičkog ana-
liziranja ulaza:
//: znakovninizovi/BoljeCitanje.java
import java.util
K onstruktor klase Scanner prim a gotovo sve vrste ulaznih objekata - m edu njim a su
objekat tipa File (koji će takođe biti objašnjen u poglavlju Javin ulazno-izlazni sistem),
objekat tipa InputStream , objekat tipa String ili u ovom slučaju objekat tipa Readable,
Poglavlje 13: Znakovni nizovi 429
što je interfejs koji je Java SE5 uvela za opisivanje „nečega što im a m etodu re a d ( )“. U tu
kategoriju spada BufferedReader iz preth od no g prim era.
U klasi Scanner se m etode za učitavanje ulaza, podelu na lekseme i jezičku analizu
kriju u raznim vrstam a m etoda ,,next“. O bična m etoda n e x t( ) vrača sledeću leksem u zna-
kovnog niza, a postoje i ,,next“ m etode za sve proste tipove (sem char), kao i za BigDeci-
m al i Biglnteger. Sve ,,next“ m etode blokiraju, što znači da vraćaju rezultat tek kada
celokupna leksema postane dostupna na ulazu. Postoje i odgovarajuće m etode ,,hasNext“,
koje vraćaju true ukoliko je sledeća ulazna leksema ispravnog tipa.
Zanimljiva razlika izm edu dva preth od na prim era: u p rogram u BoljeCitanje.java ne
postoji blok try za izuzetke tipa IOException. Jedna od pretpostavki klase Scanner jeste
da IOException signalizira kraj ulaza, pa te izuzetke Scanner guta. M edutim , najnoviji
izuzetak je dostupan preko m etode ioE xception( ), pa m ožete da ga ispitate ako treba.
Vežba 18: (2) N apravite klasu koja sadrži polja tipova int, long, float, double i String.
N apravite konstruktor za tu klasu; on treba da p rim a jedan argum ent tipa String, da ana-
lizira taj znakovni niz i da ga raščlani na spom enuta polja. D odajte m etodu to S trin g ( ) i
pokažite da klasa ispravno radi.
p ub lic c la s s GranicnikKlaseScanner {
p u b lic s t a t ic void m a in (S trin g [] args) {
Scanner skener = new Scanner("12, 42, 78, 99, 4 2 ");
s k e n e r.u s e D e lim ite r ("\ \ s * ,\ \ s * ");
w h ile (s k e n e r.h a s N e x tIn t())
S y s te m .o u t .p r in tln (s k e n e r .n e x t In t());
}
} /* Is p is :
12
42
78
99
42
* ///:-
U ovom prim eru, kao graničnici prilikom čitanja datog znakovnog niza upotrebljeni
sli zarezi (oko kojih može biti proizvoljna količina belina). Ista tehnika se m ože upotrebiti
za učitavanje datoteka u kojim a su podaci razgraničeni zarezima. Pored m etode
u se D e lim ite r() za zadavanje uzorka graničnika, postoji i d e lim ite r() - on vraća tekući
objekat tipa P attern koji se upotrebljava kao graničnik.
430 Misliti na Javi
/ / : z n a k o v n in iz o v i/ A n a liz a to rP re tn ji.ja v a
import ja v a .u t il.r e g e x .* ;
import j a v a . u t i l .* ;
p ub lic c la s s A n a liz a to rP r e tn ji {
s t a t ic S trin g p reteciPodaci =
"58.27.82.161@02/10/2005\n" +
"204.45.234.40@02/ll/2005\n" +
"58.27.82.161@02/ll/2005\n" +
"58.27.82.161@02/12/2005\n" +
"58.27.82.161@02/12/2005\n" +
"[S le d e ć i od eljak zapisnika s drugačijim formatom p o dataka]";
public s t a t ic void m a in (S trin g [] args) {
Scanner skener = new S c a n n e r(p re te ciP o d a ci);
S trin g uzorak = "(\\d + [.]\\d + [.]\\d + [.]\\d + )@ " +
" (\\d{2 }/\\d{2 }/\\d{4 })";
w hile(skener.hasN ex t(u zorak)) {
sk e n e r.n ex t(u zo rak );
MatchResult pronasao = sk en er.m a tch ();
S trin g ip = p ro n a sao .g ro u p (l);
S trin g datum = pronasao.group(2);
Sy stem .o u t.fo rm a t("Pretn ja dana %s od %s\n", datum .ip);
1
)
} /* Is p is :
Pre tn ja dana 02/10/2005 od 58.27.82.161
Pre tn ja dana 02/11/2005 od 204.45.234.40
Pre tn ja dana 02/11/2005 od 58.27.82.161
Pre tn ja dana 02/12/2005 od 58.27.82.161
Pre tn ja dana 02/12/2005 od 58.27.82.161
* ///:-
Klasa StringTokenizer
Pre regularnih izraza (koji su uvedeni u J2SE1.4) ili klase Scanner (koja se koristi od Jave
SE5), znakovni niz se cepao na lekseme p o m o ću klase StringTokenizer. Ali sada se to
m nogo lakše i sažetije obavlja pom oću regularnog izraza ili klase Scanner. Pogledajte jed-
nostavno poređenje ldase StringTokenizer i druge dve tehnike:
p u b lic c la s s UmestoStringTokenizera {
p ub lic s t a t ic void m a in (S trin g [] args) {
S trin g input = "But I'm not dead y e t ! I fe e l happy!";
String Tokenizer stoke = new S trin g T o k e n iz e r(u la z );
w hile(stoke.hasM oreElem ents())
System .o u t.p rin t(sto k e.n ex tT o k en () + " " ) ;
S y s te m .o u t .p r in tln ();
S y s te m .o u t .p r in tln (A r r a y s .to S tr in g (u la z .s p lit (" " ) ) ) ;
Scanner skener = new Sc a n n e r(u la z );
w h ile (s k e n e r.h asN e x t())
S y ste m .o u t.p rin t(s k e n e r.n e x t() + " '') ;
}
} /* Is p is :
But I'm not dead y e t ! I fe e l happy!
[B u t, I'm , not, dead, y e t ! , I , f e e l , happy!]
But I'm not dead y e t ! I fe e l happy!
* ///:-
Pom oću regularnih izraza ili objekata tipa Scanner, znakovni niz m ožete podeliti na
delove i korišćenjem složenijih uzoraka - što bi bilo teško pom oću klase StringTokenizer.
Izgleda da m ožem o bezbedno reći kako je StringTokenizer zastarela.
Sažetak
Ranije je Javina podrška za obradu znakovnih nizova bila rudim entarna, ali u novijim iz-
danjim a jezika postoji m nogo sofisticiranija podrška usvojena iz drugih jezika. Sada je
podrška za znakovne nizove prilično potpuna, iako ponekad m orate paziti na efikasnost,
recimo prilikom upotrebe klase StringBuilder.
R ešenja o d a b r a n ih vežbi d a ta su u e le k tro n sk o m d o k u m e n tu Thinking in Java Annotated Solution
Guide , koji se m o ž e k u p iti na lo k a đ ji www.BruceEckel.com.
Podaci o tipu
Prepoznavanje tipa u vreme izvršavanja (engl. ru n -tim e type identification, RTTI) omogu-
ćuje ustanovljavanje i upotrebu tipa objekta dok se progratn izvršava.
Ovo je tipičan dijagram hijerarhije klasa u kom e se osnovna klasa nalazi na vrhu, a iz-
vedene klase se račvaju niže. Uobičajeni cilj u objektno orijentisanom program iranju jeste
da kod radi s referencama na osnovni tip (u ovom slučaju Oblik), jer ako kasnije odlučite
da proširite program dodavanjem nove klase (kao što je Romboid, izveden iz klase Oblik),
to neće uticati na najveći deo koda. U ovom prim eru, m etoda crtaj() u interfejsu Oblik di-
namički je povezana, pa program er klijent treba da poziva m etodu crtaj() preko generičke
reference na Oblik. Metoda crtaj() se redefiniše u svim izvedenim klasama, a pošto je reč
o dinam ički povezanoj m etodi, ona će se ponašati na odgovarajući način čak i ako se po-
ziva preko generičke reference na objekat klase Oblik. To je polim orfizam.
Stoga obično pravite određeni objekat (Krug, Kvadrat ili Trougao), pretvarate ga u
opštiji tip O blik (zanem arujete specifični tip objekta) i koristite tu referencu tipa Oblik u
ostatku program a.
Poglavlje 14: Podaci o tipu 433
a b s tra c t c la s s O blik {
void c r t a j ( ) { S y s te m .o u t.p rin tln (th is + “ . c r t a j O " ) ; }
a b s tra c t p ub lic S trin g t o S tr in g O ;
}
public c la s s Obli c i {
p u b lic s t a t ic void m a in (S trin g [] args) {
List<Oblik> lis t a O b lik a = A rr a y s .a s L is t (
new K ru g (), new K v a d ra t(), new Trougao()
);
f o r (0 b lik o b lik : lis t a O b lik a )
obl i k . c r t a j ( ) ;
}
} /* Is p is :
K r u g .c r t a j()
K v a d r a t.c r ta j ()
T ro u g ao .crtaj ()
* ///:-
O snovna klasa sadrži m etodu crtaj() koja indirektno koristi m etodu toStringO za
štam panje identifikatora klase pom oću prosleđivanja reference this m etodi Sy-
stem .out.println() (obratite pažnju na to da je m etoda toStringO deklarisana kao
apstraktna, da bi se njeni naslednici prim orali da je redefinišu i da b ise sprečilo pravljenje
objekta tipa Oblik). Ukoliko se u izrazu za nadovezivanje znakovnih nizova (u kom e
postoje znak + i objekti tipa String) pojavi neki objekat, autom atski se poziva m etoda to-
StringO prosleđenog objekta da bi predstavila njegov sadržaj u objektu tipa String. Svaka
izvedena klasa redefiniše m etodu toStringO klase Object, pa crtaj() na kraju u svakom
pojedinačnom slučaju (polim orfno) štam pa nešto različito.
U ovom p rim eru, do svodenja naviše dolazi kada se oblik smesti u List<Oblik>. Pri-
likom svođenja naviše gubi se podatak o tom e da su objekti specifični tipovi klase Oblik.
Što se tiče niza, svi njegovi elem enti su tipa Oblik.
434 Misliti na Javi
U trenutku kada se elem ent čita iz niza, kontejner - za koji su svi njegovi elementi tipa
Object - autom atski pretvara svoj rezultat ponovo u Oblik. To je najosnovniji oblik pre-
poznavanja tipa u vreme izvršavanja, pošto se u Javi u vrem e izvršavanja proverava pra-
vilnost svih konverzija tipova. Upravo to i jeste smisao prepoznavanja tipa u vreme
izvršavanja: tokom izvršavanja, prepoznaje se tip objekta.
U ovom slučaju, konverzija je sam o delimična: Object se konvertuje u tip Oblik, a ne
do kraja u tipove Krug, Kvadrat ili Trougao. U tom trenutku znate samo da je List<Ob-
Iik> p u n objekata tipa Oblik. Tokom prevođenja to osigurava kontejner i Javin sistem ge-
neričkih tipova, ali tokom izvršavanja obezbeđuje ih konverzija tipa.
Sada na scenu stupa polim orfizam , pa se kod koji se izvršava za Oblik bira prem a tom e
da li se radi o referenci na Krug, Kvadrat ili Trougao. Tako bi uglavnom trebalo da bude,
jer želite da najveći deo program a zna što m anje o specifičnim tipovim a objekata, odnosno
da radi sa opštom predstavom o porodici objekata (u ovom slučaju, Oblik). Takav kod će
se lakše pisati, čitati i održavati, a projekti će se lakše prim enjivati, razum eti i menjati. Zbog
toga je polim orfizam jedan od opštih principa u objektno orijentisanom program iranju.
M eđutim , šta će se desiti ako p ri p rogram iranju im ate poseban problem koji se najlak-
še rešava ako znate tačan tip generičke reference? Na prim er, pretpostavim o da želite
om ogućiti korisnicima da posebnom bojom istaknu sve figure određenog oblika? Tako bi
mogli da pron ađu sve trouglove na ekranu, jer su posebno obojeni. Ili, recimo, m etoda je
dobila zadatak da rotira sve oblike navedene u nekoj listi, ali krugove nem a smisla rotirati,
pa biste njih hteli da preskočite. To om ogućuje prepoznavanje tipa u vreme izvršavanja:
možete da zatražite od reference na Oblik da vrati tačan tip na koji ukazuje i tako izabere
i izdvoji specijalne slučajeve.
U JVM-u se sve klase učitavaju dinamiđd, prilikom prve upotre v .To se đešava kada program na-
pravi prvu referencu nekog statičnog člana te klase. Ispostavlja se da ie i konstruktor statična metoda
klase, iako se za njega ne piše rezervisana reč static. Zato se i pravljr ■>y; novog objekta te klase opera-
torom nevv smatra za referenciranje statičnog đana te klase.
Dakle, program napisan na Javi ne učitava se po tp u n o pro ncgo što počne izvršavanje,
nego se delovi program a učitavaju po potrebi. Po tom e se java razlikuje od m nogih tra-
dicionalnih jezika. D inam ičko učitavanje om ogućuje ponašanje koje je teško ili nem o-
guće postići u jezicim a sa statičkim učitavanjem , kao što je C-H-,
Učitavač klasa prvo proverava da li je učitan objekat tipa Class za taj tip. Ako nije, po-
drazum evani učitavač ldasa učitava ga pronalaženjem datoteke .c lass s tim im enom . (Ui
učitavač klasa, instaliran kao softverski dodatak, traži odgovarajuće bajtkodove u bazi
podataka, na prim er.) Tokom učitavanja bajtkodova klase, JVv.' noverava da li su bajtovi
oštećeni i da li sačinjavaju loš kod. (To je jedan od načina nak< ' : r;> <i postižebezbedan rad.)
Kada se objekat Class za taj tip učita u m em oriju, k o r i s t i ; iravljenje svih objekata
tog tipa. Evo jednog program a koji će vas u to ubediti:
c la s s Bombona {
s t a t ic { p rin t("U c ita v a in Bombonu"); }
}
class Zvaka {
static { printC'Ucitavam Zvaku'1); }
}
class Kolacic {
static { print("Ucitavam Kolacic"); }
}
Ucitavam Zvaku
Nakon Class.forN am e("Zvaka")
Ucitavam K o lacic
Posle p ra v lje n ja Kolacica
* ///:-
Klase Bombona, Zvaka i Kolacic sadrže blok static koji se izvršava p ri njihovom pr-
vom učitavanju. Informacije se ispisuju da biste znali kada se učitava odredena klasa.
O bjekti se u m etodi main() prave izm edu naredaba za štam panje, da bi se lakše odredio
tren u tak učitavanja.
Vidite da se svaki objekat klase Class učitava tek kada zatreba, a inicijalizacija bloka
static obavlja se po učitavanju klase.
Posebno je zanimljiv red:
Svi objekti tipa Class pripadaju klasi Class. O bjekat tipa Class je isti kao i svi drugi
objekti, pa m ožete da dobijete referencu na njega i da radite s njom (to radi učitavač kla-
sa). Jedan od načina za dobijanje reference na objekat tipa Class jeste statična m etoda for-
Name() čiji argum ent tipa String sadrži tekstualno im e (pazite na pravopis, m ala i velika
slova!) određene klase čiju referencu hoćete. Ova m etoda vraća referencu na objekat tipa
Class, koja je ovde bila zanem arena; m etodu forName() pozivam o radi njenog spored-
nog dejstva, a to je učitavanje klase Zvaka ako nije već učitana. U postu p k u učitavanja
izvršava se statični blok klase Zvaka.
U p rethodnom prim eru, ako m etoda Class.forN am e( ) zakaže zato što ne može da
pronađe klasu koju treba da učita, generisaće izuzetak ClassNotFoundException. Ovde
sam o prijavljujem o problem i idem o dalje, ali u sofisticiranijim program im a mogli biste
pokušati da rešite problem u bloku za o bradu izuzetaka.
Kad god budete hteli da upotrebite podatke o tipu prepoznate u vreme izvršavanja, naj-
pre ćete m orati da pribavite referencu odgovarajućeg objekta Class. Jedan od podesnih
načina za to pruža m etoda Class.forN am e( ), zato što vam nije potreban objekat tog tipa
da biste dobili referencu klase Class. M eđutim , ukoliko već im ate objekat tipa koji vas za-
nim a, referencu klase Class m ožete pribaviti pozivanjem m etode getC lass( ) koja je deo
korenske klase Object. Ona vraća referencu klase Class koja predstavlja stvarni tip objek-
ta. Class ima m nogo zanimljivih m etoda; evo nekih od njih:
in te rfa c e Im aB aterije {}
in te rfa c e OtpornaNaVodu {}
in te rfa c e Puca {}
c la s s Igracka {
// Sled eći podrazumevani konstruktor p re tv o r ite u komentar
// da b is te v id e li izuzetak NoSuchMethodError od ( * !* )
Poglavlje 14: Podaci o tipu 437
Ig ra c k a () {}
Ig ra c k a (in t i ) {}
}
S y s t e m .e x it (l);
} c a tc h (Ille g a lA c c e ss E x c e p tio n e) {
p rin t("N e mogu da p ris tu p im ");
S y s t e m .e x it (l);
}
p r in t In f o (o b j.g e t C la s s ()) ;
}
} /* Is p is :
Ime k lase: typ e in fo .ig ra c k e .L e p a Ig ra c k a j e in t e r f e j s ? [ f a ls e ]
Prosto ime: Lepalgracka
Kanonsko ime : typ ein fo .ig ra c k e .L e p a lg ra c k a
Ime k lase: ty p e in fo .ig ra c k e .Im a B a te rije je in t e r f e j s ? [tru e ]
Prosto ime: Im aB aterije
Kanonsko ime : ty p e in fo .ig ra c k e .Im a B a te rije
Ime k lase: typeinfo.igracke.OtpornaNaVodu je in t e r f e j s ? [tru e ]
Prosto ime: OtpornaNaVodu
4 38 Misliti na Javi
Vežba 5: (3) U program u Oblici.java realizujte m etodu rotiraj(O blik) tako da proverava
da li joj je zadato da rotira Krug (i neka ga u tom slučaju ne rotira).
Vežba 6: (4) Prepravite program Oblici.java tako da ističe sve oblike određenog tipa, od-
nosno postavlja indikator u njim a. M etoda to S trin g ( ) svake izvedene potklase klase Ob-
Iik treba da pokaže da li je taj O blik ,,istaknut“.
Vežba 7: (3) Prepravite ProdavnicaSlatkisa.java tako da svaku vrstu pravljenja objekta
određuje argum ent koji se zadaje na kom andnoj liniji. Dakle, ako na kom andnoj liniji
piše „java ProdavnicaSlatkisa Bombon“, o nda se pravi sam o objekat tipa Bombon.
O bratite pažnju na to kako argum entom s kom andne linije određujete Class objekte koji
če biti učitani.
Vežba 8: (5) Napišite m etodu koja prim a objekat i rekurzivno ispisuje sve klase u njegovoj
hijerarhiji.
Vežba 9: (5) Prepravite prethodnu vežbu tako da se pom oću m etode Class.getDeclared-
F ields( ) prikazuju i podaci o poljim a klase.
Vežba 10: (3) Napišite program koji utvrđuje da li je niz elem enata tipa char p ro st tip ili
pravi objekat.
Literali klase
Java nudi još jedan način za dobijanje reference na objekat klase Class, a to je korišćenje
literala klase (engl. class literal). U prethodno navedenom p rogram u to bi izgledalo ovako:
što ne sam o da je jednostavnije, već je i bezbednije jer se proverava već tokom prevođenja
(te stoga ne m ora biti smešteno u blok try). Pošto se ne poziva m etoda forName(), isto-
vrem eno je i efikasnije.
Literali klase rade sa običnim klasama, ali i sa interfejsima, nizovim a i prostim tipovi-
ma. Pored toga, sve om otačke klase prostih tipova sadrže standardno polje TYPE. Ovo
polje daje referencu na objekat tipa Class za sve odgovarajuće proste tipove, kao što su:
...ekvivalentno je ...
boolean.class Boolean TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class lnteger.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE
440 Misliti na Javi
Preporučujem da koristite verzije class ako je to moguće, pošto se tako pristupa i obič-
nim klasama.
Zanimljivo je prim etiti da se pravljenjem reference na objekat tipa Class literalom
class, taj objekat ne inicijalizuje autom atski. Priprem a klase za upotrebu zapravo obu-
hvata tri koraka:
1. Učitavanje, koje obavlja učitavač klasa. O na pronalazi bajtkod (obično na disku na-
vedenom u sistemskoj prom enljivoj classpath, ali to ne m o ra biti slučaj) i od njih
pravi objekat tipa Class.
2. Povezivanje. U fazi povezivanja proverava se bajtkod klase, dodeljuje m em orija sta-
tičnim poljim a, i ako treba, razrešavaju sve reference na druge klase koje ova klasa
pravi.
3 . Inicijalizaciju. Ako postoji natklasa, ona se inicijalizuje. Izvršavaju se statični inici-
jalizatori i statični blokovi inicijalizatora.
Inicijalizacija se odgađa do prve reference na neku statičnu m etodu (konstruktor je
im plicitno statičan) ili na neko nekonstantno statično polje:
c la s s In ita b e la {
s t a t ic f in a l in t s t a t ic F in a l = 47;
s t a t ic fin a l in t s t a t ic F in a l2 =
C la s s ln it ia liz a t io n .s lu c a ja n . n e x t ln t (1000);
s t a t ic {
S y s t e m .o u t .p r in t ln ( " In ic ij a liz a c ija In i t a b e l e " ) ;
1
}
»
c lass In ita b e la 2 {
s t a t ic in t sta tic n a N e Fin a ln a = 147;
s t a t ic {
S y s t e m .o u t .p r in t ln ( " In ic ij a liz a c ija In it a b e le 2 " ) ;
}
}
c la s s In ita b e la 3 {
s t a t ic in t staticn a N eFin aln a = 74;
s t a t ic {
S y s t e m .o u t .p r in t ln ( " In ic ij a liz a c ija In it a b e le 3 " ) ;
}
}
C lass In ita b e la = In it a b e la .c la s s ;
Sy stem .o u t.p rin tln ("N ak o n p ra v lje n ja re f In it a b e le " ) ;
// Ne pokreće i n i c i j a l i z a c i j u :
S y s t e m .o u t .p r in t ln ( In it a b e la .s t a t ic F in a l) ;
// Pokreće i n i c i j a l i z a c i j u :
S y s te m .o u t .p r in t ln ( In it a b e la .s t a t ic F in a l2 ) ;
// Pokreće i n i c i j a l i z a c i j u :
S y s te m .o u t.p rin tln (In ita b e la 2 .s ta tic n a N e F in a ln a );
Class In ita b e la 3 = C la s s .fo rN a m e ("In ita b e la 3 ");
Sy stem .o u t.p rin tln ("N ak o n p ra v lje n ja r e f In it a b e le 3 " ) ;
S y s te m .o u t.p rin tln (In ita b e la 3 .s ta tic n a N e F in a ln a );
}
} /* Is p is :
Nakon p ra v lje n ja r e f In ita b e le
47
I n i c i j a l i z a c i j a In ita b e le
258
I n i c i j a l i z a c i j a In ita b e le 2
147
I n i c i j a l i z a c i j a In ita b e le 3
Nakon p ra v lje n ja r e f In ita b e le 3
74
* ///:-
Class<Number> genericNumberClass = in t . c la s s ;
Ovo naizgled ima smisla jer se Integer nasleđuje od klase Number. Ali to nije ispravno,
zato što Integer Class objekat nije potklasa N um ber Class objekta. (Razlika vam je mož-
da previše tanana; razm otrićem o je pobliže u poglavlju Generički tipovi).
Da bih ublažio ograničenja prilikom korišćenja generičkih Class referenci, upotreblja-
vam džokera koji je deo Javinih generičkih tipova. Džokerski znak je ? i on naznačuje „bilo
šta“. Stoga običnoj Class referenci u gornjem p rim eru m ožem o dodati džokere i dobiće-
mo iste rezultate:
U Javi SE5, Class<?> treba da im a prednost nad golim Class iako su ekvivalentni, a
golo Class, kao što ste videli, ne prouzrokuje upozorenje prevodioca. Prednost reference
Class<?> jeste to što pokazuje da nespecifičnu referencu klase ne upotrebljavate slučajno
ili zbog neznanja, nego namerno.
Za pravljenje Class reference ograničene na određeni tip ili bilo koji njegov podtip,
kom binujte džokera i rezervisanu reč extends; tim e pravite ograničenje. Dakle, um esto da
kažete sam o Class<Number>, recite:
Poglavlje 14: Podaci o tipu 443
c la s s PrebrojaniC eoBroj {
p riv a te s t a t i c long b ro jac;
p riv a te f in a l long id = brojac++;
p u b lic S trin g to S trin g O { return L o n g .to S tr in g (id ); }
}
Vodite računa o tom e da ova klasa m ora pretpostaviti kako svaki tip s kojim radi im a
podrazum evani konstruktor (onaj bez argum enata), inače ćete dobiti izuzetak. Za ovaj
program prevodilac ne generiše nikakvo upozorenje.
Zanimljiva se stvar dešava kada za objekte tipa Class upotrebite generičku sintaksu:
m etoda new lnstance( ) vratiće tačan tip objekta, a ne sam o tip Object kako ste videli u
program u Ispitivanjelgracaka.java. To je donekle ograničeno:
Ako dobijete natklasu, prevodilac će dozvoliti samo da kažete kako je referenca natkla-
se „neka klasa koja je natklasa klase Lepalgracka“, kao što se vidi iz izraza Class<? super
Lepalgracka>. Neće prihvatiti deklaraciju CIass<Igracka>. To izgleda pom alo čudno,
zato što getSuperclass( ) vraća osnovnu klasu (ne interfejs), a prevodilac u vrem e pre-
vođenja zna koja je to klasa - u ovom slučaju, Igracka.class, a ne sam o „neka natklasa od
Lepalgracka“. U svakom slučaju, zbog te neodredenosti, povratna vrednost m etode
nad.new lnstance( ) nije tačan tip, nego sam o Object.
c la s s Zgrada { }
c la s s Kuca extends Zgrada { }
p u b lic c la s s KonverzijaClassTipova {
p u b lic s t a t i c void m a in (S trin g [] args) {
Zgrada z = new K u c a ();
Class<Kuca> tipKuce = K u c a.class;
Kuca k = tip K u c e .c a s t(z );
k = (K u ca)z ; // . . . i l i u ra d ite samo ovo.
}
} ///:-
Poglavlje 14: Podaci o tipu 445
M etoda c a st( ) prim a objekat kao argum ent i pretvara ga u tip Class reference. Narav-
no, nakon što pogledate gornji kod, to izgleda kao m nogo više posla nego u poslednjem
redu m etode m a in ( ) koji radi istu stvar.
N ova sintaksa za konverziju tipova je podesna za situacije u kojim a ne možete da upo-
trebite običnu konverziju tipa. To se najčešče događa kada pišete generički kod (što ćete
naučiti u poglavlju Generički tipovi) i uskladištili ste Class referencu koju kasnije nam e-
ravate da upotrebite za konverziju. Ispostavlja se da je to retko p o treb n o —u celoj biblio-
teci Jave SE5 našao sam sam o jedan slučaj gde je m etoda c a st( ) bila korišćena (u
com .sun.m irror.util.D eclarationFilter).
Jedna nova m ogućnost uopšte nije našla p rim enu u biblioteci Jave SE5: Class.asSub-
class( ). O na služi za konverziju objekta tipa Class u specifičniji tip.
G ornja naredba if proverava da li objekat x pripada klasi Pas pre nego što izvrši kon-
verziju u tip Pas. Kada nem a drugih inform acija o tipu objekta, važno je da se in stan ceo f
upotrebi pre svođenja naniže; u suprotnom , nastaće izuzetak tipa ClassC astException.
446 Misliti na Javi
O bično ćete biti usredsređeni na jedan tip (npr. da pretvorite sve trouglove u ružičaste),
ali pom oću rezervisane reči instanceof m ožete lako proveriti sve objekte. Pretpostavimo
da im am o porodicu klasa za opisivanje kućnih Ljubimaca (i njihovih vlasnika, što će nam
zatrebati kasnije). Svaka Jedinka u hijerarhiji im a id i opciono ime. Iako sledeće klase na-
sleđuju klasu Jedinka, ona je donekle složena, pa ćemo njen kod prikazati i objasniti u
poglavlju Detaljno razmatranje kontejnera. Kao što zapažate, u ovom trenutku i nije neop-
hodno da vidite kod klase Jedinka - treba sam o da znate sledeće: možete je napraviti sa
im enom ili bez im ena i svaka Jedinka im a m etodu id ( ) koja vraća jedinstven identifikator
(napravljen brojanjem svakog objekta). Tu je i m etoda toS tring( ); ako napravite prim erak
klase Jedinka, a ne date joj ime, m etoda to S trin g ( ) vratiće samo prosto ime tipa.
Ova hijerarhija klasa nasleđuje klasu Jedinka:
/ / : podaciotipu/1jubim ci/Mesanac.ja va
package p o d a c io tip u .lju b im c i;
I I : podaciotipu/1jubim ci/Velska.java
package p o d a c io tip u .lju b im c i;
Da bi dobila Listu objekata tipa Class, apstraktna m etoda tip o v i( ) obraća se nekoj izve-
denoj klasi - ovo je varijanta projektnog obrasca Template Method (Šablonska metoda).
Pošto je zadato da tip klase bude „nešto izvedeno iz klase Ljubixnac“, m etoda new-
Instance( ) pravi objekat tipa Ljubimac bez konverzije tipa. nekiLjubim ac( ) nasumično
indeksira Listu i izabrane objekte tipa Class upotrebljava za generisanje nove instance te
klase pom oću m etode Class.new lnstance( ). M etoda createA rray( ) upotrebljava m etodu
nekiLjubim ac( ) za popunjavanje niza, a arrayL ist( ) upotrebljava createArray( ).
Kada pozovete m etodu new ln stance( ), m ožete da prouzrokujete dve vrste izuzetaka
koje su obrađene u odredbam a catch iza bloka try. Podsetim o se, im ena izuzetaka su re-
lativno jasna objašnjenja onoga što je zakazalo (IllegalAccessException označava kršenje
pravila Javinog m ehanizm a bezbednosti, u ovom slučaju podrazum evani konstruktor je
privatan).
Kada izvodite potklasu klase PravljenjeLjubimaca, jedino m orate da navedete Listu
tipova ljubim aca koju hoćete da napravite m eto dom nekiLjubim ac( ) i drugim m etoda-
ma. M etoda getTypes( ) obično vraća sam o referencu neke statične Liste. Evo jedne rea-
lizacije m etodom forN am e( ):
M etoda lo ad e r( ) pravi Listu objekata tipa Class pom oću m etode Class.forN am e( ).
To može da prouzrokuje izuzetak ClassNotFoundException, što im a smisla jer joj pro-
sleđujete znakovni niz čija se validnost ne m ože proveriti u vrem e prevođenja. Pošto su
objekti tipa Ljubimac u paketu podaciotipu, prilikom navođenja im ena klase m ora se
navesti i ime paketa.
Da bi se napravila Lista objekata tipa Class za različite tipove, potreb n a je konverzija
tipova koja prouzrokuje upozorenje u vrem e prevođenja. M etodu lo a d e r( ) definišemo
zasebno i zatim sm eštam o u n u tar statičnog bloka za inicijalizaciju, zato što se anotacija
@SuppressWarnings ne m ože napisati direktno u n u ta r statičnog bloka za inicijalizaciju.
Za brojanje prim eraka klase Ljubimci treba nam alatka koja prati broj različitih tipova
prim eraka klase Ljubimci. Za to je Mapa savršena; ključevi su im ena tipova Ljubimaca,
a vrednosti Integeri koji odražavaju broj Ljubimaca. Na taj način m ožete reći: „Koliko
im a objekata tipa Hrcak?" Za brojanje prim eraka klase Ljubimci m ožem o upotrebiti re-
zervisanu reč instanceof:
p u b lic c la s s Ljubimci {
p u b lic s t a t i c fin a l Pravljen jeL ju b im a ca p ra v lje n je =
new R u cn o Pravljen je L ju b im a ca ();
p u b lic s t a t ic Ljubimac nekiLjubim acO {
return p r a v lje n je .n e k iL ju b im a c ();
}
Poglavlje 14: Podaci o tipu 453
// : podaciotipu/PrebrojavanjeLjubim aca2.java
import p o d a c io tip u .lju b im c i.* ;
Dinamički instanceof
M etoda Class.islnstance nudi način za dinam ičko ispitivanje tipa objekta. Tako se sve do-
sadne naredbe instanceof m ogu ukloniti iz prim era PrebrojavanjeLjubimaca:
p u b lic S trin g t o S t r in g () {
S trin g B u ild e r re z u lta t = new S t r in g B u ild e r C 'C ') ;
for(M ap.Entry<Class<? extends Ljubimac>,Integer> par
: e n t r y S e t ()) {
rez u lta t.a p p e n d (p a r.g e tK e y ().g e tSim p le N a m e ());
rez u ltat.ap p en d ("= “ ) ;
rez u lta t.a p p en d (p a r.g etV al u e ( ) ) ;
re z u lta t.a p p e n d (", " ) ;
}
r e z u lt a t .d e le t e (r e z u lt a t .le n g t h ()- 2 , r e z u lt a t .le n g t h O );
re z u lta t.a p p e n d C '}");
return r e z u l t a t . t o S t r in g ( ) ;
}
}
p u b lic s t a t ic void m a in (S trin g [] args) {
BrojacLjubim aca brojLjubim aca = new B ro ja cL ju b im a c a ();
for(Ljub im ac ljubim ac : L ju b im c i.c re a te A rra y (2 0 )) {
p rin tn b (l jub im ac.g etC lassO .getSimpleName() + " " ) ;
b ro jLju b im a ca .p reb ro j(1 ju b im a c );
}
p r in t();
p rin t(b ro jL ju b im a c a );
}
} /* Is p is :
Pacov Manska Velska Mesanac Mops Velska Mops Manska Velska Pacov Egipatska
Hrcak Egipatska Mesanac Mesanac Velska Mis Mops Mis Velska
{Ljubimac=20, Pas=6, Macka=9, Glodar=5, Mesanac=3, Mops=3, Egipatska=2, Man-
ska=7, Velska=5, Pacov=2, Mis=2, Hrcak=l}
* ///:-
Da bi se prebrojali svi različiti tipovi prim eraka klase Ljubimac, Mapa BrojacLjubi-
maca se unapred popunjava tipovim a iz liste RucnoPravIjenjeLjubimaca.sviTipovi. Ta
lista upotrebljava klasu net.mindview.util.M apData koja prim a objekat tipa Iterable (li-
stu sviTipovi) i konstantu (u ovom slučaju, nulu), i popunjava Mapu ključevima uzetim
iz liste sviTipovi i vrednošću nula. Da Mapu nism o popunili unapred, prebrojali bismo
sam o nasum ično generisane tipove, ali ne i osnovne tipove kao što su Ljubimac i Macka.
Vidite da zbog korišćenja m etode islnstance() nisu potrebni izrazi instanceof. Osim
toga, to znači i da nove tipove kućnih ljubim aca m ožete da dodajete jednostavnim me-
njanjem niza tipovi; ostatak program a ne m ora da se m enja (što nije bio slučaj pri koriš-
ćenju izraza instanceof).
M etodu to S trin g () preklopili sm o da bi davala čitljiviji izlaz koji i dalje odgovara
tipičnom izlazu koji se dobija ispisivanjem M ape.
Rekurzivno brojanje
Mapa u PrebrojavanjeLjubimaca3.BrojacLjubimaca bila je unapred popunjena svim
različitim klasama Ljubimac. Umesto da m apu popunjavam o unapred, m ožem o upotre-
biti m etodu Class.isAssignableFrom ( ) i napraviti alatku opšte nam ene koja um e da bro-
ji sve, a ne samo prim erke klase Ljubimci:
Poglavlje 14: Podaci o tipu 455
// : podaciotipu/PrebrojavanjeLjubim aca4.ja va
import p o d a c io tip u .lju b im c i.* ;
import n e t.m in d v ie w .u til.* ;
import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ;
456 Misliti na Javi
Generički p aram etar T znači da m etoda create( ) m ože da vraća različit tip rezultata za
razne realizacije interfejsa Proizvodjac.
U ovom prim eru, osnovna klasa Deo sadrži listu objekata tipa Proizvodjac. D oda-
vanjem u listu proizvodjaciDelova, u osnovnoj klasi se ,,registruju“ proizvodni interfejsi
za tipove koje će praviti m etoda createR andom ( ):
c la s s Oeo (
pu b lic S trin g to S tr in g () {
return g etC lass().g etSim p le N am e();
}
s t a t ic List< Proizvodjac< ? extends D e o » pro izvo djaciD elo va =
new A rrayList< Proizvodjac< ? extends D e o » ( ) ;
s t a t ic {
// C o lle c tio n s .a d d A ll() daje upozorenje "unchecked generic
// a rra y cre atio n . . . fo r varargs parameter"
/'/ ("neprovereno p ra v lje n je generičkog niza . . .
// za parametar prom enljive d u ž in e ").
proizvodjaciD elova.add(new P r e c is t a c G o r iv a .P r o iz v o d ja c O );
proizvodjaciD elova.add(new P re c is ta c V a z d u h a .P ro iz v o d ja c O );
proizvodjaciD elova.add(new PrecistacVazduhaZaKabinu. P ro iz v o d ja c ( ) ) ;
proizvodjaciD elova.add(new P r e c is t a c U lja .P r o iz v o d ja c ()) ;
proizvodjaciD elova.add(new K a is V e n tila to r a .P r o iz v o d ja c O );
proizvodjaciD elova.add(new K a is S e rv o V o la n a .P ro iz v o d ja c O );
proizvodjaciD elova.add(new K a is G e n e ra to ra .P ro iz v o d ja c ()) ;
}
p riv a te s t a t ic Random slu ca ja n = new Random(47);
p ub lic s t a t ic Deo createRandom() {
in t n = s lu c a ja n . n e x tIn t(p r o iz v o d ja c iD e lo v a .s iz e ()) ;
return p ro iz v o d ja c iD e lo v a .g e t (n ).c r e a t e ();
}
}
c la s s F i l t a r extends Deo {}
c la s s P r e c is ta c U lja extends F i l t a r {
p ub lic s t a t i c c la s s Proizvodjac
implements p o d a cio tip u .p ro izvo d jac.P ro izvo d jac< P recistacU lja> {
p u b lic P r e c is ta c U lja c re a te () { return new P r e c i s t a c U lj a ( ) ; }
}
}
Neke klase iz hijerarhije nisu u listi: Filtar i Kais su sam o klasifikatori, pa nikad ne pra-
vim o objekte tih klasa, nego sam o njihovih potklasa. Klase koje m etoda createRandom(
) treba da uzm e u obzir sadrže un utrašnju klasu Proizvodjac. Kao što vidite, osnovni in-
terfejs Proizvodjac koristi se tako što se navede p o tp u n o ime podaciotipu.proizvod-
jac.Proizvodjac i tako se izbegava dvosmislenost.
Prilikom pravljenja liste objekata nism o koristili m etodu CoIIections.addAll( ), zato
što bi to izazvalo grešku „generic array creation“ (pravljenje generičkog niza, što će biti
objašnjeno u poglavlju Generički tipovi). Umesto nje, koristio sam m etodu a d d ( ). Meto-
da createR andom ( ) nasum ično bira objekat iz liste proizvodjaciDelova i poziva njegovu
m etodu create( ) da napravi nov Deo.
Vežba 14: (4) I konstruktor je svojevrsna proizvodna m etoda. Prepravite program Regi-
strovaniProizvodjaci.java tako da se um esto eksplicitnog navođenja im ena proizvođača,
objekat klase smešta u listu, i svaki se objekat pravi m etodom new lnstance( ).
Vežba 15: (4) Realizujte novu kiasu PravIjenjeLjubimaca koristeći Registrovane Proiz-
vodiace i prepravite Fasadu Ljubimci tako da upotrebljava nju, a ne druge dve. Postarajte
se da ostali prim eri u kojima se upotrebljava program Ljubimci.java i dalje rade ispravno.
Vežba 16: (4) Izmenite hijerarhiju Kafa u poglavlju Generički tipovi tako da se koriste Re-
gistrovani Proizvodjaci.
//: podaciotipu/SrodnostProtivTacnogTipa.java
// R azlik a između in stan ceof i e k v iv a le n c ije
package podaciotipu;
import s t a t ic n e t.m in d v ie w .u til. P r in t . * ;
460 Misllti na Javi
c lass Osnovna { }
c la s s Izveđena extends Osnovna {}
p ub lic c la s s SrodnostProtivTacnogTipa {
s t a t ic void te s t(O b je c t x) {
p r in t (" T e s t ir a n je x tip a " + x .g e t C la s s ( )) ;
p r in t (" x in stan ceo f Osnovna " + (x in stan ceo f Osnovna));
p r in t (" x in stan ceo f Izvedena " + (x in stan ceo f Iz ved en a));
p rin t("O s n o v n a .is In s ta n c e (x ) " + O s n o v n a .c la s s .is ln s ta n c e (x ));
p r in t("lz v e d e n a .is ln s t a n c e (x ) " + Iz v e d e n a .c la s s .is ln s ta n c e (x ));
p r in t (" x .g e t C la s s () == O snovna.class " +
(x .g e tC la s s () == O s n o v n a .c la s s ));
p r in t (" x .g e t C la s s () == Iz ved en a.class " +
(x .g e tC la s s () == Iz v e d e n a .c la s s ));
p r in t("x .g e tC la s s (),e q u a ls (O s n o v n a .c la s s )) " +
(x .g e tC la s s ().e q u a ls (O s n o v n a .c la s s )));
p r in t("x .g e t C la s s ().e q u a ls (Iz v e d e n a .c la s s )) " +
(x .g e t C la s s ().e q u a ls (Iz v e d e n a .c la s s )));
}
p ub lic s t a t i c void m a in (S trin g [ ] args) {
test(new O sno vn aO );
test(new Iz v e d e n a ());
}
} /* Is p is :
T e s tira n je x tip a c la s s Osnovna
x in stan ce o f Osnovna tru e
x in stan ce o f Izvedena fa ls e
O sn o vn a.isln stan ce(x ) tru e
Iz v e d e n a .isln sta n ce (x ) fa ls e
x .g e tC la s s () == Osnovna.class tru e
x .g e tC la s s () == Iz ved en a.class fa ls e
x .g e tC la s s (),e q u a ls (O s n o v n a .c la s s )) true
x .g e tC la s s (),e q u a ls (Iz v e d e n a .c la s s )) fa ls e
T e s tira n je x tip a c la s s Izvedena
x in stan ce o f Osnovna tru e
x in stan ce o f Izvedena tru e
O sn o vn a.isIn stan ce(x ) tru e
Iz v e d e n a .is ln s ta n c e (x ) tru e
x .g e tC la s s () == Osnovna.class fa ls e
x .g e tC la s s () == Iz ve d e n a.class true
x .g e tC la s s ().e q u a ls (O s n o v n a .c la s s )) fa ls e
x .g e tC la s s (),e q u a ls (Iz v e d e n a .c la s s ) true
* ///:-
M etoda test() proverava tip, pri čem u njen argum ent koristi oba oblika instanceof.
Potom čita referencu na Class i koristi = = i equals() za ispitivanje jednakosti objekata
tipa Class. Kao što je i očekivano, instanceof i islnstance() daju istovetne rezultate, isto
kao i equals() i ==. M edutim , sami testovi dovođe do različitih zaključaka. Saglasno s
pojm om tipa, instanceof pita: ,,Da li je ovo tvoja klasa, ili klasa izvedena iz nje?“ S druge
strane, ako stvarne objekte tipa Class uporedite pom oću operatora = =, tu nasteđivanje
ne igra nikakvu ulogu: objekti su ili istovetnog tipa ili nisu.
Poglavlje 14: Podaci o tipu 461
Klasa Class, opisana ranije u poglavlju, podržava pojam refleksije, a postoji i dodatna
biblioteka java.lang.reflect s klasam a Field, M ethod i C onstructor (pri čem u svaka rea-
lizuje interfejs Member). O bjekte tih tipova pravi Javina virtuelna m ašina tokom izvrša-
vanja, da bi predstavila odgovarajućeg člana nepoznate klase. Zatim m ožete koristiti
konstruktore da biste pravili nove objekte, m etode get() i set() za čitanje i m enjanje polja
povezanih sa objektim a tipa Field i m etodu invoke() za pozivanje m etode povezane
s objektom tipa M ethod. Pored toga, m ožete da pozovete m etode getFields(), getMet-
hods(), getConstructors() itd. da biste dobili nizove objekata koji predstavljaju polja,
m etode i konstruktore. (Više o ovom e naći ćete u dokum entaciji o klasi Class u Javinom
razvojnom okruženju, JDK.) Znači, inform acije o klasi an onim nih objekata m ogu se u
p otpunosti otkriti tokom izvršavanja, a ništa ne m ora da se zna tokom prevođenja.
Važno je shvatiti da refleksija nije ništa posebno. Kada koristite refleksiju za rad sa
objektim a nepoznatog tipa, Javina virtuelna m ašina će proveriti da li neki objekat pripada
određenoj klasi (kao p ri običnom p ostu p ku prepoznavanja tipa u vreme izvršavanja), ali
zatim , pre nego što uradi bilo šta drugo, m ora da učita objekat tipa Class. Stoga datoteka
.class za taj tip i dalje m ora da bude d o stu pna Javinoj virtuelnoj m ašini, bilo na lokalnom
sistem u ili u mreži. Prem a tom e, prava razlika izm eđu prepoznavanja tipa u vreme izvr-
šavanja i refleksije leži u tom e što u prvom načinu prevodilac otvara i ispituje datoteku
.class tokom prevođenja. D rugim rečima, m ožete da pozovete sve m etode objekta na
uobičajen način. Pri refleksiji, datoteka .class nije dostupna tokom prevođenja; nju otva-
ra i ispituje izvršno okruženje.
M etode getMethods() i getC onstructors() klase Class vraćaju niz objekata tipa Met-
hod odnosno Constructor. Svaka od ovih klasa im a dalje m etode za izdvajanje im ena, ar-
gum enata i povratnih vrednosti m etoda koje predstavljaju. M eđutim , za dobijanje
znakovnog niza s potpunim potpisom m etode možete da koristite sam o m etodu to-
StringO, kao što je ovde učinjeno. O statak koda služi samo za izdvajanje inform acija s ko-
m andne linije, za utvrđivanje da li određeni potpis odgovara ciljnom znakovnom nizu
(korišćenjem funkcije indexOf()) i za odbacivanje kvalifikatora im ena p om oću regular-
nih izraza (koji su predstavljeni u poglavlju Znakovni nizovi).
Pošto rezultat koji daje Class.forName() ne m ože da bude poznat u vrem e prevođenja,
sve inform acije o potpisu m etode izdvajaju se tokom izvršavanja. Ako potražite objašnje-
nje refleksije u dokum entaciji na Webu, videćete da im a dovoljno podrške za stvarno po-
dešavanje i pozivanje m etode za objekat koji je p o tp u n o nepoznat tokom prevođenja
(prim era za ovo biće u nastavku knjige). lako ovo m ožda nikada nećete m orati da radite
sami, vrednost pune refleksije um e da iznenadi.
G ornje rezultate je proizvela kom andna linija:
Ovo daje listing koji sadrži podrazum evani javni konstruktor, m ada se u kodu vidi da
nije definisan nikakav konstruktor. K onstruktor koji vidite autom atski pravi prevodilac.
Ako potom klasu PrikaziMetode pretvorite u nejavnu (dakle, s paketnim p ristupom ),
napravljen podrazum evani konstruktor se više neće pojavljivati u rezultatu program a.
N apravljenom podrazum evanom konstruktoru se autom atski dodeljuje isti nivo pristu-
pa kao klasi.
Bilo bi zanimljivo pozvati i java P rikaziM etodc java.lan g .S trin g uz dodatni argum ent
tipa char, in t, S trin g itd.
Ova alatka zaista može da uštedi vrem e prilikom program iranja, kada ne m ožete da se
setite da li klasa ima neku m etodu, a ne želite da pregledate hijerarhiju klasa u dokum en-
taciji na Webu, ili ne znate da li ta klasa, prim era radi, može da radi nešto sa objektiina
tipa Color.
Poglavlje Grafićka koristiička okruženja sadrži grafičku verziju ovog program a (prila-
gođenu za izdvajanje inform acija za Swing kom ponente), pa m ožete da je izvršavate dok
pišete kod da biste brzo pronašli ono što vam treba.
Vežba 17: (2) Izm enite regularan izraz u program u PokaziMetode.java tako da se
uklanjaju i rezervisane reči native i final. Uputstvo: upotrebite o perator I (OR).
Vežba 18: (1) Izm enite klasu PokaziMetode tako da ne bude javna i dokažite da se napra-
vljen podrazum evani konstruktor više ne pojavljuje u rezultatu program a.
Poglavlje 14: Podaci o tipu 465
Dinamički posrednici
Projektni obrazac Proxy (Posrednik) jedan je od osnovnih projektnih obrazaca. To je ob-
jekat koji umećete um esto ,,pravog“ objekta da biste obavili neke dopunske ili različite
operacije — m eđu njim a je obično i kom unikacija sa određenim „pravim " objektom .
S truk turu projektnog obrasca Proxy pokazaćem o n a jed n o m trivijalnom prim eru:
in te rfa c e In t e r fe js {
void u ra d iN e s to ();
void nestoDrugo(String a r g );
1
c la s s ProstPrim erZaPosrednika {
p u b lic s t a t ic void p o tr o s a c (In te r fe js i f e j s ) {
if e js .u r a d iN e s t o ( ) ;
i f e j s . nestoDrugo("bonobo");
466 Misliti na Javi
Pošto p o tro sac( ) prim a jedan Interfejs, on ne m ože znati da li je dobio PraviObjekat
ili objekat tipa JednostavanPosrednik, jer oba realizuju Interfejs. Ali JednostavanPo-
srednik, koji je um etnut izm eđu klijenta i objekta PraviObjekat obavlja operacije i zatim
poziva identičnu m etodu za PraviObjekat.
Projektni obrazac Proxy je podesan kad god neke dodatne operacije treba izdvojiti od
„pravog objekta" i naročito kada hoćete da olakšate prelaz od nekorišćenja tih dodatnih
operacija na korišćenje i obrnuto (svrha projektnog obrasca jeste kapsuliranje prom ena
- da biste opravdali upotrebu obrasca, nešto m orate da menjate). Na prim er, kako biste
pratili pozive m etoda u obiektu PraviObjekat ili merili režijske troškove tih poziva? Tak-
vom kodu nije m esto u gotovoj aplikaciji, pa Proxy om ogućuje da ga lako dodate i uklonite.
Javin dinamički posrednik (engl. dynamicproxy) podiže ideju upotrebe posrednika na
viši nivo, pošto objekat posrednika pravi dinam ički i isto tako obrađuje pozive posredo-
vanih m etoda. Svipozivi dinam ičkogposrednika bivaju preusm ereni na jedini blokza po-
zive (engl. invocation handler) koji prepoznaje poziv i odlučuje šta s njim da radi. Preradio
sam ProstPrim erZaPosrednika.java tako da posređnik bude dinamički:
c la s s ProstDinam ickiPosrednik {
p ub lic s t a t ic void p o tr o s a c (In te r fe js i f e j s ) {
if e js .u r a d iN e s t o ( ) ;
ifejs.n e sto D ru g o ("b o n o b o ");
}
p ub lic s t a t ic void m a in (S trin g [] args) {
PraviO bjekat pravi = new P r a v iO b je k a t();
p o tro s a c (p ra v i) ;
// Umetni posrednika i pozovi ponovo:
In t e r f e js posrednik = (In te rfe js )P ro x y.n e w P ro x yIn sta n c e (
In t e r fe js .c la s s .g e t C la s s L o a d e r (),
new C la s s [ ]{ In t e r f e js .c la s s } ,
new B1okDi nami ckogPosredni k a (p ra v i) ) ;
p o tro sac(p o sre d n ik );
}
} /* Is p is : (95% podudaranja)
uradiNesto
nestoDrugo bonobo
** ** posrednik: c la s s $ProxyO, metoda: pu b lic a b s tra ct void
In t e r f e js .u r a d iN e s t o (), argumenti: null
uradiNesto
**** posrednik: c la s s $ProxyO, metoda: pu b lic a b s tra ct void
In te rfe js .n e s to D ru g o (ja v a .la n g .S trin g ), argumenti:
[ Ljava.lang.0bject;@ 42e816
bonobo
nestoDrugo bonobo
* ///:-
in te rfa c e NekeMetode {
void d o sad n alO ;
void dosadna2();
void z a n im ljiv a (S tr in g a r g );
void z a n im ljiv a 3 ( ) ;
}
c lass IzborMetoda {
p ub lic s t a t ic void m a in (S trin g [] args) {
NekeMetode posrednik= (NekeMetode)Proxy.newProxyInstance(
NekeMetode.class. g e tC la s s L o a d e r(),
n e w C la s s []{ NekeMetode.class } ,
new IzborMetoda(new R e a li z a c i j a ( ) ) ) ;
posrednik.dosadnal( ) ;
posredni k.dosadna2 ();
posrednik.zanim lj i va ("b o n o b o ");
posredni k.dosadna3 ();
}
} /* Is p is :
dosadnal
dosadna2
Posrednik je o tk rio za n im ljivu metodu
za n im ljiva bonobo
dosadna3
* ///:-
Poglavlje 14: Podaci o tipu 469
Ovde su nas zanim ala sam o im ena m etoda, ali vi možete tražiti i druge delove potpisa
m etoda, pa čak i vrednosti određenih argum enata.
D inam ički posrednik nije alatka koju ćete upotrebljavati svakog dana, ali neke vrste
problem a njom e m ožete veoma lepo da rešite. O projektnom obrascu Proxy i drugim pro-
jektnim obrascim a više ćete saznati u knjigam a Thitiking in Patterns (posetite www.Min-
dView.net) i Design Patterns, autor je Erich G am m a i dr. (Addison-Wesley, 1995).
Vežba 21: (3) Prepravite program ProstPrim erZaPosrednik.java tako da m eri vrem ena
poziva m etoda.
Vežba 22: (3) Prepravite program ProstDinam ickiPosrednik.java tako da m eri vrem ena
poziva m etoda.
Vežba23: (3) U nutar m etode invoke( ) u program u ProstDinamickiPosrednik.java, po-
kušajte da ispišete argum ent posrednik i objasnite šta se dešava.
Projekat:2 Napišite sistem u kojem dinam ički posrednici realizuju transakcije, gde po-
srednik obavlja potvrđivanje ako je posredovani poziv bio uspešan (nije generisao izuzet-
ke), o dno sno poništavanje ako je poziv zakazao. Potvrđivanje i poništavanje se obavljaju
na spoljnoj tekstualnoj datoteci koja je izvan kontrole Javinih izuzetaka. M oraćete da pa-
zite na atomizovanost (usitnjenost) operacija.
Null objekti
Kada ugrađenu null referencu upotrebljavate da biste naznačili kako određeni objekat ne
postoji, svaki p u t kada dobijete neku referencu, m orate proveriti da li je jednaka null. To
um e da postane veom a zam orno i da proizvede zam oran kod. Problem je to što null re-
ferenca nem a svoje ponašanje, sem što proizvodi NullPointerException ako išta pokuša-
te s njom da uradite. Katkada je korisno uvesti Null objekat* koji prim a poruke za objekat
koji ,,zam enjuje“, ali vraća vrednosti koje pokazuju da nem a ,,pravog“ objekta. O nda biste
smeli pretpostaviti da su svi objekti validni pa ne biste gubili program sko vrem e prove-
ravajući reference na n u ll (i na čitanje rezultujućeg koda).
Iako bi bilo zabavno zamišljati program ski jezik koji autom atski pravi Null objekte za
vas, u praksi nem a smisla upotrebljavati ih posvuda —ponekad baš treba proveriti da li re-
ferenca ukazuje na null, katkada se m ože razborito pretpostaviti da null referenca neće
naići, dok im a i slučajeva kada je prihvatljivo otkrivanje grešaka preko izuzetka NullPo-
interException. Izgleda da su NuII objekti najkorisniji kada su „bliže podacim a“, sa ob-
jektim a koji predstavljaju entitete u prostoru problem a. Jednostavan prim er je to što
m nogi sistemi imaju klasu Osoba, a u kodu ima situacija kada stvarna osoba ne postoji
(ili postoji, ali vi još niste dobili sve podatke o njoj), pa bi se tada tradicionalno koristila
null referenca i odgovarajuća provera. Umesto toga, m ožem o napraviti Null objekat. lako
2 P ro je k ti su p re d lo z i z a s e m e s tra ln e ra d o v e (n a p r im e r ) . Solution g u id e n e s a d rž i re š e n ja p ro je k a ta .
‘ Iz u m ili su ga B o b b y W o o lf i B ru c e A n d e rs o n . To se m o ž e s m a tr a ti s p e c ija ln im s lu č a je m p ro je k tn o g
o b ra s c a Strategy (S tra te g ija ). V a rija n ta N u ll objekta je o b ra z a c N u ll iterator k o ji ite rir a n je čv o ro v a u
k o m p o z itn o j h ije ra rh iji č in i n e v id ljiv im za k lije n ta (k lije n t is tu lo g ik u m o ž e d a u p o tr e b i za ite rira n je
k o m p o z itn ih i z a v ršn ih č v o ro v a ).
470 Misliti na Javi
Null objekat odgovara na sve poruke na koje bi odgovorio i pravi objekat, i đalje je po-
treban način ispitivanja jednakosti s null. Najjednostavniji način da se to uradi je isto-
im eni interfejs:
Time bi se om ogućilo da in stan c eo f otkrije Null objekat, i što je još važnije, ne biste
m orali da dodajete m etodu is N u ll() svim svojim klasam a (što bi, na kraju krajeva, samo
predstavljalo drugačiji način prepoznavanja inform acija tokom izvršavanja - pa zašto
onda ne bism o koristili ugrađeni RTTI?).
//: podaciotipu/Osoba.java
// Klasa koja ima Null objekat.
import n e t.m in d v ie w .u til.* ;
c la s s Osoba {
p u b lic fin a l S trin g ime;
p ublic fin a l S trin g prezime;
p ublic fin a l S trin g adresa;
// e tc .
p ublic O soba(String ime, S trin g prezime, S trin g ad re sa ){
th is.im e = ime;
this.prezim e = prezime;
th is.a d re sa = adresa;
}
p ub lic S trin g to S trin g O {
return "Osoba: " + ime + " " + prezime + " " + adresa;
}
p ublic s t a t ic c lass NullOsoba
extends Osoba implements Null {
p riv a te NullOsoba() { super("Nem a", "Nema", "Nema"); }
p ublic S trin g to S trin g O { retu rn "N ullOsoba"; }
}
p ublic s t a t ic fin a l Osoba NULL = new N u llO so ba();
} ///:-
//: podaciotipu/RadnoMesto.java
c la s s RadnoMesto {
p riv a te S trin g zvanje;
p riv a te Osoba osoba;
p u b lic RadnoMesto(Stririg Fu n k cija , Osoba zaposleni) {
zvanje = Fu n k cija;
osoba = zap o sleni;
if(o so b a == n u ll)
osoba = Osoba.NULL;
}
p u b lic RadnoMesto(String Fu n k cija ) {
zvanje = Fu n k cija ;
osoba = Osoba.NULL;
}
p u b lic S trin g d ajZ van jeO { return zvanje; }
p u b lic void z a d a jZ v a n je (Strin g novoZvanje) {
zvanje = novoZvanje
}
p u b lic Osoba dajOsobu() { retu rn osoba; }
p ub lic void zadaj0sobu(0soba novaOsoba) {
osoba = novaOsoba;
if(o s o b a == n u ll)
osoba = Osoba.NULL;
}
p u b lic S trin g to S trin g O {
return "RadnoMesto: " + zvanje + " " + osoba;
}
} ///= -
Vodite računa o tom e da na nekim m estim a ipak m orate proveravati postoje li Null
objekti, što se ne razlikuje baš m nogo od provere null vrednosti. Na drugim m estim a
- kao što su to S trin g ( ) konverzije, u ovom slučaju - ne m orate da obavljate dodatne pro-
vere; sm ete da pretpostavite kako su sve reference objekata validne.
Ako um esto s konkretnim klasama radite sa interfejsim a, m ožete u potrebiti Dyna-
micProxy za autom atsko pravljenje Null objekata. Pretpostavim o da im am o interfejs Ro-
bot koji definiše ime, m odel i List<Operacija> koji opisuju šta Robot um e da radi.
O peracija sadrži opis i kom andu - to je jedna vrsta obrasca Comtnand (Kom anda):
//: podaciotipu/Robot.java
import j a v a . u t i l . * ;
import n e t.m in d v ie w .u til.* ;
Pretpostavljamo da će biti m nogo različitih tipova Robota i hteli bismo da svaki Null
objekat radi nešto posebno za svaki tip Robota - u ovom slučaju, sadrži podatke o tačnom
tipu Robota koji Null objekat zamenjuje. Te podatke će hvatati dinamički posrednik:
Poglavjje ) Podaci o tipu 475
p u b lic c la s s NullRobot {
p ub lic s t a t ic Robot
novNullRobot(Class<? extends Robot> t ip ) {
return (Robot)Proxy.newProxyInstance(
Nul1R o b o t.c la s s .g e tC la s s L o a d e r(),
new C la s s [ ]{ N u ll.c la s s , Robot.class } ,
new B lo kZaO bradu N u llR o bo ta(tip));
}
p u b lic s t a t ic void m a in (S trin g [] args) {
Robot[] botovi = {
new RobotKoji Ci s tiS n e g ("S n e z a n a "),
novNul1R o b o t(R o b o tK o jiC istiSn e g .class)
};
for(Robot bot : botovi)
R o b o t.T e s t.te s t(b o t);
}
} /* Is p is :
Robot ime: Snezana
Robot model: SnegoBot S e r ij a 11
Snezana može da č i s t i sneg
Snezana č i s t i sneg
Snezana može da č i s t i led
Snezana č i s t i led
Snezana može da p o č is ti krov
Snezana p o č is ti krov
4 76 Misliti na Javi
[Null Robot]
Robot ime: RobotKojiCistiSneg NullRobot
Robot model: RobotKojiC istiSneg NullRobot
* ///:-
Kad god vam zatreba prazan (null) objekat tipa Robot, sam o pozovete m etodu
novN ullR obot( ) prosleđujući joj tip Robota za koji hoćete posrednika. Posrednik ispu-
njava zahteve interfejsa Robot i Null i daje specifično im e tipa za koji posreduje.
p ub lic in te rfa c e A {
void f ( ) ;
} ///:-
Kada se ovaj interfejs realizuje, videćete kako se ipak može saznati stvarni realizovani tip:
c la s s B implements A {
p u b lic void f ( ) {}
p ub lic void g () {}
}
Poglavlje 14: Podaci o tipu 477
//: po da ci ot ip u/ paketnipristup/SkrivenaC.java
package podacioti pu.paketni pristup;
import po da ci ot ip u.interfejsa.*;
import static net.mi nd vi ew .u t i1 .P r i n t .*;
class C implements A {
public void f() { print("javna C . f ( ) “); }
public void g() { print("javna C .g ()"); }
void u() { print("paketni C .u ()"); }
protected void v() { print("zaštićena C .v ()“) ; }
private void w() { print("privatna C . w ( ) “ ); }
}
//: podaciotipu/SkrivenaRealizacija.java
// Zaobilaženje paketnog pristupa.
import podaciotipu.interfejsa.*;
import podaciotipu.paketnipristup.*;
import java.lang.reflect.*;
Kao što vidite, i dalje je zb o g refleksije m o g u će pozivati sve m eto d e , čak i privatne! Ako
z n ate im e m eto d e k oju h o će te d a pozovete, sam o pozovite setAccessible(true) za taj ob-
jek at tipa M ethod, kako piše u definiciji m eto d e pozivSkriveneM etode( ).
Poglavlje 14: Podaci o tipu 479
M ožda m islite da ćete to sprečiti tako što ćete d is trib u ira ti sam o već p rev ed en k o d , ali
varate se. D ov oljn o je takav k o d p ro p u s titi k ro z javap, p rev o d ilac u n a z a d k o ji se ispo-
ru ču je uz JDK. Evo kako izgleda o d g o v araju ća k o m a n d n a linija:
javap -private C
In d ik ato r -private kazuje d a treb a p rik azati sve članove, čak i o n e privatne. Evo rezultata:
N a taj n a čin , svi m o g u sazn ati im en a i p o tp ise svih vaših (i n ajp riv atn ijih ) m e to d a i
pozvati ih.
Šta će biti ako interfejs realizujete kao p riv a tn u u n u tra š n ju ldasu? Evo kako to izgleda:
//: podaciotipu/UnutrašnjaRealizacija.java
// Privatne unutrašnje klase ne mogu se sakriti od refleksije.
import podaciotipu.interfejsa.*;
import static net.mind vi ew .u ti l.Print.*;
class UnutrasnjaA {
private static class C implements A {
public void f() { print("javna C .f ()"); }
public void g() { print("javna C.g()"); }
void u() { print("paketna C .u ()"); }
protected void v() { print("zaštićena C.v()"); }
private void w() { print("privatna C.w()"); }
I
public static A napraviA() { return new C(); }
} /* Ispis:
javna C.f()
UnutrasnjaA$C
javna C.g()
paketna C.u()
zaštičena C.v()
privatna C.w()
* ///:-
//: podaciotipu/AnonimnaRealizacija.java
// Anonimne unutrašnje klase ne mogu se sakriti od refleksije.
import podaciotipu.interfejsa.*;
import static ne t. mi nd vi ew .u ti l.Print.*;
class AnonimnaA {
public static A napraviA() {
return new A() {
public void f() { print("javna C.f()"); }
public void g() { p r in t(“javna C.g()''); }
void u() { print("paketna C.u()"); }
protected void v() { print("zaštićena C.v()"); }
private void w() { print("privatna C.w()"); }
};
}
}
public class AnonimnaRealizacija {
public static void main(String[] args) throws Exception {
A a = A n on im na A. na pr av iA ();
a.f 0 ;
System.out.println ( a . ge tC la ss(),getName());
// Refleksija i dalje hvata anonimne klase:
SkrivenaRealizacija.pozivSkriveneMetode(a, "g");
SkrivenaRealizacija.pozivSkriveneMetode(a, "u");
SkrivenaReal izacija.pozi vSkri veneMetode(a, "v");
Skri venaReal izacija . p o z i vSkri veneMetode(a, "w");
}
} /* Ispis:
javna C.f()
AnonimnaA$l
javna C.g()
paketna C.u()
zaštićena C.v()
privatna C.w()
Poglavlje 14: Podaci o tipu 481
//: podaciotipu/ModifikovanjePrivatnihPolja.java
import java.lang.reflect.*;
class UzPrivatnoFinalnoPolje {
private int i = 1;
private final String s = "Potpuno sam bezbedan";
private String s2 = “Jesam li bezbedan?";
public String t o S t r i n g O {
return "i = " + i + ", " + s + ", " + s2;
}
}
Po pravilu, sva ta kršenja prava p ristu p a n isu n ajgora stvar n a svetu. U koliko neko u p o -
trebi takvu teh n ik u za pozivanje m e to d a koje ste označili kao p riv a te ili kao m eto d e s pa-
ketn im p ristu p o m (čim e ste svim a jasn o stavili d o zn an ja kako ne bi trebalo da ih pozivaju),
o n d a teško m ože da se žali k ad a izm enite neki aspekt tih m etoda. S d ru g e stran e, činjenica
da uvek im ate m ala v rata u klasu, m ože o m o g u ćiti da rešite o d ređ en e vrste p ro b lem a koji bi
inače bili teški ili nem ogući, a p red n o sti refleksije su, u o p šte n o govoreći, neosporne.
Vežba 25: (2) N apravite klasu koja sadrži p riv a tn e m eto d e , zaštićene m eto d e i m eto d e
s p a k etn im p ristu p o m . N ap išite k o d za p ristu p a n je tim m e to d a m a spolja, tj. izvan pak eta
te klase.
Sažetak
P rep o zn av an je tip a to k o m izvršavanja o m o g u ć u je o tk riv a n je in fo rm ac ija o tip u p o m o ć u
a n o n im n e reference n a klasu. P o četn ici ga često z lo u p o treb ljav aju je r im izgleda p o g o d -
nije o d poziva p o lim o rfn ih m eto d a. M n o g i lju d i, navikli n a p ro c e d u ra ln o p ro g ram ira n je ,
teško se o d v ikavaju o d o rg an izo v an ja p ro g ra m a u o b lik u sk u p a n a re d a b a sw itch . Takva
s tru k tu ra bi se m o gla p o stići o d re đ iv a n je m tip a u v re m e izvršavanja, ali b i se izgubila va-
žna o so b in a p o lim o rfizm a to k o m razvoja i o d ržav an ja koda. S u štin a o'bjektno o rijen tisa-
n o g p ro g ra m ira n ja je d a se u k o d u k o riste pozivi p o lim o rfn ih m eto d a, a da se
p rep o zn av an je tip a u v rem e izvršavanja k o risti sam o kada je n eo p h o d n o .
Da b i se p ozivi p o lim o rfn ih m e to d a ko ristili na p ro p isan i n ačin , m o ra se k o n tro lisati
definicija o sn o v n e klase, zato što bi se m o g lo desiti d a u o d re đ e n o m tre n u tk u , kada p ro -
širujete p ro g ra m , u stan o v ite k ako o sn o v n a klasa ne sadrži p o tre b n u m eto d u . Ako osnov-
na klasa p o tiče iz biblioteke ili je k o n tro liše n eko d ru g i, rešenje p ro b le m a leži u
p rep o zn av an ju tipa to k o m izvršavanja, je r tak o m o žete izvesti n o v tip i d o d a ti željenu
m eto d u . N a n ek o m d ru g o m m e stu u k o d u m o žete d a o tk rijete pravi tip i pozovete speci-
jaln u m e to d u . P olim orfizam i m o g u ć n o st p ro širiv an ja p ro g ra m a neće b iti u p ro p ašćen i,
jer se p ri d o d av a n ju novog tip a ne m o ra trag ati za n a re d b a m a sw itc h u p ro g ra m u . M e-
đ u tim , u k o d u za koji je n e o p h o d n a n o v a m eto d a, m o ra te p rim e n iti p rep o zn av an je tip a
u v rem e izvršavanja da b iste o tk rili o d re đ e n i tip.
Z bog sm eštan ja nove funkcije u o sn o v n u klasu m o g lo bi se desiti d a zarad jed n e klase
svim ostalim klasam a izvedenim iz iste o sn o v n e klase treb a d o d a ti n ek u b esm islen u m e-
to d u . T im e se gubi p reg led n o st in terfejsa i n e rv iraju se p ro g ra m eri, koji m o raju da rede-
finišu a p stra k tn e m e to d e k ad a izvode d ru g e klase iz te o sn o v n e klase. Na p rim er,
p o sm a tra jin o h ije ra rh iju klasa koja pred stav lja m u zičke in stru m e n te . P retp o sta v im o da
želite d a p ro čistite piskove svih d u v ačk ih in s tru m e n a ta u o rk estru . Jedna m o g u ćn o st je da
stavite m e to d u procistiPisak() u o sn o v n u klasu Instruinent, ali to z b u n ju je zato što p o d -
razum eva da i u d aračk i i žičani in s tru m e n ti im aju pisak. P rep o zn av an je tip a u v rem e iz-
vršavanja o bezb eđ u je m n o g o p o g o d n ije rešenje u ovom slučaju zato što m e to d u m ožete
staviti u o đ re đ e n u klasu (u o v o m slučaju, u klasu d u v ačk ih in stru m e n a ta ) gde joj je i m e-
sto. M eđ u tim , m n o g o bolje rešenje je d a se m e to d a priprem ilnstrum ent() stavi u osnov-
n u klasu. Ipak, to m o žd a n ećete p red v id eti kada se p rv i p u t sretn ete s p ro b le m o m , pa ćete
p ogrešno zaključiti d a m o ra te k o ristiti p re p o z n a v an je tip a u v rem e izvršavanja.
Poglavlje 14: Podaci o tipu 483
1 Angelika L anger piše Java Generics FAQ (O dg ov ori na najčešća pitanja o Javinim generičkiin tipovi-
m a, videti www.langer.camelot.de). Ti i d ru g i njeni tekstovi (napisani zajedno s K lausom K rettom )
bili su m i o d neprocenjive vred n o sti tokom p rip re m e ovog poglavlja.
2 Ili klasa koja im a sam o privatne k o n stru k to re.
Poglavlje 15: Generički tipovi 485
Poređenje sa C++-om
P ro je k ta n ti Jave kažu d a je d o b a r d eo tog jezika b io reakcija n a C + + . U p rk os to m e , Javu
je m o g u ć e p re d av ati u g la v n o m b ez sp o m in ja n ja C + + -a , i ja sam se tru d io d a ra d im tako,
sem k ad a p o re đ e n je daje d u b lji uvid.
G en eričk i tip o v i z ah te v aju više p o re đ en ja s C + + -o m iz dva razloga. P rvo, kad a shvatite
o d re đ e n e asp ek te C + + -o v ih šablona (engl. tem plateš) - ko ji su bili glavno n a d a h n u ć e za
generičke tip o ve, čak i za n jih o v u o sn o v n u sin ta k su - b o lje ćete shvatiti tem elje to g k o n -
cepta, k ao i - a to je v e o m a v ažn o - o g ra n ič en ja p rilik o m u p o tre b e Javinih g en eričk ih
tip o v a i razloge za njih . K rajnji cilj je d a stek n ete ja sn u p red stav u o to m e gde su granice,
p o što z n a m iz so p stv en o g iskustva: k ad a shv atite gde su granice, p o stajete bolji p ro g ra -
m er. K ada zn ate šta n e m o ž e d a se u ra d i, bo lje ćete isk o ristiti o n o što m ože (d elo m i zato
što ne g u b ite v re m e su d a ra ju ć i se sa z ido vim a).
D ru g i razlog je značajan n e sp o ra z u m koji u n u ta r Javine zajednice vlada kad a je reč o
C + + ša b lo n im a . Taj n e sp o ra z u m m o že jo š više d a vas zb u n i u p o g led u p re d v iđ en e u p o -
treb e g e n eričk ih tipova.
Stoga ću u ovo m po glavlju d a ti tek m in im a la n broj p rim e ra s C + + šab lo nim a.
//: ge ne ri cki/Skladistel.java
class Automobil (}
Pre Jave SE5, je d n o sta v n o b ism o joj dali d a čuva jed a n Object:
//: genericki/Skladiste2.java
Sada Skladiste2 m ože da p rim i b ilo šta - a u ovom p rim e ru isto Skladiste2 čuva tri
različita tip a objekata.
Im a slučajeva kada hoćete da k o n tejn er sldadišti više tipova objekata, ali se u k o n tejn er
o b ičn o stavlja je d a n tip objekata. Jedna o d o sn o v n ih m otivacija za generičke tipove bilo je
zadavanje tipa objekta koji k o n tejner sadrži, i da tu specifikaciju pod rži i proveri prevodilac.
Z ato b ism o u m esto tip a Object hteli d a u p o tre b im o nespecificiran tip, koji m ože biti
o d re đ e n k asnije. To se rad i tako što n ak on im ena klase u n u ta r znakova m anje od i veće o d
n ap išete p a ra m eta r tipa, i zam e n ite ga stv arn im tip o m kada tu klasu u p o tre b ite. Za klasu
Skladiste to bi izgledalo ovako (T je p a ra m e ta r tipa):
//: genericki/Skladiste3.java
K ada sada n aprav ite neko Skladiste3, istom sintak so m sa znakov im a m an je od i veće od
m o rate zadati tip koji ho ćete da čuvate u n jem u , kao što vidite u m eto di m a in ( ).
U Skladiste je dozvoljeno stavljati sam o objekte tog tip a (ili po d tip a, pošto p rin cip zam ene
Poglavlje 15: Generički tipovi 487
Biblioteka n-torki
Ć esto bi b ilo p o željno v ra titi više objekata iz je d n o g poziva m etod e. N ared b a retu rn do z-
voljava zadavanje sam o je d n o g objekta, p a je rešenje n a p ra v iti o bjekat koji sad rži više o b -
jek a ta koje h o ćete da v ratite. N arav n o , m o žete da pišete p o seb n u k lasu svaki p u t kada
n aiđ e te na tak v u situaciju , ali p o m o ć u g en eričk ih tipova p ro b le m m o žete rešiti je d n o m
zauvek i tak o sebi ušted eti tru d u b u d u će. Isto v rem en o , u vrem e p rev o đ en ja svi generički
tipo vi će biti provereni.
O vaj k o n c e p t se naziva n-torka (engl. tuple), a rad i se o g ru p i objek ata o m o ta n ih za-
je d n o u je d a n objekat. P rim alac objekta m ože d a čita njegove elem en te, ali ne i d a stavlja
nove u njega. (O vaj k o n cep t nazivaju i D ata Transfer Object ili Messenger, O b jek at za p re-
nos p o d a ta k a ili D ostavljač p o ru k a.)
n -to rk e m o g u biti p roizvoljne du žin e i svaki n jih o v o b jek at m o že b iti različitog tipa.
M ed u tim , m i h o ć em o da z ad am o tip svakog o bjek ta i o bezb ed im o da p rim a la c - kada
p ro č ita njegovu v re d n o st-d o b ije ispravan tip. P rob lem zbog različitih d u žin a rešićem o
prav ljen jem različitih n -to rk i. Evo jed n e koja čuva dva objekta:
K o n stru k to r hvata ob jek at koji treba sačuvati, a to S trin g ( ) je p rig o d n a funkcija za p ri-
kazivanje v red n o sti iz liste. O b ra tite p a žn ju na to da n -to rk a svoje elem ente im p lic itn o
čuva u p o re tk u .
488 Misliti na Javi
//: net/mindview/util/Trojka.java
package net.mindview.util;
//: ne t/ mi nd vi ew /u ti1/Cetvorka.java
package net.mindview.util;
//: net/mindview/util/Petorka.java
package net.mindview.util;
//: ge ne ri cki/IspitivanjeEntorki.java
import net.mindview.util.*;
class Amfibija {}
class Vozilo {}
Klasa steka
P ogledajm o nešto što je m alo kom pliko vanije: klasični stek s kojeg se p rv o u zim a o n o što
je n a njega p o sled n je stavljeno. U p o glavlju Č uvatije objekata realizovali sm o stek p o m o ć u
u lan ča n e liste (LinkedList) kao klasu net.m indview.util.Stack (n a stra n a m a 320-323). U
to m p rim e ru v id ite d a u lan ča n a lista već im a m e to d e p o tre b n e za prav ljen je steka. Klasu
Stack n ap ravili sm o slaganjem je d n e gen eričk e klase (Stack<T>) s d ru g o m g eneričkom
klasom (LinkedList<T>). N a to m p rim e ru vid ite da je generički tip kao i svaki d ru g i, (uz
nekoliko izuzetaka koje ćem o ra z m o triti k asnije).
U m esto d a u p o tre b im o LinkedList, m o že m o d a realizu jem o so p stv en i u n u tra šn ji
u lan ča n i m eh a n iz a m za skladištenje.
public T skinisa() {
T rezultat = vrh.stavka;
if(!vrh.kraj())
vrh = vrh.sledeca;
return rezultat;
}
public static void main(String[] args) {
Ul ancaniStek<String> uss = new U l a n c a ni St ek <S tr in g>( );
for(String s : "Phasers on stun!".split(" "))
u s s . st av in a( s);
String s;
while((s = uss.skinisa()) != null)
System.o ut .p ri nt ln (s);
}
} /* Ispis:
stun!
on
Phasers
* ///:-
NasumicnaLista
Kao d ru g i p rim e r za skladiste, p re tp o sta v im o da h o ćete p o se b n u v rstu liste koja n asu m ič-
no bira je d a n o d svojih elem en ata k ad a se pozove iz a b e ri( ). Pošto hoćete alatk u koja radi
sa svim o b jek tim a, k o ristite g eneričke tipove:
//: ge ne ricki/NasumicnaLista.java
import j a v a . u t i l .*;
Vežba 6: (1 ) U p o tre b ite k lasu NasumicnaLista s dva tip a više n ego što je p o k az an o u m e-
to d i m a in ().
Generički interfejsi
G enerički tip o v i su p rik la d n i i za interfejse. N a p rim er, generator je klasa koja p rav i o b -
jekte. Z apravo, ra d i se o specijalizaciji p ro je k tn o g o b rasca Factory M eth o d (P roizvo dn a
m e to d a ), ali k a d a o d g e n e ra to ra zatraž ite n o v objekat, n e p ro sleđ u jete m u arg u m en te,
d o k ih P ro izv o d n a m e to d a o b ič n o p ro sleđ u jete. G e n e ra to r zna kako da pravi nove o bjek-
te b ez ikakvih d o d a tn ih in fo rm ac ija.
G e n e ra to r o b ič n o definiše sam o je d n u m e to d u , o n u koja prav i nove objekte. O vde
ćem o je nazvati sledeci( ) i u k lju čiti m e đ u sta n d a rd n e uslužn e klase:
//: n e t/ mi nd vi ew /u ti1/Generator.java
// Generički interfejs.
package net.mindview.util;
public interface Genera to r< T> { T s l e d e c i (); } ///:-
P o v ratn i tip m e to d e sledeci() p ara m e triz o v a n je u T. Kao što v id ite, generički tip ov i se
u in terfe jsim a u p o treb lja v a ju kao i u klasam a.
R ealizaciju interfejsa G e n e ra to r p o k azaćem o na klasam a u h ijerarh iji kafe:
//: ge ne ri ck i/kafa/Kafa.java
package g e n e r i c k i .kafa;
//: ge ne ri ck i/ ka fa /S Ml eko m. ja va
package genericki.kafa;
public class SMlekom extends Kafa {} ///:-
Poglavlje 15: Generički tipovi 493
//: ge nericki/kafa/Moka.java
package genericki.kafa;
public class Moka extends Kafa {} ///:-
//: genericki/kafa/Cappuccino.java
package genericki.kafa;
public class Cappuccino extends Kafa {} ///:-
//: genericki/kafa/Kratka.java
package genericki.kafa;
public class Kratka extends Kafa {} ///:-
Sađa m o ž em o da realizu jem o G e n e ra to r< K a fa > koji p rav i različite tipove K afa ob je-
kata:
//: genericki/kafa/GeneratorKafe.java
// Generiše različite tipove Kafa:
package g e n e r i c k i .kafa;
import ja v a . u t i 1.*;
import net.mindview.util.*;
//: g e n e r i c k i/ Fi bo na cc i.java
// Pravljenje Fibonaccijevog niza.
import net.mindview.util.*;
} /* Ispis:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
* ///:-
U fo reach n ared b i, klasu Ite ra b iln iF ib o n a c c i upotreb ljav ate tako što n je n o m kon
s tru k to ru zadajete g ran ic u , da bi m eto d a h a s N e x t( ) znala kada treb a da v ra ti false.
496 Misliti na Javi
Generičke metode
D o sad sm o p a ram etrizo v ali cele klase. M ožete p ara m e triz o v a ti i p o je d in e m e to d e u n u ta r
klase. S am a klasa m ože, ali n e m o ra b iti gen eričk a - to ne zavisi o d g en e rič n o sti m eto d e.
G en eričn o st m eto d e znači da o n a m o že da se m en ja n ezav isn o o d klase. Po p rav ilu , ge-
neričke m e to d e bi treb alo d a u p o treb ljav ate ,,kad g o d m o ž e te “. D ru g im rečim a, ako se
u m e sto cele klase sam o m eto d a m o že n a p ra v iti d a b u d e g en eričk a, v e ro v a tn o će tak o i
k o d b iti jasniji. Pored toga, ako je m e to d a statičn a, o n a n e m a p ristu p a g e n eričk im p a ra -
m e trim a tip a (svoje generičke) klase, p a u koliko tre b a d a k o risti g en e rič n o st, m o ra sam a
da b u d e generička.
G enerička m e to d a se definiše stavljanjem liste g en eričk ih p a ra m e ta ra isp red p o v ra tn e
v red n o sti, ovako:
//: genericki/GenerickeMetode.java
//: net/mindview/util/Nova.java
// Uslužne metode koje pravljenje generičkih kontejnera
// pojednostavljuju tako što same zaključuju o tipu argumenta.
package net.mindview.util;
import j a v a . u t i l .*;
//: ge ne ricki/JednostavnijiLjubimci.java
import podaciotipu.ljubimci.*;
import ja v a . u t i l .*;
import net.mindview.util.*;
M ada je ovo zanim ljiv p rim e r zaključivanja o tip u arg u m e n ta , teško je reći koliko je to
zapravo korisno. O soba koja čita ko d m o ra da analizira i shvati o v u d o d a tn u b ib lio tek u i
njen e posledice, pa je m o žd a jed n ak o p ro d u k tiv n o ostaviti p rv o b itn u d efin iciju (m ad a
im a m n o g o ponav ljanja) - d a ironija b u d e veća, jed n o sta v n o sti rad i. M eđ u tim , k ad a bi u
s ta n d a rd n u Javinu b ib lio tek u bila d o d ata u slu žn a klasa p o p u t g o rn je N o v a.jav a, bilo bi
p a m e tn o koristiti je.
Z aključivanje o tip u arg u m e n ta fun k cio niše isključivo za d odeljivanje. U koliko rezul-
tat poziva m e to d e kao što je N o v a .m a p () prosled ite kao a rg u m e n t d ru g o j m eto d i, prevo-
dilac nećep o k u šati da zaključi koji je tip a rg u m e n ta. Poziv te m e to d e o n će tre tira ti kao da
je n jena p o v ra tn a v red n o st dod eljen a p ro m en ljiv o j tip a O b je c t. Evo je d n o g takvog
(n eu spešno g ) p rim era :
//: genericki/GraniceZakljucivanja.java
import po da ci otipu.ljubimci.*;
import ja v a . u t i l .*;
public class GraniceZakljucivanja {
Poglavlje 15: Generički tipovi 499
static voiđ
f(Map<Osoba, List<? extends L j u b i m c i » 1judiSLjubimcima) {}
public static void main(String[] args) {
// f ( N o v a . m a p O ) ; // Neće biti prevedeno
}
} ///:-
Vežba 11: ( 1) Ispitajte klasu Nova.java p rav ljen jem so p stv en ih klasa. D o kažite d a Nova s
njim a rad i ispravno.
//: genericki/IzricitoZadavanjeTipa.java
import po da ci ot ip u. lj ub im ci.*;
import ja v a . u t i l .*;
import net.mindview.util.*;
N aravno, tim e se gubi p re đ n o st korišćenja klase Nova koja sm an ju je k o ličin u pisan ja,
ali je d o d a tn a sintaksa p o tre b n a sam o kada ne pišete n are d b e dodeljivan ja.
V ežba 12: (1) P onovite p re th o d n u vežbu uz eksp licitn o zadavanje tipova.
//: genericki/Generatori.java
// Uslužna metoda za korišćenje s Generatorima.
import genericki.kafa.*;
import java.util.*;
import net.mindview.util.*;
Am ericano 2
Moka 3
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
* ///:-
//: ne t/ mi nd vi ew/util/E1ementarniGenerator.java
// Automatski pravi Ge nerator date klase
// koja ima podrazumevani konstruktor (bez argumenata).
package net.mindview.util;
O va klasa p red stavlja e le m e n ta rn u realizaciju koja pravi objekte klase koja je (1) javna
(p o što je E lem entarniG enerator u zaseb n o m p ak etu , klasa o kojoj se rad i m o ra im ati jav-
ni, a ne sa m o p ak e tn i p ristu p ), i (2) im a p o d ra zu m e v an i k o n s tru k to r (onaj koji ne p rim a
a rg u m e n te ). Da biste na p rav ili jed an o d o b jek ata tip a ElementarniGenerator, pozivate
m e to d u n a p ra v i( ) i p ro sleđ u jete joj leksem u (engl. token) tip a koji h o ćete d a generišete.
G enerička m e to d a n a p ra v i( ) o m o g u ću je d a kažete Elem entarniG enerator.napra-
vi(MojTip.cIass) u m esto nevv ElementarniGenerator<M ojTip>(M ojTip.cIass), što bi
bilo ružnije.
Na p rim e r, ova je d n o sta v n a klasa im a p o d raz u m e v an i k o n stru k to r:
502 Misliti na Javi
//: genericki/PrebrojaniObjekat.java
//: ge nericki/PrimerElementarnogGeneratora.java
import net.mindview.util.*;
Iz ovoga v idite koliko generička m eto d a sm an ju je količinu pisanja kada se pravi objekat
Generator. Javin generički m eh an izam vas p rim o ra v a da ipak pro sled ite objekat tipa Class,
pa ga o n d a m ožete u p o treb iti i za zaključivanje o tip u arg u m en ta u m eto d i napravi( ).
Vežba 14: ( 1) Izm en ite PrimerElem entarnogGeneratora.java tako da se Generator pravi
eksplicitno (tj. u m esto generičke m eto d e n a p ra v i( ) u p o tre b ite eksplicitan k o n stru k to r).
//: net/mindview/util/N_torka.java
// Biblioteka n-torki u kojoj se upotrebljava
// zaključivanje o tipu argumenta.
package net.mindview.util;
Poglavlje 15: Generičkl tipovi 503
//; genericki/IspitivanjeEntorki2.java
import net.mindview.util.*;
import static n e t. mi nd vi ew .u ti 1.N_torka.*;
//: genericki/vodeneboje/Vodeneboje.java
package genericki.vodeneboje;
/ / : genericki/SkupoviVodenihBoja.java
import genericki.vodeneboje.*;
import j a v a . u t i l .*;
import static net.mind vi ew .u ti l.Print.*;
import static net.mind vi ew .u ti l.Sku po vi.*;
import static genericki.vodeneboje.Vodeneboje.*;
/ / : net/mindview/util/RazlikeKontejnerskihMetoda.java
package net.mindview.util;
import java.lang.reflect.*;
import j a v a . u t i l .*;
class Klijent {
private static long brojac = 1;
private final long id = brojac++;
private Klijent() {}
public String t o S t r i n g O { return "Klijent " + id; }
// Metoda za pravljenje objekata tipa Generator:
public static Ge ne ra to r< Kl ij en t> generator() {
return new Generator<Klijent>() {
public Klijent sledeci() { return new Klijent(); }
};
}
}
class Sluzbenik {
private static long brojac = 1;
private final long id = brojac++;
private Sluzbenik() {}
public String t o S t r i n g O { return "Sluzbenik " + id; }
// Samo jedan objekat tipa Generator:
public static Ge ne ra to r< Sl uz be ni k> ge nerator =
new Generator<Sluzbenik>() {
public Sluzbenik sledeci() { return new Sluzbenik(); }
};
}
) /* Ispis:
Sluzbenik 3 uslužuje kl ijenta K1ijent 1
Sluzbenik 2 uslužuje kl ijenta K1 ijent 2
Sluzbenik 3 uslužuje kl ijenta K1 ijent 3
Sluzbenik 1 uslužuje kl ijenta K1 ijent 4
Sluzbenik 1 usl užuje kl ijenta K1 ijent 5
Sluzbenik 3 uslužuje kl ijenta K1 ijent 6
Sluzbenik 1 uslužuje kl ijenta K1 ijent 7
Sluzbenik 2 uslužuje kl ijenta K1ijent 8
S1uzbeni k 3 uslužuje kl i jenta K1 ijent 9
Sluzbenik 3 uslužuje kl ijenta K1ijent 10
Sluzbenik 2 uslužuje kl ijenta Klijent 11
Sluzbenik 4 uslužuje kl ijenta K1 ijent 12
Sluzbenik 2 uslužuje kl ijenta K1ijent 13
Sluzbenik 1 uslužuje kl ijenta K1 ijent 14
Sluzbenik 1 uslužuje klijenta Klijent 15
*///:-
//: ge ne ri ck i / L i s t a N _ t o r k i .java
// Kombinovanje generičkih tipova da bi se dobili
// složeni generički tipovi.
import j a v a . u t i l .*;
import net.mindview.util.*;
for(Cetvorka<Vozilo,Amfibija,String,Integer> i: tl)
Sy st em .o ut .p ri nt ln (i);
}
} /* Ispis: (75% podudaranja)
(Vozilo@llb86e7, Amfibija@35ce36, zdravo, 47)
(Vozilo@757aef, Amfibija@d9f9c3, zdravo, 47)
* ///:-
//: genericki/Prodavnica.java
// Pravljenje složenog modela pomoću generičkih kontejnera.
import ja v a . u t i l .*;
import net.mindview.util.*;
class Proizvod {
private final int id;
private String opis;
private double cena;
public Proizvod(int IDbroj, String ops, double cena){
id = IDbroj;
opis = ops;
this.cena = cena;
S y st em .o ut .println(toString());
}
public String t o S t r i n g O {
return id + " + opis + ", cena: $" + cena;
}
public void promenaCene(double change) {
cena += change;
}
public static Generator<Proizvod> ge nerator =
new Generator<Proizvod>() {
private Random slucajan = new Random(47);
public Proizvod sledeci() {
return new Pr oi zv od (s l u c a j a n , n e x t I n t (1000), "Test",
Math.round(slucajan.nextDouble() * 1000.0) + 0.99);
}
};
}
class Blagajna {}
class Kancelarija {}
* ///:-
Tajanstveno brisanje
K ako b u d e te sve tem eljnije u p o zn av ali generičke tipove, nailazićete na stvari koje v am
sp očetk a neće biti logične. N a p rim er, iako se m ože reći ArrayList.class, n e m o že se reći
ArrayList<Integer>.class. I p o g led ajte ovo:
//: genericki/EkvivalentnostTipovaZbogBrisanja.java
import j a v a . u t i l .*;
//: ge ne ricki/Izgubljenelnformacije.java
import j a v a . u t i l .*;
class Frob {}
class Fnorkle {}
class Kvark<Q> {}
class Ce st ica<P0L0ZAJ,M0MENAT> {}
} /* Ispis:
[E]
[K, V]
[Q]
[POL OZ AJ, MOMENAT]
* ///:-
Pristup u C++-u
P o gledajm o p rim e r iz C + + -a u kojem su u p o tre b lje n i šabloni (engl. tem plates). V idećete
da je sin tak sa za p a ra m e trizo v a n e tip o v e p o tp u n o jed n ak a, p o što je Java n ap ra v ljen a na
o sn o v u C + + -a :
//: g e n e ri ck i/ Sa bl on i.cpp
#include <iostream>
using namespace std;
class ImaF {
publi c :
void f() { cout « "ImaF::f()" « endl; }
514 Misliti na Javi
int main() (
ImaF imaf;
Manipulator<ImaF> m a ni pu la to r( im af );
ma ni pu la to r. ma ni pu lis i();
} /* Ispis:
ImaF: :f ()
III--
K lasa M anipulator sadrži o b jek at tip a T. Z an im ljiva je m e to d a m an ip u lisi( ) koja p o -
ziva m e to d u f ( ) za obj. Kako o n a m ože zn ati d a m eto d a f ( ) p o sto ji za p a ra m e ta r tip a T?
P revodilac C + + -a p roverava tipove k ad a p rav ite p rim e ra k šab lo n a, p a u tre n u tk u p ra -
vljenja klase M anipulator<Im aF> vidi d a ImaF im a m e to d u f ( ). D a je n em a, do b ili biste
g rešk u u v rem e p rev ođ en ja, i b e z b ed n o st tip o v a bi bila očuvana.
Pisan je ovakvog k o d a u C + + -U je jed n o stav n o , zato što k o d ša b lo n a zn a tip p a ra m e ta -
ra svog šab lo n a u tre n u tk u k ad a treb a d a ga n ap ravi. Javini generički tip o v i su drugačiji.
N apisali sm o ImaF u Javi:
//: genericki/ImaF.java
//: ge nericki/Manipulacija.java
// {Compi1eTimeError} (Ne može da se prevede)
class Manipulator<T> {
private T obj;
public Manipulator(T x) { obj = x; }
// Greška: cannot find s y m b o l : method f():
public void mani pul i si () { obj.f(); }
}
//: genericki/Manipulator2.java
//: genericki/Manipulator3.java
class Manipulator3 {
private ImaF obj;
public Ma ni pu la to r3 (I ma F x) { obj = x; }
public void manipulisi() { obj.f(); }
} ///:-
/ / : genericki/VracanjeGenerickogTipa.java
Migracijska kompatibilnost
D a b ism o sprečili m og u će z a b u n e u vezi s b risa n je m , m o ra te n ed vo sm isleno shvatiti da
b risan je n/j'eobeležje jezika. R adi se o k o m p ro m isu u realizaciji Javinih g eneričkih tipova,
p o tre b n o m zato što je jezik n a p o č e tk u b io nap rav ljen bez n jih . Taj k o m p ro m is će vam
sm etati, p a se m o ra te navići n a njega i sh vatiti zašto je u čin jen.
D a su generički tip o v i b ili d eo Jave o d verzije 1.0, n e b i bili realizovani p o m o ć u b ri-
sanja - koristila b i se konkretizacija za p a m ć e n je p a ra m e ta ra tip a k ao p rv o ra z re d n ih en -
titeta, p a biste s n jim a m o g li d a o b avljate jezičke i refleksivne o p eracije zasno van e na
tip o v im a. V idećete u n astav k u poglavlja d a b risan je sm a n ju je ,,op štost“ generičk ih tipova.
O n i u Javi jesu korisni, ali n e toliko koliko bi m o gli b iti, a razlog je u p rav o brisanje.
U realizaciji zasnovanoj n a b risan ju , g en erički tip o v i se tre tira ju kao d ru g o ra z re d n i
tip o v i koji se n e m o g u u p o tre b lja v ati u n ek im v až n im k o n tek stim a . G en erički tip ov i
p o stoje sam o to k o m statičke pro v ere tip o v a. N ak o n toga, svi generički tip ov i u p ro g ra m u
bivaju o b risa n i i zam enjen i n e k o m n eg en e ričk o m g o rn jo m g ra n ico m . N a p rim er, a n o ta -
cije tip a k ao što je L ist< T > b iv aju b risa n je m sved en e n a L ist, a p ro m en ljiv e o b ičn o g tipa
svedene na O b je c t, ukoliko neka g ran ica n ije zadata.
O sn o v n a m o tivacija za b risa n je jeste to što o m o g u ć u je ko rišćenje g eneričk ih klijenata
s n eg en eričk im b ib lio tek am a i o b rn u to . To se često naziva migracijska kom patibilnost. U
id e a ln o m svetu, sav kod b i u isto m tre n u tk u p o stao generički. U stv arn o sti, čak i da p ro -
g ra m e ri p išu sam o generički k o d , m o rali bi da vode ra č u n a o n eg en eričk im b ib lio te k a m a
n a p isa n im pre Jave SE5. A u to ri tih b ib lio tek a m o žd a n ik ad a neće p rep rav iti svoj k o d tako
da p o sta n e generički, ili će to m o ž d a u ra d iti polako.
Z ato Javina realizacija g en eričk ih tip o v a m o ra da p o d ržav a n e sam o vertikalnu kom pa-
tibilnost - postojeći ko d i d ato tek e klasa m o ra ju o stati legalne i značiti isto što su značile
pre - nego i m igracijsku k o m p a tib iln o st, d a bi b ib lio tek e m ogle p o stati generičke kada to
n jim a b u d e odgovaralo, i d a ta d a n e bi sru šile k o d i aplikacije koje o d njih zavise. Kada su
to sebi zadali k ao cilj, p ro je k ta n ti Jave i raz n e g ru p e koje su rad ile na to m p ro b le m u o d -
lučili su d a je b risan je je d in o ostvarivo rešenje. B risanje o m o g u ć u je m igraciju ka generič-
kim tip o v im a tako što dozvoljava p o sto ja n je n eg en eričko g k o da zaje dn o s generičkim .
Na p rim e r, recim o da o d re đ e n a aplikacija koristi dve biblioteke, X i Y, i da Y koristi bi-
b lio te k u Z. Pošto je u m e đ u v re m e n u o bjavljena Java SE5, tvorci te aplikacije i tih biblio -
teka vero vatn o će, je d n o g d an a , o d lu čiti da p re đ u n a generički kod. M eđ u tim , svaki od
njih će im ati d ru g ačiju m o tiv aciju i o g ra n ičen ja u p o g led u v re m e n a tog prelaska. D a bi se
postigla m igracijska k o m p a tib iln o st, svaka b iblio teka i ap likacija m o ra biti nezavisna od
svih o stalih u o d n o su na to d a li k o risti g en eričk i kod. Stoga n e sm eju im ati m o g u ćn o st da
o tk riju da li d ru g e biblioteke koriste ili n e k o riste generički kod. Z ato do k az da o d ređ en a
bib lio tek a k o risti generički k o d m o ra biti „obrisan".
Bez neke vrste p u tanje za m igraciju, sve biblioteke n apisan e tok om vrem ena bile su u
o p asn o sti da b u d u odsečene o d p ro g ram era koji su odlučili da p red u na Javin generički
kod. T vrdi se d a su biblioteke deo jezika koji najviše u tiče na p ro d u k tiv n o st, pa toliki rizik
nije bio prihvatljiv. Da li je brisan je bilo jed in i ili n ajbolji p u t za m igraciju, pokazaće vrem e.
Poglavlje 15: Generički tipovi 517
Problem s brisanjem
D akle, o sn o v n i razlog za b risa n je jeste prelazak s n eg en eričko g n a generički k o d i n a m e ra
da se gen erički k o d u ključi u jezik b ez ru še n ja p o sto ječih b ib liotek a. B risanje o m o g u ću je
p o sto jećem n eg en eričk o m k lijen tsk o m k o d u d a n astav i d a rad i bez ikakve izm en e, sve
d o k klijen ti ne b u d u sp re m n i d a p re ra d e k o d n a generički. Ta m otiv acija je p lem en ita,
zato što ne ruši o d je d n o m sav p o sto jeći kod.
C ena b risan ja je zn a tn a . G en e ričk i tip o v i se n e m o g u u p o treb ljav ati u o p eracijam a
koje u v rem e izvršavanja izričito u p u ć u ju n a tipove, k ao što su eksplicitne konverzije
tipova, o p eracije instanceof i izrazi new. P ošto se g u b e sve inform acije o tip u p a ra m e ta ra ,
kad a pišete generički k o d m o ra te se staln o p o d sećati d a sam o izgleda kao da im ate in fo r-
m acije o tip u p a ra m e ta ra . D akle, k ad a pišete ovakvo p arče koda:
class Nesto<T> {
T promenljiva;
1
čini se d a k od u klasi Nesto treb a da zn a kako sada radi sa o b jek to m tip a Macka.
Sintaksa vas jak o navodi na p o m isa o d a tip T biva zam e n jen u celoj klasi. Ali to nije istina,
i kad god pišete ko d za tu klasu, m o rate sebi reći: ,,Ne, to je sam o objekat“.
Pored toga, b risa n je i m ig racijsk a k o m p a tib iln o st znače d a se generički kod ne koristi
ni ta m o gde biste to m o žd a želeli:
/ / : ge nericki/BrisanjelNasledjivanje.java
class GenerickaOsnovna<T> {
private T element;
public void set(T arg) { arg = element; }
public T get() { return element; }
}
Object obj = d 2 . g e t ( ) ;
d2.set(obj); // Ovde biste dobili upozorenje!
}
} ///= -
Izvedena2 nasleđuje k lasu GenerickaOsnovna b ez gen eričk ih p a ra m e ta ra i prev o d i-
lac ne daje u p o zo renje. U p o zo ren ja n e m a d o poziva m eto d e s e t( ).
Za isključivanje u p o zo re n ja Java im a an o tac iju , o n u ko ju v id ite u listin g u (ta an otac ija
nije bila p o d rž a n a u ran ijim v erzijam a Jave SE5):
@SuppressWarnings("unchecked")
/ / : genericki/TvoracNizova.java
import j a v a .1a n g .r e f l e c t .*;
import j a v a . u t i l .*;
Iako je klasa vrsta n a p isa n a kao Class<T>, b risan je znači d a če o n a b iti usklad išten a
sam o kao Class, b ez p a ra m e tra . Z ato, k ad a s n jo m n ešto n ap rav ite, recim o n ek i niz, m e-
to d a A rray.new lnstance( ) ne d o b ija in fo rm ac ije o tip u koje vrsta im p licira; zbog to g a ta
m e to d a n e m ože d ati rezu ltat specifičnog p o d tip a , te ga m o ra te e k sp licitn o k o nvertovati,
što proizvo di u p o zo ren je koje n e m o žete d a o tk lo n ite.
Im ajte u v idu d a je m e to d a A rray.new lnstance( ) p re p o ru č e n a za p ravljenje nizova u
g en eričk o m kodu.
U koliko u m esto n iza n a p ra v im o k o n tejn er, stv ari izgledaju d rugačije:
//: genericki/TvoracListi.java
import j a v a . u t i l .*;
//: genericki/TvoracPopunjeneListe.java
import j a va .u ti1 .*;
u sk la đ e n o sa A rra y L is t< T > . D akle, iako b risan je u k lan ja in fo rm a c ije o stv a rn o m tip u
u n u ta r m e to d e ili klase, p rev o d ilac ip a k m ože da o b ezb ed i u n u tra š n ju d o sle d n o st u
n a č in u n a koji je taj tip u p o tre b lje n u n u ta r m eto de, o d n o s n o klase.
P ošto b risan je u k lan ja in fo rm ac ije o tip u iz tela m eto d e , u v re m e izvršavanja važne su
granice. tačke gd e o b jek ti ulaze u m e to d u i izlaze iz nje. U tim ta č k a m a p rev o d ilac u v rem e
p rev o đ e n ja p ro v erav a tip o v e i u m eč e k o d za njihove konverzije. P ogledajm o sledeći n e-
g enerički p rim e r:
//: ge ne ri ck i/ Je dnostavnoSkladiste.java
//: genericki/GenerickoSkladiste.java
Kompenzacija za brisanje
Kao što sm o videli, b risa n jem se g u b i m o g u ć n o s t ob avljanja o d re đ e n ih operacija u gene-
ričk o m k odu . Sve o n o za šta je p o tre b n o p o zn av an je stv a rn ih tip o v a u v rem e izvršavanja,
neće raditi:
//: genericki/Obrisana.java
// {CompileTimeError} (Neće biti prevedena)
//: genericki/HvatanjeTipaKlase.java
class Zgrada {}
class Kuca extends Zgrada {}
/ / : genericki/PravljenjelnstanceGenerickogTipa.cpp
// C++, ne Java!
int main() {
Nesto<Bar> nb;
Nesto<int> ni; // ... radi i s prostim tipovima
} III--
524 Misliti na Javi
//: genericki/PravljenjelnstanceGenerickogTipa.java
import static net.mind vi ew .u ti l.Print.*;
class K1asaKaoProizvodjac<T> {
T x;
public KlasaKaoProizvodjac(Class<T> vrsta) {
try {
x = vrsta.ne wl ns ta nc e( );
} catch(Exception e) {
throw new RuntimeE xc ep ti on (e );
}
}
}
class Zaposleni {}
//: genericki/OgranicenjeProizvodjaca.java
interface ProizvodjacI<T> {
T n a p r a v i ();
}
class Nesto2<T> {
private T x;
Poglavlje 15: Generički tipovi 525
class Spravica {
public static class Proizvodjac implements ProizvodjacI<Spravica> {
public Spravica napravi() {
return new Spravica();
}
}
}
class X {}
Vežba 22: (6) U p o treb ite o zn ak u tip a i refleksiju za pravljen je m e to d e koja k o risti verziju
sa a rg u m e n to m m e to d e new lnstance( ) koja tre b a da p rav i o b jek te klase čiji k o n stru k to r
p rim a arg u m en te.
Vežba 23: (1) P rep rav ite p ro g ram OgranicenjeProizvodjaca.java tako d a m e to d a
n a p ra v i( ) p rim a jed an arg u m en t.
Vežba 24: (3) Izm en ite vežbu 21 tako da se p ro iz v o d n i ob jek ti d rže u Mapi u m esto u
Class<?>.
//: genericki/ListaGenerickih.java
import j a v a . u t i l .*;
O vako ste dob ili p o n ašan je niza, ali i p ro v eru tipova u v rem e p re v o đ en ja koju do n o si
generički kod.
K atkada će v am ipak zatreb ati niz g eneričkih tipova (na p rim e r, A rra y L ist u p o treb lja-
va nizove in te rn o ). Da stv ar b u d e zanim ljivija, m ožete definisati referencu tak o da prevo-
dilac b u d e zadovoljan. Na p rim er:
//: genericki/NizGenerickihReferenci.java
class Generic<T> {}
Prevodilac će to p rih v a titi bez ikakvog u p o zo ren ja . Ali p o što nećete m o ć i da n ap ra v ite
niz ta č n o tog tip a (u k lju ču ju ći tu i p a ra m e tre tip a ), stv ari su p o m a lo zb u n ju ju će. P ošto svi
n izovi im a ju istu stru k tu r u (veličinu svakog e le m e n ta i ra sp o red elem en ata) bez o b zira na
tip koji sadrže, izgleda kao da bi se m o g ao n a p ra v iti niz tip a Object i ko n v erto v ati u niz
željenog tipa. To će se d o d u še prevesti, ali neće m o ći da se izvršava, je r pro izv o d i Class-
CastException:
P rob lem je u to m e što nizovi p am te svoj stv arn i tip, a o n biva u tv rđ e n u tre n u tk u p ra -
vljenja niza. D akle, iako je gia k onvertovana u Generic<Integer>[], ta in fo rm ac ija p o sto -
ji sam o u vrem e p rev o đ en ja (a da n em a an o tac ije @ SuppressV V arnings, do b ili biste
u p o zo ren je za tu konverziju). U v rem e izvršavanja, to je sam o n iz tip a Object, i to stvara
p ro b le m e. Jedini n ačin da u sp ešn o n ap rav ite niz g en eričk o g tip a jeste d a n a p rav ite nov
niz o b risan o g tip a i da njega k onvertujete.
P ogledajm o nešto teži p rim er. R azm o trićem o je d n o sta v a n g en eričk i o m o ta č oko niza:
//: genericki/GenerickiNiz.java
1 warning
//: genericki/GenerickiNiz2.java
Isprv a ovo ne izgleda b itn o drugačije, sa m o što je konverzija p rem ešte n a. Bez an o tacija
@SuppressWarnings, d o b ijaćete u p o z o ren ja „ u n ch eck ed " (n ep ro v eren o ). M eđ u tim , in -
te rn o je niz sad a pred stav ljen kao Object[], a ne kao T [|. Kada se pozove m eto d a g e t( ),
o n a k o n v e rtu je o b jek at u tip T, što u stv ari i jeste tačan tip, pa je to b ezb ed n o . M e đ u tim ,
ako pozovete re p ( ), o n a će p o n o v o p o k u šati da Object[] k o n v ertu je u T [], što je i dalje
n etač n o , i p ro izv o di u p o zo ren je u v rem e p rev o đ en ja i izuzetak u v rem e izvršavanja. D ak-
le, n e m a n ačin a da se o b o ri tip p rip a d n o g in te rn o g niza koji m ože biti sa m o Object[].
P re d n o st in te rn o g tre tira n ja niza kao tip a Object[] u m esto T [] jeste u to m e što je m an je
v ero v a tn o da ćete u v rem e izvršavanja zab o rav iti koji je tip niza i zato slu čajn o n a p rav iti
g rešku (iako se većina tak v ih grešaka, a m o žd a i sve, b rzo o tk rije u v rem e izvršavanja).
U n o v o m k o d u , tre b a lo bi d a p ro sled ite leksem u tipa. U to m slučaju, GenerickiNiz bi
izgledao ovako:
//: ge ne ri ck i/ Ge nerickiNizSLeksemomTipa.java
import java.lang.reflect.*;
public ArrayList(Collection c) {
velicina = c . si ze ();
elementPodataka = (E[])new O b j e c t [ v e l i c i n a ] ;
c . to Ar ra y(elementPodataka);
}
A ko pregledate klasu A rra y L ist.ja v a , n aći ćete m n o štv o takv ih eksp licitnih konverzija.
I šta se dešava kada klasu prevedem o?
Kao što sm o i m islili, s ta n d a rd n e b ib lio tek e p ro u z ro k u ju m n o štv o u p o zo ren ja. Ako ste
pisali n a C -u , po g otovo o n o m e p re izdavan ja sta n d a rd a ANSI C, sećate se je d n e posledice
upo zo ren ja: kada shvatite da m ožete d a ih zan e m a rite, tako i radite. Z ato je najbolje da
p revodilac ne daje nikakve p o ru k e uk o lik o p ro g ra m e r na njih ne m o ra da reaguje.
U svom vveblogu,3 Neal G after (jed an o d v o d ećih p ro g ra m e ra Jave SE5) ističe da je bio
lenj to k o m p rerad e Javinih b ib lio tek a i d a m i ostali ne bi treb alo da sledim o njegov pri-
m er. N eal kaže i da nije m o g ao da p o p ra v i d eo k oda Javine b iblioteke, a da p ri to m ne
' http://gafter.blogspot.com/2004/U9/puzzling-through-erasurc-cinswcr.html
Poglavlje 15: Generički tipovi 531
sru ši postojeći interfejs. D akle, čak i ako se u izv o rn o m k o d u Javinih b ib lio tek a pojav lju ju
n ek i id io m i p ro jek to v an ja, n e znači d a je to p rav i n ačin rad a. D o k čitate k o d b ib lio tek a,
ne sm ete u zim ati zdravo za g otovo d a je to u z o r koji treb a d a sledite u svom k o d u .
Granice
Granice su u k ra tk o p red stav ljen e u p re th o d n o m đ elu poglavlja (v id eti stran ice 514 i 515).
G ran ice o m o g u ć u ju d a n am eć ete o g ran ičen ja p a ra m e ta rsk im tip o v im a koje m o žete
u p o treb ljav ati u g en eričk o m k o d u . Iako tim e d o b ijate m o g u ćn o st da n am ećete p ravila o
tip o v im a n a koje m ožete p rim e n iti svoj gen erički k od , p o ten cijaln o važniji efekat je d a
m o žete p ozivati m e to d e d efin isan e u vašim o g ra n ič e n im tip o v im a.
P ošto b risan je u k lan ja in fo rm a c ije o tip o v im a, jed in e m e to d e koje m o žete poziv ati za
n eo g ra n ič e n generički p a ra m e ta r jesu o n e đ o stu p n e za O b je c t. M e đ u tim , ukoliko taj p a-
ra m e ta r m o žete o g ran ič iti tak o da b u d e p o d sk u p tipova, o n d a m o žete poziv ati m e to d e
to g p o d sk u p a . D a b i sproveo to o g ran ičen je, Javin gen eričk i k o d p o n o v o u p o treb ljav a re-
zerv isan u reč e x te n d s . N e sm ete s m e tn u ti s u m a d a u k o n te k stu generičk ih g ran ica, ex-
te n d s im a sasvim d ru g ačije zn ačen je n ego o b ičn o . U n a re d n o m p rim e ru p rik azaćem o
o sn o v n o o g ran icam a:
// Ovo neće raditi -- prve moraju biti klase, a tek onda interfejsi:
// class Ob oj enaDimenzija<T extends ImaBoju & Đimenzija> {
// Više granica:
class ObojenaDimenzija<T extends Dimenzija & ImaBoju> {
T stavka;
Ob oj enaDimenzija(T stavka) { this.stavka = stavka; }
T getltemO { return stavka; }
ja va.awt.Color boja() { return s t av ka .g et Co lo r( ); }
int getX() { return stavka.x; }
int g e t Y () { return stavka.y; }
int getZ() { return stavka.z; }
532 Misliti na Javi
class Ogranicen
extends Dimenzija implements ImaBoju, Tezinu {
public java.awt.Color getColor() { return null; }
public int tezina() { return 0; }
}
//: ge nericki/NasledjivanjeGranica.java
class DrziStavku<T> {
T stavka;
DrziStavku(T stavka) { this.stavka = stavka; }
T getltemO { return stavka; }
}
//: genericki/EpskaBitka.java
// Primer granica u Javinom generičkom kodu.
import ja v a . u t i 1 .*;
interface SuperSila {}
interface RentgenskiVid extends SuperSila {
void vidi Kr oz Zi do ve ();
}
interface SuperSluh extends SuperSila {
void cuje Tih e Z v u k o v e O ;
}
interface SuperNjuh extends SuperSila {
void pr at iPoMiris u ( ) ;
}
Džokerski argumenti
Već ste videli neke jed n o sta v n e u p o treb e džokerskih argum enata -z n a k o v a p ita n ja u izra-
zim a generičkih a rg u m en a ta - u poglavlju Č uvanje objekata, a jo š više u poglavlju Podaci
o tipu. U ovom odeljk u ra z m o trić em o ih detaljnije.
Počećem o od p rim e ra koji po k azuje o d re đ e n o p o n a ša n je nizova: n iz izvedenog tip a
m ožete da d o d elite referenci niza o sn o v n o g tipa:
//: ge ne ricki/KovarijantniNizovi.java
class Voce {}
class Jabuka extends Voce {}
class Jonathan extends Jabuka {}
class Narandža extends Voce {}
Prvi red u m eto d i m a in ( ) pravi niz Jabuka i d ođ elju je ga referenci na niz tip a Voce. To
im a sm isla - Jabuka je v rsta voća, pa niz Jabuka treb a isto vrem eno da b u d e i niz tip a Voce.
M e đ u tim ,d a je pravi tip niza Jabuka[], u njega b iste m ogli da stavite sam o o b jek at tip a
Jabuka ili p o d tip a o d Jabuka, što bi zaista delovalo i u v rem e p rev o đ en ja i u v re m e izvrša
vanja. Ali o b ra tite p a žn ju na to da p revodilac dozvoljava stavljanje ob jek ta tip a Voce u niz.
P revo dio cu to im a sm isla, pošto o n im a referencu n a Voce[] - zašto da ne dozv oli d a se u
niz stavi o bjek at tip a Voce ili bilo šta izvedeno iz tip a Voce, kao što je Narandža? Stoga je
u v rem e prev ođ enja to dozvoljeno. M eđ u tim , u v rem e izvršavanja, m e h an izam nizova
zna da radi s nizom Jabuka[] i generiše izuzetak kada se stra n i tip stavi u taj niz.
„Svođenje naviše“ ovde nije p rav a reč. Vi zaprav o je d an niz do d elju jete d ru g o m . Svaki
niz se p o n aša kao d a sadrži d ru g e objekte, ali p o što m o žem o d a svedem o naviše, jasn o je
da objekti niza m o g u da p o štu ju p ravila o tip u o b jek ata koje niz sadrži. To je kao d a nizovi
zn aju šta sadrže, pa ne m ožete da ih p revarite zbog pro v era u v rem e p re v o đ en ja i u v rem e
izvršavanja.
536 Misliti na Javi
Takvo p o n ašan je nizova nije to lik o stra šn o , p o što u v rem e izvršavanja vi ip ak saznajete
d a ste u m e tn u li p o grešan tip . Ali je d a n o d o sn o v n ih ciljeva gen eričk o g k o d a jeste o tk ri-
v anje tak v ih grešaka već u v rem e p rev o đ e n ja . Šta će se d esiti ako u m esto nizova p o k u ša-
m o da u p o tre b im o kontejnere?
T ip o b jek ta listav sad a je List<? extends Voce>, što m o žete čitati kao „lista bilo kojeg
tip a koji n asled u je Voce“. M eđ u tim , to ne znači da ova Lista p rim a sve p o d tip o v e klase Vo-
ce. D žok ersk i a rg u m e n t o značava o d re đ e n tip , p a g o rn ji izraz znači „o d ređ en tip koji
referenca listav ne specificira“ Stoga d o d eljen a Lista m o ra d a čuva sp e c ifk ira n tip k ao što
su Voce ili Jabuka. D a bi svođenje naviše n a listavbilo m og u će, taj tip je „ n ije v ažn o koji“.
U koliko je je d in o o g ran ičen je da ta L ista sadrži o d ređ e n o Voce ili p o d tip klase Voce, ali
zapravo v am nije v ažno koji, šta m o žete d a u ra d ite s tak vo m L istom ? A ko ne zn ate koji tip
ta L ista sadrži, kako d a joj b ezb ed n o d o d ate neki objekat? N e m ožete, kao što ni u p ro -
g ra m u KovarijantniNizovi.java niste m ogli da niz „svedete naviše“, sem što u o v o m sluča-
ju to sprečava prevodilac, a ta m o sistem za izvršavanje. Sada p ro b le m otk riv ate ranije.
M ožd a ste po m islili d a je ovo o tišlo p redaleko, je r sada Listi za koju ste u p rav o rekli da
sad rži o b jek te tip a Jabuka, n e m o žete d a d o d a te o b jek at tip a Jabuka. U p ra v u ste, ali p re-
vodilac to ne zna. O b je k a t tip a List<? extends Voce> m o ž e legalno da u k azuje n a o b jek at
tip a List<Narandža>. N ak o n što u ra d ite ovu v rstu „svođenja naviše“, g u b ite m o g u ćn o st
d a bilo šta p ro sled ite u listu, čak i Object.
S d ru g e stra n e, ako po zo v ete m e to d u koja v raća Voce, to je b ezb ed n o je r z n a te da
sadržaj L iste m o ra u n a jm a n ju ru k u b iti tip a Voce, p a će p rev o d ilac to dozvoliti.
Vežba 26: (2) P okažite k o v arijan su nizova p o m o ć u klasa N um ber i Integer.
Vežba 27: (2) P okažite d a k o v arijan sa ne rad i s Listama i klasam a Number i Integer, a za-
tim uvedite d žokerske arg u m e n te .
//: ge nericki/PametPrevodioca.java
import j a v a . u t i 1 .*;
Pozvali sm o m e to d e c o n tain s( ) i ind ex O f( ) koje p rim a ju objekte tip a Jabuka kao ar-
g u m e n te , i sve d o b ro radi. Z nači li to d a prevo dilac isp itu je k o d i proverava da li o d re đ e n a
m e to d a m o d ifik u je svoj objekat?
Iz d o k u m e n ta c ije za ArrayList viclim o da p rev o d ilac nije toliko p a m e ta n . D o k m e to d a
a d d ( ) p rim a a rg u m e n t tip a g eneričkog p a ra m e tra , m eto d e c o n tain s( ) i indexO f( ) p ri-
m a ju a rg u m e n te tip a Object. Stoga k ad a zad ate ArrayList<? extends Voce>, a rg u m e n t za
a d d ( ) p o staje ? extends Voce. Iz to g o pisa p rev od ilac n e m o že znati koji bi p o d tip klase
Voce tre b a lo da d o đ e na to m esto, te stoga ne p rih v ata n ijed an od tih p o d tip o v a. Nije
538 Misliti na Javi
važno da li ste na jp re tip Jabuka sveli naviše n a Voce - prev o d ilac p ro s to o d b ija da pozove
m e to d u (kao što je a d d ( )) uk o liko je u listi a rg u m e n a ta džo kersk i a rg u m en t.
A rg u m en ti m eto d a co n tain s( ) i ind ex O f( ) tip a jesu Object, n e m a nikak vih d žo-
kerskih arg u m en a ta, i prev o d ilac dozvoljava poziv. To zn ači d a je na p ro je k ta n tu generič-
ke klase d a o d lu či koji p ozivi su b ez b e d n i i d a za njih ov e arg u m e n te u p o tre b i tip Object.
U koliko hoćete d a o n e m o g u ć ite o d re đ e n i p oziv k ad a se u tip u u p o treb lja v a džokerski
arg u m en t, u listu a rg u m e n a ta stavite p a ra m e ta r tip a.
To m o žete vid eti u ovoj v eom a je d n o sta v n o j klasi Skladiste:
//: genericki/Skladiste.java
sa o b je k to m tip a Jabuka n iti sa o b jck to m tip a Voce, p o što je njen a rg u m e n t tak o đ e ? Ex-
tends Voce, što znači d a m ože b iti b ilo šta, a p rev o d ilac ne m ože da p ro v eri b e zb ed n o st
tip a „bilo šta“.
M e đ u tim , m eto d a eq u a ls( ) rad i d o b ro , zato što kao a rg u m e n t p rim a Object, a ne T.
D akle, prevod ilac pazi sa m o na tip o v e o b jek ata koji bivaju p ro sleđ en i ili vraćeni. O n ne
an alizira k o d kako b i v id eo d a li zaista u p isu jete ili učitavate.
Kontravarijansa
M ože se o tići i s u p ro tn im p u te m i u p o tre b iti džokerski argum ent nadtipa. Tada kažete da
je džo kersk i a rg u m e n t o g ran ičen bilo k o jo m n a tk laso m o d re đ e n e klase, tako što zad ate <?
super MojaKlasa> ili čak u p o tre b ite p a ra m e ta r tipa: <? super T> (iako g enerički p ara-
m e ta r ne m o žete zad ati kao g ra n ic u n a d tip a , tj. n e m o žete reći <T super MojaKlasa>).
O vo o m o g u ć u je d a b ezb ed n o p ro sled ite o b jek at n ek o g tip a u generički tip. D akle, sa džo-
kerskim a rg u m e n tim a n a d tip o v a m o žete u p isiv ati u k o n te jn e re (objekte tip a Collection):
//: genericki/DzokeriNadtipova.java
import java.util.*;
A rg u m e n t jabuke je L ista n ekog tip a koji je n a d tip klase Jabuka; zato zn ate d a je toj li-
sti b ezb e d n o d o d a ti ob jek at tip a Jabuka ili p o d tip a klase Jabuka. M e đ u tim , p o što je Ja-
buka donja granica, ne zn ate da li je takvoj L isti b ezb ed n o d o d a ti Voce, p o što biste na taj
naćin dozvolili da se L ista o tv a ra za d o d av an je tip o v a koji n isu Jabuka, što bi ugrozilo
b e z b e d n o st statičn ih tipova.
D akle, g ranice p o d tip o v a i n ad tip o v a m o žete sm a tra ti n ač in im a „upisivanja" (p ro -
sleđivanja u m e to d u ) u generički tip, o d n o sn o ,,učitavanja“ (vraćanja iz m eto d e) iz gene-
ričkog tipa.
G ran ice n a d tip o v a u b lažu ju o g ran ičen ja o n o g a što m o žete da p ro sled ite u m eto d u :
//: genericki/GenerickoCitanje.java
import java.util.*;
//: genericki/NeograniceniDzokeri1.java
import java.util
//: genericki/NeograniceniDzokeri'2.java
import java.util
//: genericki/Dzokeri.java
// Istraživanje značenja džokerskih argumenata.
// skladiste.set(arg); // Upozorenje:
// Unchecked call to set(T) as a
// member of the raw type Skladiste
// (Neproveren poziv metode set(T) kao
// člana sirovog tipa Skladiste)
// skladiste.set(new DzokeriO); // Isto upozorenje
siroviArgumenti(sirovo, lng);
siroviArgumenti(potpunoZadato, lng);
siroviArgumenti(neograniceno, lng);
siroviArgumenti(ograniceno, Ing);
neogArg(sirovo, lng);
neogArg(potpunoZadato, lng);
neogArg(neograniceno, lng);
neogArg(ograniceno, Ing);
divljiNadtip(potpunoZadato, lng);
// divljiNadtip(neograniceno, lng); // Greska:
// divljiNadtip(Skladiste<? super T>,T) ne mo že biti
// primenjena na (Sk1adiste<(rezultat od) ?>,Long)
// divljiNadtip(ograniceno, lng); // Greska:
// divljiNadtip(Skladiste<? super T>,T) ne može biti
// primenjena na (Skladiste<(rezultat od) ? extends Long>,Long)
}
} III--
U m eto d i siroviA rgum enti( ), p revodilac zn a d a je Skladiste gen erićk i tip, pa iako je
o vde izražen k ao sirov tip, p revodilac zna d a p ro sled iv an je o b jek ta tip a Object m etodi
s e t( ) nije b ezbedno . Pošto je to sirov tip , m eto d i s e t( ) m o žete p ro sled iti o b jek at bilo ko-
jeg tip a koji će biti sveden naviše na Object. D akle, k ad g o d im a te sirov tip, n em ate p ro-
v eru u v rem e prevođenja. Isto po k azu je poziv m eto d e g e t( ): n em a tip a T, pa rezultat
m o že biti sam o Object.
Lako je pom isliti da su sirovo Skladiste i Skladiste<?> p rib ližn o ista stvar. M etoda
neogA rg( ) ističe njihovu različitost - o n a otkriva istu vrstu p ro b lem a, ali ih prijavljuje kao
greške, a ne kao upozorenja, p o što sirovo Skladiste p rim a sve kom b in acije svih tipova, d o k
Skladiste<?> p rim a h o m o g en u kolekciju određenog tipa, i ne m ožete m u proslediti Object.
Poglav|je 15: Generički tipovi 547
Konverzija hvatanjem
Jedna k o n k re tn a situacija zahteva da u p o tre b ite džo k erski a rg u m e n t < ?> , a ne sirov tip.
Ako sirov tip p ro sledite m e to d i koja u p o treb ljav a < ?> , p revo dilac m o že da zaključi koji je
stvarn i p a ra m e ta r tipa, tako da i ta m eto d a m o ž e da pozove d ru g u m e to d u koja p rim a
548 Misliti na Javi
//: genericki/KonverzijaHvatanjem.java
Nedostaci
U o v o m o d eljk u g o v o rim o o n ek im ned o stacim a korišćenja gen eričk ih tip o v a u Javi.
//: genericki/Listalnt.java
// Automatsko pakovanje nadoknađuje nemogućnost
// upotrebe prostih tipova u generičkom kodu.
import java.util
//: genericki/SkupBajtova.java
import java.util.*;
/ / : genericki/TestProstihGenerickih.java
import net.mindview.util.*;
3909
5202
2209
5458
* ///:-
//: genericki/ViseVarijanatalnterfejsa.java
// {CompileTimeError} (Ne može se prevesti)
interface PlacaSe<T> {}
class StekNepromenljiveVelicine<T> {
private int indeks = 0;
private Object[] skladiste;
public StekNepromenljiveVelicine(int velicina) {
skladiste = new 0bject[vel icina];
552 Misliti na Javi
//: genericki/PotrebnaKonverzija.java
import java.io.*;
import java.util.*;
Kao što čete v ideti u sledećem poglavlju, m e to d a readO bject( ) ne m o že znati šta čita,
pa vraća o b jek at koji se m o ra ko n v erto vati. Ali k ad a k o m e n ta riše te a n o ta c iju @Suppress-
W arnings i prev ed ete p ro g ra m , d o b ićete u p o zo ren je:
/ / : genericki/ClassKonverzija.java
import java.io.*;
import java.util.*;
List<Spravica>.class.cast(ulaz.readObject())
(List<Spravica>)List.class.cast(ulaz.readObject())
Preklapanje
O vo se neće prevesti, iako bi se reklo da valja p o k u šati:
//: genericki/ListaZaUpotrebu2.java
import java.util
//: genericki/UporedivLjubimac.java
V redelo b i suziti tip s kojim se po tk lasa o d UporedivLjubimac m ože p o red iti. P rim era
ra d i, Macka bi treb alo d a je u p o red iv a (engl. cottiparable) sam o s d ru g im o b jek tim a tip a
Macka:
//: genericki/OtetiInterfejs.java
// {CompileTimeError} (Ne može se prevesti)
/ / : genericki/Ogranicenillporedi vi Ljubimci.java
// IIi samo:
Samoograničeni tipovi
U Javino m g eneričk o m k o d u p e rio d ič n o se p o n av lja je d a n z b u n ju ju ć i id io m p ro -
jek to van ja. Evo kako izgleda:
//: genericki/GenerickiTipKojiSamSebePonavlja.java
class GenerickiTip<T> {}
O vo b ism o m ogli nazvati generički tip koji se neobično ponavlja ( Curiously Recurring
Generics, CRG), p o C o p lien o v o m Šablonskom obrascu koji se neobičnoponavlja u C + + -u .
O n o „n eo b ič n o ponavlja“ o d n o si se n a čin jen icu d a se klasa, p rilič n o n eo b ičn o , pojavljuje
u so pstvenoj o snov n o j klasi.
D a biste shvatili šta to znači, naglas izgovorite: „P ravim n o v u klasu koja nasleđuje generi-
čki tip koji im e m oje klase u zim a za svoj p a ra m e ta r“. Sta generički osnovni tip m ože da u ra-
di s d a tim im e n o m izvedene klase? Pa, u Javi se generički m e h a n iza m o d n o si n a arg u m en te
i p o v ra tn e tipove; o n m ože d a n ap rav i o sn o v n u klasu koja za svoje arg u m en te i p o v ratn e
tipove up otrebljava izvedenu klasu. Isto tako, izvedenu klasu o n m ože da upo treb i za tipove
polja, iako će o n i brisan jem b iti svedeni n a Object. To izražava sledeća generička klasa:
D obili sm o o b ič an generički tip čije m eto d e i p rim a ju i d a ju objekte istog p aram e tar-
skog tip a , k ao i m e to d u koja o b ra đ u je to u sk lad išten o polje (iako n ad njim obavlja sam o
op eracije tip a Object).
OsnovnoSkladiste m o žem o d a u p o tre b im o u g en eričk o m tip u koji se n eo b ičn o p o -
navlja (CRG):
klasa, ali se za sve a rg u m e n te i p o v ra tn e v red n o sti te fu n k cio n aln o sti, u p o trebljava izvede-
ni tip. D ru g im rečim a, u rezultujućoj klasi se u p o treb ljav a tačan, a n e o sn o v n i tip. Z ato su
u klasi Podtip i a rg u m e n t m e to d e s e t( ) i p o v ra tn i tip m eto d e g e t( ) tač n o tipa Podtip.
Samoograničenje
OsnovnoSkladiste m o ž e d a upotrebi bilo koji tip k a o svoj generički parametar, k a o ovde:
class Drugo {}
class DrugoOsnovno extends OsnovnoSkladiste<Drugo> {}
//: genericki/SamoOgranicavajuci.java
class D {}
// Ovo ne možete:
// class E extends SamoOgraniceni<D> {}
// Greška u vreme prevođenja: parametar tipa D nije unutar svojih granica
558 Misliti na Javi
/ / : generi cki/NijeSamoOgranicena.java
class D2 {}
// Ovo je sada ispravno:
class E2 extends NijeSamo0granicena<D2> {} ///:-
//: genericki/SamoOgranicavajuceMetode.java
Kovarijansa argumenata
V red n o st sa m o o g ran ič av aju ćih tip o v a jeste to što proizv od e kovarijantne tipove argu-
m enata - t i p o v i a rg u m e n a ta m e to d a v a rira ju k ao n jihove potklase.
lako sa m o o g ran ičav aju ći tip o v i p ro izv ođ e i p o v ra tn e tipove koji su isti kao tip p o tk la-
se, to nije toliko važn o, p o što je Java SE5 uvela kovarijantne povratne tipove.
//: genericki/KovarijantniPovratniTipovi.java
class Osnovna {}
class Izvedena extends Osnovna {}
interface ObicnaDajOb {
Osnovna dajob();
}
//: genericki/GenerickeMetodelPovratniTipovi.java
//: genericki/ObicniArgumenti.java
class ObicnaZadajOb {
void set(Osnovna osnovna) {
System.out.println("ObicnaZadajOb.zadajOb(Osnovna)");
}
}
}
} /* Ispis:
IzvedenaZadajOb.zadajOb(Izvedena)
ObicnaZadajOb.zadajOb(Osnovna)
* ///:-
//: genericki/SamoOgranicavanjelKovarijantniArgumenti.java
/ / : genericki/ObicnoGenerickoNasledjivanje.java
//: genericki/ProveravanaLista.java
// Koristimo metodu Collection.checkedList().
import podaciotipu.ljubimci.*;
import java.util.*;
Izuzeci
U gen eričk o m kod u se, zbog brisan ja, izuzeci veom a m alo u p o treb ljav aju . B lok c a tc h ne
m ože da hvata izuzetke generičkog tipa, zato što tača n tip izuzetka m o ra b iti p o z n a t i u
v rem e p rev o đ en ja i u v rem e izvršavanja. Takođe, generička klasa ne m o že ni p o sre d n o n i
n ep o sre d n o da nasledi klasu T h ro w a b le (i tim e o p e t sprečava d a definišete generički izu-
zetak koji se ne m ože uh vatiti).
564 Misliti na Javi
/ / : genericki/GenerisanjeGenerickoglzuzetka.java
import java.util.*;
Pokretac0brada<Integer,Greska2> pokretac2 =
new Pokretac0brada<Integer,Greska2>();
for(int i = 0 ; i < 3 ; i++)
pokretac2.add(new Preradjivac2());
try {
System.out.println(pokretac2.obradiSve());
} catch(Greska2 e) {
System.out.println(e);
}
}
} ///:-
Preradjivac poziva m e to d u o b ra d a ( ) i m o ž e generisati izuzetak tip a E. R ezultat m e-
to d e o b ra d a ( ) sm ešta se u List<T> kontejnerRezultata (to nazivam o param etar kolekci-
je). Klasa PokretacO brada im a m e to d u obradiSve( ) koja izvršava sve o bjek te tipa
O brada koje klasa sadrži i vraća kontejnerRezultata.
Da ne m o žete p a ra m e triz o v a ti g en erisan e izuzetke, ne b iste m ogli napisati ovaj k o d
generički, i to zbog prov erav an ih izuzetaka.
Vežba 36: (2) D o d ajte d ru g i p a ra m e triz o v a n i izuzetak kJasi Preradjivac i pok ažite da izu-
zeci m o g u da se m en jaju nezavisno.
Miksini
T erm in miksin (engl. m ixin) s v re m e n o m je d o b io razn a značenja, ali o sn o v n o je: m ešanje
m o g u ć n o sti više klasa da bi se d o b ila rezu ltu ju ća klasa koja pređstavlja sve um ešan e tip o -
ve, tj. o n a se naziva m ešavina ili m iksin. To se o b ičn o radi u zadnji čas, što je čini p rig o d -
n o m alatk o m za lako sastavljanje klasa.
Jedna o d p re d n o sti m ik sin a jeste to što o n i k arak teristik e i p o n ašan ja d o sled n o p ri-
m e n ju ju na razn e klase. Uz to, k ad a nešto p ro m e n ite u m iksin klasi, te p ro m e n e se pre-
nose na sve klase na koje se m ik sin p rim e n i. Z ato su m iksini zn ačajan d eo aspektno
orijentisanogprogram iranja (A O P), a za rešavanje p ro b le m a s m ešav in am a često se p red -
lažu razni aspekti.
566 Misliti na Javi
//: genericki/Miksini.cpp
#include <string>
#include <ctime>
#include <iostream>
using namespace std;
class Osnovni {
string vrednost;
publi c:
void postavi(string vre) { vrednost = vre; }
string daj() { return vrednost; }
t;
int main() {
SVremenskomOznakom<SaSerijskimBrojem<Osnovni> > miksinl, miksin2;
miksinl.postavi("ispitni znakovni niz 1");
miksin2.postavi("ispitni znakovni niz 2");
cout « miksinl.daj() « " " « miksinl.dajOznaku() «
" " « miksinl.dajSerijskiBrojO « endl;
cout « miksin2.daj() « " " « miksin2.daj0znaku() «
" " « miksin2.dajSerijskiBroj() « endl;
Poglavlje 15: Generički tipovi 567
} /* Ispis: (primer)
ispitni znakovni niz 1 1129840250 1
ispitni znakovni niz 2 1129840250 2
* ///:-
U m eto d i m a in ( ), rez u ltu ju ći tip o b jekata m iksinl i m iksin2 im a sve m eto d e u m eša-
n ih tipova. S m atrajte m ik sin fu n k c ijo m koja preslikava p o sto jeće klase na nove potklase.
O b ra tite p ažn ju na to kako se p o m o ć u ove teh n ik e lako prave m iksini; u suštini, vi sam o
kažete: „Evo šta h o ć u “, i to se izvrši:
//: genericki/Miksini.java
import java.util.*;
interface Osnovni {
public void postavi(String vre);
public String d aj();
}
//: genericki/dekorator/Dekorisanje.java
package genericki.dekorator;
import java.util.*;
class Osnovni {
private String vrednost;
public void set(String vre) { vrednost = vre; }
public String daj() { return vrednost; }
}
Z a razlik u o d statičn o g tip a, sam o d in am ičk i tip sadrži sve u m ešan e tipove, p a ovo i
dalje nije o n a k o lepo kao C + + -o v p ristu p , jer m o ra te da svedete n an iže n a o d g o v araju ći
tip pre n eg o što pozovete m eto d e za njega. M eđ u tim , z n a tn o je bliže p rav o m m ik sin u .
U p o d rš k u m ik sin a u Javi u lo žen o je p riličn o m n o g o rad a. E ksplicitno za tu sv rh u n a-
p rav ljen je i n ajm an je je d a n softverski d o d atak , jezik Jam.
Vežba 39: (1) U p ro g ra m M iksinDinamickimPosrednikom.java d o d a jte n o v u m iksin
klasu Obojen, um ešajte je u m iksin i d okažite da radi.
572 Misliti na Javi
Latentni tipovi
N a p o č e tk u ovog poglavlja p red stav ili sm o id eju p isan ja k o d a koji se m o že p rim e n iti
u o p šte n o u najvećoj m o g u ćo j m eri. D a b ism o to p o stig li, m o ra m o u blažiti o g ran ičen ja
tip ov a s ko jim a naš k o d rad i, a d a n e iz g u b im o p re d n o s ti statič n e p rovere (b ezb ed n o sti)
tipova. Tada ćem o m o ći d a p išem o k o d koji se bez iz m e n a m o ž e u p o treb ljav ati u više si-
tuacija - tj. opštiji kod.
Izgleda d a Javin generički k o d p rav i jo š je d a n k o ra k u to m sm eru . Kada pišete ili k o ri-
stite generičk i k o d koji sa m o čuva o b jekte, o n ra d i sa svim tip o v im a (sem p ro stih , iako
sm o videli da au to m a tsk o p ak o v an je to izglađuje). Ili, d ru g im rečim a, g eneričko skladište
m ože da kaže: „M eni je svejedno koji si tip “. K od k ojem nije važno s ko jim tip o v im a radi,
zaista se m ože p rim e n iti sv u d a i stoga je p o tp u n o o p šti (g enerički).
Kao što ste tako đ e videli, p ro b le m n astaje k ad a h o ć e te d a o b ra đ u je te generičke tipove
(ukoliko se to n e m ože o baviti p o ziv an jem m eto d a klase O b je c t), je r b risan je zahteva da
zadate granice generičkih tip o v a koji se m o g u u p o tre b iti, d a b i bilo m o g u će b ezb ed n o p o -
zivati k o n k re tn e m eto d e za generičke o b jek te u vašem k o d u . To je zn ačajno ograničenje
p o jm a opštosti, p o što svoje generičke tipove m o ra te o g ran ičiti tako da n asleđ u ju o d ređ en e
klase ili realizuju o d ređ en e interfejse. U n ek im slučajevim a, m o žd a ćete u m esto generičkih
klasa i interfejsa u p o tre b iti o b ičn e klase ili interfejse, p o što se o g ran ičen i generički tip ne
m o ra razlikovati od specificiranja klase ili interfejsa.
N eki p ro g ra m sk i jezici to rešavaju tak o što k o riste latentne ili strukturirane tipove.
M alo neobavezniji te rm in je duck typing, kao u izreci ,,lf it w alks like a d u c k a n d talks like
a d u ck , you m ig h t as vvell tre a t it like a d u c k “. (A ko h o d a k ao p a tk a i zvuči kao patka, o n d a
sm atra jte da je to p atk a.) D u ck ty p in g je p o sta o p rilič n o p o p u la ra n te rm in , m o žd a zato
što za so b o m ne vuče istorijski p rtljag kao p re th o d n a dva te rm in a.
G enerički k o d o b ičn o poziva tek n ekoliko m eto d a za o d re đ e n generički tip, a jezik s la-
te n tn im tip o v im a ublažu je to o g ra n ič e n je (i daje o pštiji k o d ) tako što zahteva realizo-
vanje sam o nekog p o d sk u p a m e to d a , a ne o d re đ e n e klase ili interfejsa. L aten tn i tipovi
o m o g u ć u ju rad s razn im h ije ra rh ija m a klasa je r se poziv aju m e to d e koje n isu deo zajed-
n ičkog interfejsa. Stoga parče k o d a m o že zap ravo reći: ,A ko m ožeš g o v o r i ti ( ) i se d eti( ),
m e n i je svejedno koji si tip “. U koliko ne zahteva o d re đ e n tip , kod je opštiji.
L atentni tipov i su m e h a n iz a m za rasp o ređ iv an je i p o n o v n o korišćenje koda. V išekrat-
n o u p o treb ljiv k o d je lakše p isati s n jim a nego bez njih . R aspoređivanje i p o n o v n o ko-
rišćenje koda jesu o sn o v n i p rin c ip i sveg p ro g ra m ira n ja : napiši k o d je d a n p u t, koristi ga
više p u ta i čuvaj n a je d n o m m estu . Pošto n e m o ra m da n avedem o d re đ e n i interfejs koji
m oj k o d o b rađ u je , la te n tn i tip o v i m i o m o g u ć u ju d a pišem m an je k oda i d a ga lakše p ri-
m en ju jem na više m esta.
O d jezika koji p o d ržav aju la te n tn e tip o v e, dva su P y th o n (m o žete ga b esp latn o p re u -
zeti n a adresi w ww .Python.org) i C + + .6 P y th o n tipove proverava d in am ičk i (gotovo sve
p ro veravanje tipov a obavlja se u v rem e izvršavanja), a C + + statički (u v rem e prev o đ en ja).
D akle, late n tn i tip ovi ne zah te v aju n i statičko ni d in am ič k o p ro vcravanje tipova.
A ko u z m e m o g o rn ji o p is i izrazim o ga n a P y th o n u , d o b ić em o ovo:
#: genericki/PsiIRoboti.py
class Pas:
def govoriti (self):
print “Av!"
def sedeti(self):
print "Sedim"
def reprodukovatise(self):
pass
class Robot:
def govoriti(self):
print "Klik!"
def sedeti(self):
print "Klank!"
def promenaUlja(self):
pass
def obavi(bilosta):
bi1osta.govori t i ()
bi1osta.sedeti()
a = Pas()
b = Robot()
obavi(a)
o bavi(b)
#:~
//: genericki/PsiIRoboti.cpp
class Pas {
publi c:
void govoriti() {}
574 Misliti na Javi
void sedeti() {}
void reprodukovatiseO {}
};
class Robot {
public:
void govoriti() {}
void sedeti () {}
void promenalllja() {
};
int main() {
Pas d;
Robot r;
obavi(d);
obavi(r);
} III--
I u P y th o n u i u C + + -u , P as i R o b o t n em aju n išta zajedničko, sem što im aju dve m e-
to d e sa id e n tič n im p o tp isim a . Sa sta n o v išta tipova, to su sasvim različiti tipovi. M eđ u tim ,
funkciji o b a v i ( ) nije važno koji je tip n jen o g a rg u m en ta, a la ten tn o st tip o v a jo j o m o -
gućuje da p rih v ati objekte o b a tip a.
C + + p ro verava m o že li zaista d a pošalje te p oruk e. A ko p o ku šate d a prosled ite p o -
grešan tip , p rev o dilac će v am ispisati p o ru k u o grešci (te p o ru k e o greškam a su o d uv ek
bile užasne i o p širn e, i o sn o v n i su razlog što C + + -o v i šab lo ni im aju iošu rep u taciju ). Iako
to rad e u različita v re m en a - C + + u v rem e p revo đen ja, a P y th o n u vrem e izvršavanja -
o b a jezika p roveravaju u p o tre b u tip o v a, pa ih sm a tra ju strogo tipiziratiim jezicim a.7 La-
te n tn i tip o v i ne u grožavaju stro g u tip iziran o st.
Pošto su g enerički tip o v i u Javu d o d a ti n ak n a d n o , nije bilo šanse da se realizuje bilo
koja v rsta la te n tn ih tipova, pa Java n e m a tu m o g u ćn o st. Z ato isprva izgleđa kao da je Ja-
vin generički m eh a n iza m „m an je generičk i" o d jezika koji p o držav aju la te n tn e tip ov e.8
N a p rim er, ako g o rn ji p rim e r p o k u ša m o da realizujem o u Javi, m o raćem o da u p o tre b im o
klasu ili interfejs koje će m o sp ecificirati u izrazu za granice:
//: genericki/Obavlja.java
//: genericki/PsiIRoboti.java
// U Javi nema latentnih tipova
import podaciotipu.ljubimci.*;
import static net.mindview.util.Print.*;
class Komuniciraj {
public static <T extends Obavlja>
void obavi(T izvodjac) {
izvodjac.govoriti();
izvodjac.sedeti();
}
}
//: genericki/ProstiPsiIRoboti.java
// Uklanjamo generički kod; prograin i dalje radi.
class KomunicirajJednostavno {
static void obavi(Obavlja izvodjac) {
izvodjac.govorit i ();
izvodjac.sedeti();
576 Misliti na Javi
)
}
Refleksija
Jedan p ristu p je u p o treb a refleksije. Evo m e to d e o b a v i ( ) koja u p o treb ljav a laten tn e tipove:
//: genericki/LatentnaRefleksija.java
// Pravljenje latentnih tipova pomoću refleksije.
import java.lang.reflect.*;
import static net.mindview.util.Print.*;
class KomunicirajRefleksivno
public static void obavi(Object zvucnik) {
Class<?> zvck = zvucnik.getClass();
try {
try {
Method govoriti = zvck.getMethod("govoriti");
govoriti.invoke(zvucnik);
} catch(NoSuchMethodException e) {
print(zvucnik + " ne može govoriti");
}
try {
Method sedeti = zvck.getMethod(“sedeti");
sedeti.invoke(zvucnik);
} catch(NoSuchMethodException e) {
print(zvucnik + 11 ne može sedeti");
}
} catch(Exception e) {
throw new RuntimeException(zvucnik.toString(), e ) ;
}
}
}
O vde su klase sasvim različite i n em aju zajed n ičk ih o sn o v n ih klasa (sem klase O b je c t)
niti interfejsa. P om o ču refleksije, m e to d a K o m u n ic ira jR e fle k siv n o .o b a v i( ) m ože di-
nam ički da u tv rd i da li su željene m eto d e d o stu p n e i d a ih pozove. M ože da se izbori ćak
i sa čin jenicom da M im ik a im a sam o je d n u o d p o tre b n ih m e to d a i da svoj cilj ispunjava
delim ično.
P ogledajm o p rim e r koji istražu je taj p ro b lem . P retp o sta v im o d a h o će te da n ap rav ite
m e to d u p r i m e n i ( ) koja bilo koju m e to d u p rim e n ju je n a sve o b jek te u nekoj sekvenci. U
ovoj situaciji interfejsi kao da ne o d g o v araju . H o ćete d a p rim e n ite b ilo k o ju m e to d u na
kolekciju objekata, a interfejsi n a m e ć u o g ran ičen je koje ne dozvoljava d a o pišete „bilo
koju m e to d u “. Kako da to u rad ite u Javi?
P ro b lem m o ž e m o n ajp re da rešim o refleksijom , i to isp ad a p rilič n o eleg a n tn o zbog ar-
g u m e n a ta prom enljive d u ž in e koji se k o riste o d Jave SE5:
//: genericki/Primeni.java
// (main: TestZaPrimeni}
import java.lang.reflect.*;
import java.util.*;
import static net.mindview.util.Print.*;
class Oblik {
public void rotirati() { print(this + " rotirati"); }
public void promvelicine(int novaVelicina) {
print(this + " promvelicine " + novaVelicina);
}
}
class TestZaPrimeni {
public static void main(String[] args) throws Exception {
List<Oblik> oblici = new ArrayList<0blik>();
for(int i = 0; i < 10; i++)
obl ici.add(new Oblik()) ;
Primeni.primeni(oblici, Obl ik.class.getMethod("rotirati“));
Primeni .primeni (obl ici,
Oblik.class.getMethodC'promvelicine", int.class), 5);
List<Kvadrat> kvadrati = new ArrayList<Kvadrat>();
for(int i = 0; i < 10; i++)
kvadrati.add(new Kvadrat());
Primeni.primeni(kvadrati, Oblik.class.getMethod("rotirati“));
Primeni.primeni(kvadrati,
Oblik.class.getMethod("promvelicine", int.class), 5);
U p ro g ra m u Prim eni im am o sreće, zato što je u lavu u g rađ e n interfejs Iterable koji se
upo treb ljav a u Javinoj b iblioteci k o n tejn e ra. Z ato m e to d a p rim e n i( ) m ože d a p rih v ati
sve što realizuje interfejs Iterable, a to su sve p o tld ase o d Collection kao što je List. Ali
o n a m ože da p rih v ati i sve d ru g o u čem u ste vi realizovali Iterable - na p rim er, ovde de-
finisan u klasu JednostavanRedZaCekanje koja se g o re u p o treb ljav a u m eto d i m a in ( ):
//: genericki/JednostavanRedZaCekanje.java
// Drugačija vrsta kontejnera koji je iterabilan
import java.uti1 .*;
class Ugovor {
private static long brojac = 0;
private final long id = brojac++;
public String toStringO {
return getClass().getName() + " " + id;
}
}
class PrenosVlasnistva extends Ugovor {}
class PopuniTest {
public static void main(String[] args) {
List<Ugovor> ugovori = new ArrayList<Ugovor>();
Popuni.popuni(ugovori, Ugovor.class, 3);
Popuni.popuni(ugovori, PrenosVlasnistva.class, 2);
for(Ugovor c: ugovori)
System.out.println(c);
JednostavanRedZaCekanje<Ugovor> redUgovora =
new JednostavanRedZaCekanje<Ugovor>();
// Neće raditi. popuni() nije dovoljno opšta:
// Popuni.popuni(redUgovora, Ugovor.class, 3);
582 Misliti na Javi
} /* Ispis:
Ugovor 0
Ugovor 1
Ugovor 2
PrenosVlasnistva 3
PrenosVlasnistva 4
* ///:-
//: genericki/Popuni2.java
// Upotreba adaptera za simuliranje latentnih tipova.
// (main: Popuni2Test}
import genericki.kafa.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.uti1.Print.*;
imaadd.add(classLeksema.newInstance());
) catch(Exception e) {
throw new RuntimeException(e);
}
}
// Generatorska verzija:
public static <T> void popuni(ImaAdd<T> imaadd,
Generator<T> generator, int velicina) {
for(int i = 0; i < velicina; i++)
imaadd.add(generator.sledeci());
}
class Popuni2Test {
public static void main(String[] args) {
// Prilagodi kontejner (objekat tipa Collection):
List<Kafa> nosilac = new ArrayList<Kafa>();
Popuni2.popuni(
new ImaAddCol1ectionAdapter<Kafa>(nosi 1ac),
Kafa.class, 3);
// Pomagačka metoda tip hvata:
Popuni2.popuni(Adapter.adapterKolekcije(nosilac),
SMlekom.class, 2);
584 Misliti na Javi
for(Kafa c: nosilac)
print(c);
print("................. ...... ");
// Upotreba prilagođene klase:
ImaAddJednostavanRedZaCekanje<Kafa> redKafa =
new ImaAddJednostavanRedZaCekanje<Kafa>();
Popuni2.popuni(redKafa, Moka.class, 4);
Popuni2.popuni(redKafa, SMlekom.class, 1);
for(Kafa c: redKafa)
print(c);
}
} /* Ispis:
Kafa 0
Kafa 1
Kafa 2
SMlekom 3
SMlekom 4
Moka 5
Moka 6
Moka 7
Moka 8
SMlekom 9
* ///:-
" Za njih se upotreb ljava i im e funktori la ću koristiti te rm in funkcijski objekat, p ošto „funktor" ima
specifično i d ru g o značenje u inatem atici.
586 Misliti na Javi
import java.util.*;
import static net.mindview.util .Print.*;
for(T t : sekv)
if(pred.test(t))
rezultat.add(t);
return rezultat;
}
// Da bismo mogli da koristimo gornje generičke metode,
// moramo napraviti funkcijske objekte za prilagođavanje metoda
// našim specifičnim potrebama:
static class SabiracCelih implements Kombinator<Integer> {
public Integer objedini(Integer x, Integer y) {
return x + y;
}
}
static class
OduzimacCelih implements Kombinator<Integer> {
public Integer objedini(Integer x, Integer y) {
return x - y;
}
}
static class
SabiracVelikihDecimalnih implements Kombinator<BigDecimal> {
public BigDecimal objedini(BigDecimal x, BigDecimal y) {
return x.add(y);
}
}
static class
SabiracVelikihCelih implements Kombinator<BigInteger> {
public Biglnteger objedini(Biglnteger x, Biglnteger y) {
return x.add(y);
}
}
static class
SabiracAtomicLong implements Kombinator<AtomicLong> {
public AtomicLong objedini(AtomicLong x, AtomicLong y) {
// Nisam siguran da ovo ima smisla:
return new AtomicLong(x.addAndGet(y.get()));
}
}
// Možemo napraviti čak i UnarnuFunkciju s metodom "ulp”
// (Units in the last place, jedinica na poslednjem mestu):
static class VelikiDecimalniUlp
implements UnarnaFunkcija<BigDecimal,BigDecimal> {
public BigDecimal funkcija(BigDecimal x) {
return x.ulp();
}
}
static class VeceOd<T extends Comparable<T»
implements UnarniPredikat<T> {
private T granica;
public VeceOd(T granica) { this.granica = granica; }
588 Misliti na Javi
print(forEach(li,
new KolektorMnoziCele()).rezultat());
print(filtar(lvd,
new VeceOd<BigDecimal>(new BigDecimal(3))));
P o činjem defin isanjem interfejsa za različite tipove fu nkcijskih objek ata. N jih sam
prav io p o p o treb i, kako sam razvijao različite m eto d e i u v iđ ao p o tre b u za svakim . Klasu
K om binator je p red lo žio a n o n im n i k o m e n ta to r jedn og o d članaka ob jav ljenih n a m ojoj
W eb lokaciji. Kombinator a p stra h u je k o n k retn e detalje sa b iran ja dva o b je k ta i k azuje
sam o da o n i bivaju nekako o b jed injen i. Kao rezultat, m ožete videti da SabiracCelih i
OduzimacCelih m o g u biti tipovi n ad tip a Kombinator.
UnarnaFunkcija p rim a sam o jedan a rg u m e n t i daje rezultat; a rg u m e n t i rez u lta t ne
m o ra ju biti istog tipa. Kolektor se upotreb ljav a kao „ p a ra m e ta r kolekcije", a rezu ltat
m ožete da izdvojite kada završite. U narniPredikat daje rezu ltat tip a boolean. M o g u se
d efinisati funkcijski o bjekti i d ru g ih tipova, ali ovo je bilo d o v o ljn o za p o u k u .
Klasa Funkcional sadrži više g eneričkih m eto d a koje funkcijske o b jekte p rim e n ju ju na
sekvence. red u k u j( ) p rim e n ju je funkciju u o b je k tu tipa Kom binator n a svaki elem en t
sekvence da bi proizvela sam o jed a n rezultat.
fo rE ach ( ) p rim a Kolektor i p rim en ju je svoju fun kciju na svaki elem en t, z a n e m a ru -
ju ći re z u lta t svakog poziva funkcije. N ju m ožete p ozivati sam o zbog sp o re d n o g efekta (to
ne bi bio fu n k cio n a ln i stil p ro g ram iran ja, ali ipak m ože da po služi), ili Kolektor m ože da
o d ržava in te rn o stan je d a bi p o stao p a ra m e ta r kolekcije, kao što je slučaj u ov o m p rim e ru .
tran sfo rm isi( ) pravi listu pozivanjem funkcijskog o bjekta U narnaFunkcija za svaki
o b jek at u sekvenci i h v atan jem rezultata.
590 Misliti naJavi
Pročitajte i ovo
U vodni d o k u m e n t za g eneričke tip o v e je Gcnerics in thc java Progratnming Langttage, čiji
je a u to r G ilad B racha, a nalazi se na lokaciji h ttp://iava.fuii.coni/j2se/l.5/pdf/gcucrics-tti-
torial.pdf
Jara Gerterics FAQs A ngelike L anger veo m a je k o ristan resurs, na lokaciji www.lan-
ger.camelot.dc/GenericsFAQ/JavaGencricsFAQ.htnil.
Više o d žo k ersk im a rg u m e n tim a saznaćete iz teksta A dding VVildcards to thc Java Pro-
gram tning Language, čiji su a u to ri T orgerson, E rnst, H ansen, Von d e r A he, B racha i Gaf-
ter. Č lan ak je d o stu p a n na lokaciji w ww.iot.lin/issucs/isstie_2004_12/articlc5.
Rešenja odabranih vežbi data su u elektronskom dokum entu l'lic Thiitking in lciva Annotatctl Soltt-
tion Guidekoji se može kupiti na lokaciji wmv.MiiidVicw.coiu.
Nizovi
N a kraju poglavlja Inicijalizacija i čišćenje objašnjeno j e kako :,c definiše i in icija lizu je niz.
/ / : nizovi/PoredjenjeSKontejnerima.java
import java.uti1 .
import static n e t ,mindview.uti1 .Print.*;
594 Misliti na Javi
class SferaOdBeri1ijuma {
private static long brojac;
private final long id = brojac++;
public String toStringO { return "Sfera " + id; }
}
List<SferaOdBerilijuma> listaSfera =
new ArrayList<SferaOdBerilijuma>();
for(int i = 0 ; i < 5 ; i++)
listaSfera.add(new SferaOdBerilijumaO);
print(li staSfera);
print(listaSfera.get(4));
int[] celiBrojevi = { 0, 1, 2, 3, 4, 5 };
print(Arrays.toString(celiBrojevi));
print(cel iBrojevi [4]);
U Javi se granice p roveravaju bez o b zira na to da li ko ristite niz ili k o n tejn er; p riv id n o
je jed in a razlika m eđ u n jim a to što nizovi im aju o p e ra to r [ ] za p ristu p a n je e lem en tim a,
a liste im aju m eto de kao što su a d d () i g et(). S ličnost i/m e d u nizova i klase A rra y L ist na-
m ern a je, da bi bilo lako k o ristiti i je d n e i d ru g e. Ali kao što ste videii u pogiavlju Čuvanje
objekata, k o n te jn e ri su m n o g o fu n k cio n aln iji o d nizova.
Poglavlje 16: Nizovi 595
pa m ožete da u tv rd ite da li o d red en i eiem en t niza sadrži o b jek at tako što p ro v erite da li je
njegova v red n o st null. Slično to m e, niz p ro stih tipova se a u to m atsk i inicijalizuje vred-
n o šć u n u la za n u m eričk e tipove, (char)O za tip char, o d n o sn o false za tip boolean.
N iz c pokazuje kako se pravi objekat niza, n ak on čega sledi dod ela o bjekata klase
SferaOdBerilijuina svim njegovim elem entim a. Niz d p o k azu je sintaksu agregatne inicija-
lizacije koja om ogu ću je pravljenje objekta niza (im plicitno , p o m o ću o p erato ra new, kao za
niz c) i njegovo inicijalizovanje o b jektim a klase SferaOdBerilijuma, sve u je d n o j naredbi.
Inicijalizacija sledećeg niza se m ože o zn ačiti kao „d in am ič k a ag reg atn a inicijalizacija".
A gregatna inicijalizacija koja je u p o tre b lje n a za n iz d m o ra se k o ristiti p rilik o m definicije
niza, ali p o m o ć u d ru g e vrste zapisa m o g u će je n ap rav iti i inicijalizovati niz b ilo gde. Na
p rim er, p re tp o sta v im o da m eto d a sakrij() k o risti niz o b jek ata klase SferaOdBerilijuma.
M ogli biste je pozvati na sledeći način:
sakrij(d);
a=d;
Kao p rim er, po g led ajm o kako se v raća niz e lem en ata tip a S trin g :
//: nizovi/Sladoled.java
// Vraćanje nizova iz metoda.
import java.uti1
M etoda skupUkusa() pravi niz elem en a ta tipa String nazvan rezultati. D užin a niza je
od re đ en a a rg u m e n to m koji se p ro sleđ u je m eto d i. N akon toga, n asu m ičn o se biraju ukusi
iz niza UKUSI i stavljaju u niz rezultati koji se vraća kao rezultat m eto d e. V raćanje niza
je isto kao vraćanje bilo kog o b jek ta - rad i se o referenci. Nije v ažno da li je niz napravljen
u n u ta r m eto d e skupUkusa() ili na bilo k o m d ru g o m m estu. S kupljač sm eća se b rin e o
b risan ju niza kada se završi ra d s n jim , tj. niz p osto ji sve d o k vam je p o treb an .
Poglavlje 16: Nizovi 599
Uzgred, dok skupUkusa() nasum ično bira ukuse, obezbeđuje da isti elem ent ne bude
izabran dvaput. To se radi u petlji do koja bira nasum ično sve d ok ne pronađe neku vred-
nost što se još ne nalazi u nizu izabran. (Naravno, moglo je da se obavi i poređenje vred-
nosti objekata klase String da bi se ustanovilo da li se izabrani elem ent već nalazi u nizu
rezultati.) Ako pronađe odgovarajući element, dodaje nov ukus i traži sledeći (i se pove-
ćava za jedan).
Iz rezultata vidite da skupUkusa() svaki p u t bira ukuse slučajnim redosledom.
Vežba 2: (1) Napišite m etodu koja prim a int argum ent i vraća niz te veličine, popunjen
objektim a SferaOdBerilijuma.
Višedimenzionalni nizovi
Lako je praviti višedim enzionalne nizove. Za višedim enzionalni niz prostih tipova, svaki
vektor niza pišete u n u tar vitičastih zagrada:
//: nizovi/TriDPomocuNew.java
import java.util.*;
//: nizovi/NepravilanNiz.java
import j a v a . u t i l .*;
Prvi new pravi niz čiji je prvi elem ent nasum ično odabrane dužine, a ostali elem enti
neodređene dužine. Drugi new unutrašnje petlje for popunjava elemente, ali treći indeks
ostavlja kao neodređen do trećeg new.
Na isti način m ožete praviti nizove neprostih objekata. Ovde ćete videti kako sm o više
new izraza okupili u n u tar vitičastih zagrada:
} /* Ispis:
[[Sfera 0, Sfera 1], [Sfera 2, Sfera 3, Sfera 4, Sfera 5], [Sfera 6,
Sfera 7, Sfera 8, Sfera 9, Sfera 10, Sfera 11, Sfera 12, Sfera 13]]
* ///:-
Vidite da su i sfere nepravilan niz, u kojem su dužine svih lista objekata različite.
Autom atsko pakovanje radi i sa inicijalizatorim a nizova:
//: ni zo vi/AutomatskoPakovanjeNizova.java
import ja v a . u t i l .*;
//: nizovi/SastavljanjeVisedimenzionalnihNizova.java
// Pravljenje višedimenzionalnih nizova.
import java.uti1 .*;
//: nizovi/VisedimNizOmotaca.java
// Višedimenzionalni nizovi "omotačkih" objekata.
import j a v a . u t i l .*;
I opet, u Integer i Double nizovima, autom atsko pakovanje Jave SE5 pravi om otačke
objekte um esto nas.
Vežba 3: (4) Napišite m etodu koja pravi i inicijalizuje dvodim enzionalni niz tipa double.
Veličinu niza ocireduju argum enti m etode, a inicijalizađone vrednosti su date opsegom
o dredenim početnom i završnom vrednošću koje su i argum enti te metode. Napravite
drugu m etodu koja ispisuje niz generisan prvom m etodom . U m etodi main() testirajte
m etode pravljenjem i ispisivanjem nekoliko nizova različitih veličina.
Vežba 4: (2) Ponovite p rethodnu vežbu za trodim enzionalni niz.
Vežba 5: (1) Pokažite da se višedim enzionalni nizovi neprostih objekata autom atski ini-
cijalizuju na null.
Vežba 6: (1) Napišite m etodu koja prim a dva int argum enta koji pokazuju odgovarajuće
dim enzije 2-D niza. M etoda treba da napravi i popuni 2-D niz SferaOdBerilijuma u skla-
du sa argum entim a dimenzija.
Vežba 7: (1) Ponovite p rethodnu vežbu za 3-D niz.
Poglavlje 16: Nizovi 603
Brisanjem se uklanja podatak o param etru tipa, a niz m o ra da zna tačan tip koji sadrži,
da bi rad s tipovim a proveravanjem učinio bezbednim .
M eđutim , m ožete param etrizovati tip sam og niza:
//: nizovi/ParametrizovanTipNiza.java
class ClassParametar<T> {
public T[] f ( T [] arg) { return arg; }
}
class ParametarMetode {
public static <T> T[] f(T[] arg) { return arg; }
}
Vodite računa o pogodnosti upotrebe param etrizovane m etode um esto param etrizo-
vane klase: ne m orate da pravite instancu klase s param etrom za sve različite tipove na
koje ćete je prim eniti, i možete da je napravite statičnom . N aravno, ne m ožete uvek da
upotrebite param etrizovanu m etodu um esto param etrizovane klase, ali to bi m oglo da
bude bolje rešenje.
Nije najtačnije reći da je nem oguće napraviti niz generičkog tipa. Istina je da prevodi-
lac neće dozvoliti pravljenje instance niza generičkog tipa. M eđutim , dopustiće da napra-
vite referencu na taj niz. Na primer:
List<String>[] ls;
604 Misliti na Javi
Ovo će proći kroz prevodilac bez problem a. I m ada ne možete napraviti objekat niza koji
sadrži generički tip, možete napraviti niz negeneričkog tipa i eksplicitno ga konvertovati:
Č im im ate referencu na L ist< S tring > [], neke provere će se obaviti u vreme pre-
vođenja. Problem je to što su nizovi kovarijantni, pa je niz L ist< S tring> [] istovrem eno i
niz O bject]]. To možete upotrebiti za dodeljivanje liste A rrayL ist< Integer> svom nizu, a
da ne izazovete grešku ni u vreme prevođenja ni u vrem e izvršavanja.
Ukoliko znate da nećete svoditi naviše i potrebe su vam relativno jednostavne, lako ćete
napraviti niz generičkih tipova koji će biti elem entarno proveravan u vreme prevođenja.
M eđutim gotovo uvek je bolje izabrati generički kontejner nego niz generičkih tipova.
Po pravilu, videćete da su generički tipovi delotvorni na granicam a klase ili metode.
U njenoj unutrašnjosti, brisanje čini generički tip neupotrebljivim . Stoga ne možete, na
prim er, napraviti niz generičkog tipa:
/ / : nizovi/NizGenerickogTipa.java
// Java neće da prevede nizove generičkih tipova.
Arrays.fill()
Standardna klasa A rrays u Javi im a svoju m etodu fill(), ali je ona prilično prosta - samo
kopira jednu vrednost u svaki elem ent niza, a ako se radi o objektim a, kopira istu referen-
cu u svaku lokaciju. Evo prim era:
//: nizovi/PopunjavanjeNizova.java
// Upotreba metode Arrays.fill()
import ja v a . u t i 1
import static n e t . m i n d v i e w . u t i l .Print.*;
Možete da popunite ceo niz ili, kao što pokazuju poslednje naredbe, nekoliko eleme-
nata niza. M eđutim , pošto m etodom Arrays.fill() m ožete da popunite niz samo jednom
vrednošću, rezultati nisu naročito upotrebljivi.
Generatori podataka
Za pravljenje zanimljivijih nizova podataka, ali na fleksibilan način, upotrebićem o kon-
cept G en erato ra predstavljen u poglavlju Generički tipovi. Ako neka alatka upotrebljava
G enerator, napravljeni podaci se menjaju u zavisnosti od izabranog G en erato ra (to je
Poglavjje 16: Nizovi 607
lako to nije baš sasvim jasno. M oglo bi sc tvrditi i da G e n e ra to r predstavlja obrazac C otntnand (Ko
m anda). M edu tim , ja sm atram da je zadatak p o p u n iti niz i da G e n e ra to r obavlja deo tog zadatka, pa
irii to više liči na strategiju nego na kom andu.
608 Misliti na Javi
baf[i] = cg.next();
return new java .l an g. St ri ng (b af);
}
}
public static class
Short implements Generator<java.lang.Short> {
private short vrednost = 0;
public java.lang.Short next() { return vrednost++; }
}
public static class
Integer implements Ge ne rator<java.1ang.Integer> {
private int vrednost = 0;
public java.lang.Integer next() { return vrednost++; }
}
public static class
Long implements Generator<java.lang.Long> {
private long vrednost = 0;
public java.lang.Long next() { return vrednost++; }
}
public static class
Float implements Generator<java.lang.Float> {
private float vrednost = 0;
public java.lang.Float next() {
float rezultat = vrednost;
vrednost += 1.0;
return rezultat;
}
}
public static class
Double implements Genera to r< ja va .1ang.Double> {
private double vrednost = 0.0;
public java.lang.Double next() {
double rezultat = vrednost;
vrednost += 1.0;
return rezultat;
}
}
} ///:-
Svaka klasa realizuje neko značenje pojm a kolićina. U slučaju klase GeneratorDa-
teKoIicine.Character, radi se o velikim i m alim slovima koja se ponavljaju. Klasa Gene-
ratorD ateK olicine.String upotrebljava GeneratorDateKolicine.Character za
popunjavanje niza znakova, koje zatim pretvara u String. Veličinu niza određuje argu-
m ent konstruktora. O bratite pažnju na to da GeneratorDateKolicine.String upotreblja-
va elem entarni Generator<java.lang.Character> um esto specifične reference na
GeneratorDateKolicine.Character. Kasnije taj generator može zam eniti RandoinGene-
rator.String iz paketa RandomGenerator.java.
Poglav[je 16: Nizovi 609
//: nizovi/TestiranjeGeneratora.java
import net.mindview.util.*;
Ovde se pretpostavlja da klasa koja se ispituje sadrži skup ugnežđenih objekata tipa Ge-
nerator, od kojih svaki ima podrazumevani konstruktor (onaj bez argum enata). Sve ug-
nežđene klase proizvodi reflektivna metoda getClasses(). Zatim metoda test() pravi instancu
svakog od tih generatora i ispisuje rezultat koji se dobija s deset poziva m etode next().
Evo skupa G eneratora koji upotrebljavaju generator slučajnih brojeva. Pošto se kon-
stru k to r klase R andom inicijalizuje konstantom , rezultat se ponavlja svaki p u t kada po-
m oću jednog od ovih G eneratora pozovete program :
//: net/mindview/util/GeneratorSlucajnih.java
// Generatori koji daju slučajne vr e d n o s t i .
package net.mindview.util;
import j a v a . u t i l .*;
610 Misliti na Javi
//: ni zo vi /TestiranjeGeneratoraSlucajnih.java
import net.mindview.util.*;
//: net/mindview/util/Generisani.java
package net.mindview.util;
import java.util
D efiniđja klase C ollectionD ata navedena je u poglavlju D etaljno razm atranje kontej-
nera. O na pravi Collection objekat popunjen elem entim a koje je napravio G en erato r
gen. Broj tih elemenata određuje drugi argum ent konstruktora. Svi podtipovi klase Col-
lection (kontejneri) imaju m etodu toA rray() koja elem entim a kontejnera popunjava niz
zađat argum entom .
Druga m etoda upotrebljava refleksiju za dinam ičko pravljenje novog niza odgovara-
jućeg tipa i veličine. On se zatim popunjava isto kao 11 prvoj m etodi.
Klasu G enerisani m ožemo ispitati nekom od klasa G en eratorD ateK olicine definisa-
nih u prethodnom odeljku:
//: ni zo vi /T estGenerisanih.java
import j a v a . u t i l .*;
import net.mindview.util.*;
Poglavlje 16: Nizovi 613
rezultat[i] = ul a z [ i ] ;
Evo prim era iz kojega možete videti kako se klasa K onvertujU upotrebljava sa obe ver-
zije m etode G enerisani.array():
S y st em .o ut .println(Arrays.toString(b));
boolean[] c = KonvertujU.prost(
Generisani.niz(Boolean.class,
new GeneratorDateK ol ic ine .B oo le an (), 7));
S y st em .o ut.pri ntln (A rr ay s.toStri n g (c ));
}
} /* Ispis:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
[true, false, true, false, true, false, true]
* ///:-
Najzad, ovo je program za testiranje alatki za generisanje nizova p om oću klasa G ene-
ratorSlucajnili:
//: nizovi/TestGenerisanjaNizova.java
// Testiranje alatki koje nizove popunjavaju generatorima.
import ja v a . u t i l .*;
import ne t. mi nd vi ew .u ti l.*;
import static ne t. mi nd vi ew .u ti l.Print.*;
} /* Ispis:
al = [true, false, true, false, false, true]
a2 = [104, -79, -76, 126, 33, -64]
a3 = [Z, n, T , c, Q, r]
a4 = [-13408, 22612, 15401, 15161, -28466, -12603]
a5 = [7704, 7383, 7706, 575, 8410, 6342]
a6 = [7674, 8804, 8950, 7826, 4322, 896]
a7 = [0.01, 0.2, 0.4, 0.79, 0.27, 0.45]
a8 = [0.16, 0.87, 0.7, 0.66, 0.87, 0.59]
* ///:-
T im e se obezbeđuje i da sve verzije m etode KonvertujU.prost() ispravno rade.
Vežba 11: (2) Pokažite da autom atsko raspakivanje ne radi s nizovima.
Vežba 12: (1) Napravite inicijalizovan niz tipa double pom oću klase GeneratorDateKo-
licine. Ispišite rezultate.
Vežba 13: (2) Popunite objekat tipa String pom oću klase GeneratorDateKolicine.Cha-
racter.
Vežba 14: (6) N apravite po jedan niz svakog od prostih tipova, zatim ih popunite pom oću
klase GeneratorDateKoIicine. Ispišite svaki niz.
Vežba 15: (2) Izm enite PoredjenjeSKontejnerima.java tako što ćete napraviti Generator
za objekte tipa SferaOdBerilijuma, i prepravite m etodu main() tako da taj Generator
upotrebi za Generisani.array().
Vežba 16: (3) Na osnovu program a GeneratorDateKolicine.java, napravite klasu
PreskociGenerator koja nove vrednosti proizvodi povećavanjem za 1, u skladu s argu-
m entom konstruktora. Prepravite program TestGenerisanjaNizova.java tako da pokaže
da vaša nova klasa radi ispravno.
Vežba 17: (5) N apravite i ispitajte Generator za tip BigDecimal, i obezbedite da radi s
m etodam a klase Generisani.
Kopiranje niza
S tandarđna biblioteka u favi obezbeđuje statičku m etodu System.arraycopy() pom oću
koje se niz m nogo brže kopira nego kada se koristi petlja for za ručno kopiranje.
System.arraycopy() je preklopljena i radi s nizovima svih tipova podataka. Evo prim era
kako ova m etoda radi s nizovim a tipa int:
//: ni zovi/KopiranjeNizova.java
// Upotreba metode System.arraycopy()
import java.util
import static ne t. mi nd vi ew .u ti l.Print.*;
A rgum enti m etode arraycopy() su izvorni niz, pom eraj u izvornom nizu od koga tre-
ba započeti kopiranje, odredišni niz, pom eraj u odredišnom nizu od koga treba da počne
kopiranje i broj elem enata za kopiranje. P rirodno, svako prekoračenje granica niza
prouzrokovaće izuzetak.
Prim er pokazuje da se m ogu kopirati i nizovi prostih tipova i nizovi objekata. M eđu-
tim , ako kopirate nizove objekata, kopiraju se sam o reference, ali ne i sami objekti. To se
zove površno kopiranje (engl. shallow copy, videti dodatke knjige na Webu).
M etoda System.arraycopy() ne obavlja autom atsko pakovanje niti autom atsko raspa-
kivanje - oba niza m oraju biti tačno istog tipa.
Vežba 18: (3) N apravite i popunite niz objekata tipa SferaOdBerilijuma. Kopirajte taj niz
u drugi, nov niz i pokažite da je to površna kopija.
Poređenje nizova
Klasa Arrays nudi preklopljene verzije m etode equals() za poređenje jednakosti dva niza.
I ova m etoda postoji za nizove svih prostih tipova i tipa Object. Da bi bili jednaki, nizovi
m oraju da im aju isti broj elem enata, i svaki elem ent m ora da bude jednak odgovarajućem
elem entu drugog niza, pri čem u se m etoda equals () poziva za svaki element. (Za proste
tipove se koristi m etoda equals() om otačke klase, npr. Integer.equals() za tip int.) Evo
prim era:
//: nizovi/PoredjenjeNizova.java
// Korišćenje metode Arrays.equals()
import java.util
import static ne t. m i n d v i e w . u t i l .Print.*;
//: nizovi/UporedivTip.java
// Primena interfejsa Comparable.
import java.util
import net.mindview.util.*;
import static n e t. mi nd vi ew .u ti l.Print.*;
public class UporedivTip implements Comparable {
i nt i ;
int j ;
2 Desij’tt Patteriis, E riđ i G am m a i dr. (A đđison- Wesley, 1995). Videti Thittking in Patterns (w ith Java)
na lokaciji w w w .M indV iew .net.
620 Misliti na Javi
Kađa definišete funkciju poređenja, sam i definišete šta znači uporediti jedan objekat
vaše klase s drugim . Ovde se za poredenje koriste sam o vrednosti polja i, a polje j se ne
uzima u obzir.
Poglavjje 16: Nizovi 621
M etoda generator() vraća objekat koji realizuje interfejt. Generator, tako što pravi
ano nim nu unutrašnju klasu. Na taj način, inicijalizacijom slučajnim vrednostim a, prave
se objekti klase UporedivTip. U m etodi m ain(), za popunjavanje niza elem enata tipa
UporedivTip koristi se generator, a niz se p o to m uređuje. D a interfejs Com parable nije
bio realizovan, pojavila bi se greška CIassCastException u vrem e prevođenja prilikom
pokušaja pozivanja m etode sort(). To bi se desilo zato što m etoda sort() tip svog argu-
m enta konvertuje u Comparable.
Pretpostavim o da radite s klasom koja ne realizuje interfejs Comparable, ili da klasa
realizuje taj interfejs, ali vam se ne sviđa kako on a radi i za određeni tip podataka radije bi-
ste koristili neku drugu funkciju za poređenje. D a biste to postigli, m orate da prim enite
drugi pristup za poređenje objekata. Treba da napravite posebnu klasu koja realizuje in-
terfejs C om parator (ukratko pređstavljen u poglavlju Č uvanje objekata). Ovo je p rim er
projektnog obrasca Strategy. C om parator im a dve m etode, compare() i equals(). M eđu-
tim , equals() se prim enjuje (realizuje) sam o kada su po treb n e specijalne perform anse, jer
se pri svakom pravljenju klase ona autom atski nasleđuje iz klase Object koja im a svoju
m etodu equals(). Znači, m ožete da koristite stand ardn u m eto d u equals() klase Object i
da zadovoljite uslove koje nam eče interfejs.
Klasa Collections (njom e ćem o se pozabaviti u n arednom poglavlju) sadrži m etodu
reverseOreder(); m etoda pravi C om parator koji obrće p riro d an redosled uređivanja. To
se jednostavno prim enjuje na našu klasu UporedivTip:
//: nizovi/ObrnutiRedosled.java
// Prikazuje Co l 1ec tio n s .r e v e rs eO rd er ().
import ja v a . u t i 1.*;
import net.mindview.util.*;
import static n e t . mi nd vi ew .u ti l.Print.*;
public class ObrnutiRedosled {
public static void main(String[] args) {
UporedivTip[] a = Generisan.niz(
new U p or ed iv Ti p[ 12 ], U p o r e d i v T i p. ge ne ra tor () );
print("pre uredivanja: ");
pr in t( Ar ra ys .t oS tr ing (a ));
Arrays.sort(a, Col 1 e c t i o n s .re ve rs eO rd er ());
print("nakon uređivanja: ");
pri n t ( A rr ay s.toStri ng (a ));
)
} /* Ispis:
pre uređivanja:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7 ] , [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
nakon uređivanja:
[ [i = 98, j = 61], [i = 93, j = 61], [i = 88, j = 28]
, [i = 68, j = 0], [i = 61, j = 29], [i = 58, j = 55]
, [i = 51, j = 89], [i = 22, j = 7], [i = 20, j = 58]
, [i = 16, j = 40], [i = 11, j = 22], [i = 9, j = 78]
]
* ///•-
622 Misliti na Javi
Možete napisati i sopstveni Com parator. Sledeći Com parator poredi objekte tipa
UporedivTip po vrednosti njihovih polja j, a ne po vrednosti polja i:
//: nizovi/PrimerZaComparator.java
// Primena interfejsa Comparator za neku klasu.
import j a v a . u t i l .*;
import net.mindview.util.*;
import static n e t . mi nd vi ew .u ti l.Print.*;
V ežba21: (3) P okušajteda uredite nizobjekata iz vežbe 18. Prim enite C o m p arab le da bi-
ste rešili problem . Potom napravite C o m p arato r za sortiranje objekata obrn u tim
redosledom.
Poglavlje 16: Nizovi 623
Uređivanje niza
Pom oću ugrađenih m etoda možete da uredite proizvoljan niz elem enata prostog tipa, od-
nosno bilo koji niz objekata koji realizuju C o m parab le ili im aju pridruženi C o m p ara-
to r.3 Evo prim era kojim se generišu, a potom i sortiraju, slučajni objekti tipa String:
//: nizovi/UredjivanjeTipaString.java
// Uredjivanje niza elemenata tipa String.
import j a v a . u t i l .*;
import net.mindview.util.*;
import static net.mind vi ew .u ti l.Print.*;
public class UredjivanjeTipaString{
public static void main(String[] args) {
S t r i n g [] sa = Generisani .niz(new String[20],
new GeneratorSlu ca jn ih .St ri ng (5 ));
print("Pre uređivanja: ", A r r a y s .t oS tr in g( sa ));
A r r a y s .s or t( sa );
Arrays2.print("Nakon uređivanja: ", A r ra ys .t oS tr in g( sa ));
Arrays.sort(sa, C o l l e c t i o n s . r e v e r s e O r d e r O ) ;
print("Uređivanje obrnutim redom: " + Arra ys .t oS tr in g( sa ));
Arrays.sort(sa, St ri ng .C AS E_ IN SE NS ITI VE _O RD ER );
print("Uredivanje bez obzira na velićinu slova: " +
Arrays .t oS tr in g( sa ));
}
} /* Ispis:
Pre uređivanja: [YNzbr, nyGcF, 0WZnT, cQrGs, eGZMm, JMRoE, suEcU, OneOE,
dLsmw, HLGEa, hKcxr, EqUCB, bklna, Mesbt, WHkjU, rUkZP, gwsqP, zDyCy,
RFJQA, HxxHv]
Nakon uređivanja: [EqUCB, HLGEa, HxxHv, JMRoE, Mesbt, 0WZnT, OneOE,
RFJQA, WHkjU, YNzbr, bklna, cQrGs, dLsmw, eGZMm, gwsqP, hKcxr, nyGcF,
rUkZP, suEcU, zDyCy]
Uređivanje obrnutim redom: [zDyCy, suEcU, rUkZP, nyGcF, hKcxr, gwsqP,
eGZMm, dLsmw, cQrGs, bklna, YNzbr, WHkjU, RFJQA, OneOE, 0WZnT, Mesbt,
JMRoE, HxxHv, HLGEa, EqUCB]
Uređivanje bez obzira na veličinu slova: [bklna, cQrGs, dLsmw, eGZMm,
EqUCB, gwsqP, hKcxr, HLGEa, HxxHv, JMRoE, Mesbt, nyGcF, OneOE, 0WZnT,
RFJQA, rUkZP, suEcU, WHkjU, YNzbr, zDyCy]
* ///:-
Verovatno ćete prim etiti da je rezultat ovog algoritm a za uređivanje niza elem enata
tipa S trin g leksikografski, odnosno na početak postavlja reči koje počinju velikim slovom,
a potom slede reči koje počinju malim slovom. (Ovako se obično uređuju stavke u tele-
fonskim im enicim a.) Ako hoćete da grupišete reči abecedno, tj. bez obzira na to da ii po-
činju velikim ili malim slovom, upotrebite String.CASE_INSENSITIVE_ORDER kao u
poslednjem pozivu m etode so rt() u gornjem prim eru.
Z ačuđo, u lavi 1.0 i 1.1 nije postojala podrška za uređivanje objekata tip a S trin g .
62 4 Misliti na Javi
//: nizovi/PretrazivanjeNiza.java
// Koriscenje Ar ra y s . b i n a r y S e a r c h ( ) .
import java.util
import net.mindview.util.*;
import static riet.mindview.util .Print.*;
U petlji vvhile generišu se slučajne vrednosti za elem ente koji se traže, sve dok se jedna
vrednost ne pronađe.
M etoda A rrays.binaryS earch() vraća vrednost koja je veća od nule ili jednaka nuli ako
se pronađe traženi element. U sup ro tn om , vraća negativnu vrednost koja predstavlja me-
sto na koje bi taj elem ent trebalo da se u m etne kada bi se redosled niza ručno održavao.
Vraćena vrednost je:
Poglavlje 16: Nizovi 625
-(tačka umetanja) - 1
Tačka um etanja je indeks prvog elem enta koji je veći od traženog, odnosno a.size()
ako su svi elem enti niza m anji od traženog.
Ako niz sadrži više elem enata iste vrednosti, ne zna se koji će od njih biti pronađen. To
znači da algoritam ne podržava duplikate elem enata, već ih toleriše. Ako vam je potrebna
uređena lista bez ponovljenih elem enata, upotrebite klasu TreeSet (koja održava uređeni
redosled) ili LinkedHashSet (koja održava redosled um etanja). Ove klase se um esto vas
autom atski staraju o svim pojedinostim a. Samo u slučajevima kada vam je neophodno
poboljšanje perform ansi trebalo bi zam eniti, jednu od tih klasa nizom čiji se redosled
održava ručno.
Ako ste za uređivanje niza objekata koristili kom parator (nizovi prostih tipova se ne
m ogu uređivati p om oću kom paratora), m orate da upotrebite isti Com parator kada pre-
tražujete m etodom binarySearch() (pom oću preklopljene verzije ove funkcije). Na pri-
mer, program UređjivanjeTipaString.java m ože se prilagoditi tako da pretražuje niz:
//: nizovi/AbecednaPretraga.java
// Pretraživanje pomoću komparatora.
import java.util.*;
import net.mindview.util.*;
Sažetak
U ovom poglavlju, videli ste da Java pruža pristojnu podršku za nizove neprom enljive ve-
ličine i niskog nivoa. Takvi nizovi im aju dobre perform anse, ali ne i fleksibilnost kao u je-
zicima C i C ++. U prvoj verziji Jave, nizovi neprom enljive veličine i niskog nivoa bili su
apsolutno neophodni, ne sam o zato što su projektanti Jave (i zbog perform ansi) odlučili
da Java im a i proste tipove, nego i zato što je podrška za kontejnere u toj verziji bila mi-
nim alna. Dakle, u ranim verzijama Jave, uvek je bilo preporučljivo koristiti nizove.
U kasnijim verzijama Jave, znatno se poboljšala podrška za kontejnere, pa o ni sada za-
senjuju nizove po svemu sem po perform ansam a, a i perform anse kontejnera su u m eđu-
vrem enu znatno poboljšane. Već sm o na više m esta rekli da problem e s perform ansam a
obično ne prouzrokuje ono na šta program er sum nja.
Nakon uvođenja autom atskog pakovanja i generičkih tipova, držanje prostih tipova u
kontejnerim a postalo je lako, pa je i to razlog da nizove zam enite kontejnerim a. Nizovi
više nem aju ni tu isključivu prednost da om ogućuju bezbedan rad s tipovim a, pošto i ge-
nerički tipovi daju kontejnere čiji se tipovi autom atski proveravaju.
Rekosmo u ovom poglavlju da su generički tipovi prilično nepodesni za rad s nizovi-
ma, a i sami ćete se uveriti kada pokušate da ih upotrebite. Često se dešava da u vreme
prevođenja dobijate upozorenja unchecked čak i kada postignete da generički tipovi i ni-
zovi na neki način sarađuju (kao što ćete videti u n arednom poglavlju).
Kada se raspravljalo o konkretnim prim erim a, projektanti jezika Java u više navrata su
m i govorili da bi umesto nizova trebalo da koristim kontejnere (pom oću nizova sam po-
kazivao specifične tehnike, pa tu m ogućnost nisam im ao).
Sve navedeno pokazuje da bi „kontejnerim a trebalo da date prednost nad nizovim a“
kada program irate u novijim verzijama Jave. Tek kada se dokaže da su perform anse pro-
blem (i da će ih prelazak na nizove znatno popraviti), trebalo bi da ponovo „podelite pro-
gram na proste faktore“ i pređete na nizove.
Ovo je prilično hrabra tvrdnja, ali neki jezici uopšte nem aju nizove niskog nivoa i ne-
promenljive veličine. Imaju samo kontejnere prom enljive veličine koji su m nogo funk-
cionalniji od Javinih nizova. Prim era radi, P ython4 im a tip list čija sintaksa liči na onu
elem entarnih nizova, ali mnogo funk đon alniji - m ože se čak i naslediti:
#: nizovi/PythonoveListe.py
aLista = [1, 2, 3, 4, 5]
print type(aLista) # <type 'lista'>
print aLista # [1, 2, 3, 4, 5]
print a L i s t a [4] # 5 Indeksiranje liste
aLista.append(6) # Veličina lista je promenljiva
aLista += [7, 8] # Dodavanje jedne liste drugoj
print aLista # [1, 2, 3, 4, 5, 6, 7, 8]
deoAListe = aLista[2:4]
print deoAListe # [3, 4]
4 Videti www.Python.org.
Poglavlje 16: Nizovi 627
Videti www.plip.net.
Detaljno razmatranje kontejnera
U poglavlju Čuvanje objekata predstavili sm o Javinu biblioteku kontejnera i n jen u osnovnu
fun kcio n aln o st, što je dovoljno za početak rada s njim a. U ovom poglavlju iserpnije ćemo
istražiti ovu va žn u biblioteku.
P n t m i n n tak<«ntm m iia k n n t p i n p r n
Poglavlje 17: Detaljno razmatranje kontejnera 629
Popunjavanje kontejnera
M ada je problem sa štam panjem sadržaja kontejnera rešen, popunjavanje kontejnera ima
isti neđostatak kao java.util.Arrays. Kao u paketu Arrays, postoji prateća klasa nazvana
Collections koja sadrži statične uslužne m etode, m eđu kojim a i jednu nazvanu fill ( ).
Kao u verziji paketa Arrays, ta m etoda fill ( ) po celom kontejneru sam o duplira jednu re-
ferencu na objekat. Sem toga, ona radi sam o za List objekte, ali se proizvedena lista može
proslediti k onstruktoru ili nekoj metodi addA ll( ):
//: kontejneri/PopunjavanjeLista.java
// Metode C o l1e c t i o n s .fi11() i Collecti on s. nC op ie s().
import j a v a . u t i 1
class AdresaStringa {
private String s;
public Ad re sa St ri ng a( St ring s) { this.s = s; }
public String t o S t r i n g O {
return s u p e r . t o S t r i n g O + " " + s;
}
}
System.o ut .p ri nt ln (li st );
}
} /* Ispis: (primer)
[AdresaStringa@82ba41 Zdravo, AdresaStringa@82ba41 Zdravo,
AdresaStringa@82ba41 Zdravo, AdresaStringa@82ba41 Zdravo]
[AdresaStringa@923e30 svima!, AdresaStringa@923e30 svima!,
AdresaStringa@923e30 svima!, AdresaStringa@923e30 svima!]
* ///:-
U p rim eru su prikazana dva načina popunjavanja kontejnera (objekta tipa Collec-
tion) referencama na jedan objekat. Prvo, m etoda CoUections.nCopies( ) pravi listu koja
se prosleđuje konstruktoru; on popunjava ArrayList.
M etoda to S trin g ( ) klase A dresaStringa poziva m etodu O bject.toString( ) koja daje
im e klase i heksadecim alno prikazan ključ za heširanje (engl. hash code) tog objekta - ge-
nerisan m etodom h ashC ode( ). Iz rezultata vidite da sve reference upućuju na isti obje-
kat, a isto važi i nakon poziva druge m etode, Collections.fill( ). M etodu fill( ) još m anje
korisnom čini to što može da zam eni sam o elem ente koji su već u listi - ona ne može da
dodaje nove elemente.
Generatorsko rešenje
Gotovo svi podtipovi interfejsa Collection imaju konstruktor koji prim a drugi objekat
tipa Collection, iz kojega može da popuni nov kontejner. Dakle, da bism o sačinili podat-
ke za testiranje, sam o treba da napravim o klasu koja kao argum ente prim a konstruktore
nekog G eneratora (oni su definisani u poglavlju Generički tipovi i pobliže objašenjeni u
poglavlju N izo vi) i neki broj kolicina:
//: ne t/ mi nd vi ew /u ti1/PodaciKontejnera.java
// Kontejner koji podacima popunjava generator.
package net.mindview.util;
import j a v a . u t i l .*;
Kada se koristi pom oćna generička m etoda, sm anjuje se količina koda potrebnog za
u p otreb u klase.
Program PodaciKontejnera je prim er projektnog obrasca A da pter (Adapter);' on
prilagođava odredeni Generator konstruktoru nekog kontejnera, tj. podtipa klase Collection.
Evo prim era u kojem se inicijalizuje obiekat tipa LinkedHashSet:
//: kontejneri/PodaciZaTestKontejnera.java
import java.util
import net.mindview.util.*;
Elementi su u poretku kojim su bili um etani, pošto L inkedH ashSet održava ulančanu
listu koja zadržava redosled umetanja.
Svi generatori definisani u poglavlju N izovi sada su dostupni preko adaptera Podaci-
K outejnera. U ovom prim eru upotrebićem o dva od njih:
//: ko ntejneri/GenerisanjePodatakaKontejnera.java
// Korišćenje generatora definisanih u poglavlju Nizovi.
import j a v a . u t i l .*;
import net.mindview.util.*;
1 O vo nije stroga definicija adaptera kao ona iz knjige Projektni obrasci, ali sm a tram d a je dovoljno pre-
cizna.
632 Misliti na Javi
System.out.println(new HashSet<Integer>(
new PodaciKontejnera<Integer>(
new Rand om Ge ne ra to r. In teg er (), 10)));
}
} /* Ispis:
[YNzbrnyGc, F0WZnTcQr, GseGZMmJM, RoEsuEcllO, neOEdLsmw, HLGEahKcx,
rEqUCBbkI, naMesbtWH, kjUrUkZPg, wsqPzDyCy]
[573, 4779, 871, 4367, 6090, 7882, 2017, 8037, 3455, 299]
* ///:-
D užinu objekta tipa String koji pravi R andom G enerator.String određuje konstruktor
u argum entu.
Generatori mapa
Na isti način m ožem o napraviti m apu, ali za nju nam treba klasa Par, pošto se za svaki po-
ziv Generatorove m etode n e x t( ) m o ra napraviti par objekata (jedan ključ i jedna vred-
nost) za popunjavanje mape:
//: net/mindview/util/Par.java
package net.mindview.util;
}
// Dva zasebna Generatora:
public Po daciIzMape(Generator<K> genK, Ge nerator<V> genV,
int kolicina) {
for(int i = 0 ; i < kolicina; i++) {
p u t ( g e nK .n ex t( ), g e n V . n e x t ( ) ) ;
}
}
// Generator za ključ i jedna vrednost:
public PodaciIzMape(Generator<K> genK, V vrednost, int kolicina){
for(int i = 0; i < kolicina; i++) {
p u t ( g e nK .n ex t( ), vrednost);
}
}
// Objekat tipa Iterable i Ge nerator vrednosti:
public Po da ciIzMape(Iterable<K> genK, Genera to r< V> genV) {
for(K kljuc : genK) {
put(kljuc, g e n V . n e x t ( ) ) ;
}
}
// Objekat tipa Iterable i jedna vrednost:
public PodaciIzMape(Iterable<K> genK, V vrednost) {
for(K kljuc : genK) {
put(kljuc, vrednost);
}
}
// Generičke pomoćne metode:
public static <K,V> PodaciIzMape<K,V>
m a p ( G e n e r a t o r < P a r < K , V » gen, int kolicina) {
return new PodaciIzMape<K,V>(gen, kolicina);
}
public static <K,V> PodaciIzMape<K,V>
ma p( Generator<K> genK, Generator<V> genV, int kolicina) {
return new P o d a c i IzMape<K,V>(genK, genV, kolicina);
}
public static <K,V> P o d a c i IzMape<K,V>
map(Generator<K> genK, V vrednost, int kolicina) {
return new PodaciIzMape<K,V>(genK, vrednost, kolicina);
}
public static <K,V> PodaciIzMape<K,V>
ma p( Iterable<K> genK, Ge nerator<V> genV) {
return new PodaciIzMape<K,V>(genK, genV);
}
public static <K,V> PodaciIzMape<K,V>
map(Iterable<K> genK, V vrednost) {
return new PodaciIzMape<K,V>(genK, vrednost);
}
} III--
63 4 Misliti na Javi
Program pruža izbor - može se koristiti: jedan objekat tipa G en erato r< P ar< K ,V » ,
dva zasebna Generatora, jedan G enerator i jedna konstan tn a vrednost, jedan objekat
tipa Iterable (koji obuhvata svaki objekat tipa Collection) i jedan Generator, ili objekat
tipa Iterable i jedna vrednost. Pom oćne generičke m etode sm anjuju količinu koda po-
treb n u za pravljenje objekta tipa PodacilzMape.
Evo prim era korišćenja program a PodacilzMape. I G enerator klase Slova realizuje in-
terfejs Iterable pravljenjem Iteratora; zato se m ože upotrebiti za testiranje m etoda
PodaciIzM ape.m ap( ) koje rade sa svim objektim a koji realizuju Iterable:
//: kontejneri/TestiranjePodatakaMape.java
import j a v a . u t i l .*;
import net.mindview.util.*;
import static ne t. mi nd vi ew .u ti l.Print.*;
D vodim enzionalni niz PODACI tipa S trin g je javan, pa se može upotrebljavati i drug-
de. FIyw eightM apa m ora realizovati m etodu e n try S e t() koja zahteva nam ensku realiza-
ciju i klase Set i klase M ap.Entry. Uloga zamajca je u tom e što svaki objekat tipa
Poglavlje 17: Detaljno razmatranje kontejnera 641
M ap.Entry jednostavno skladišti sam o svoj indeks, a ne stvarni ključ i vrednost. Kada po-
zovete m etodu getKey( ) ili getV alue( ), ona će vam vratiti odgovarajući elem ent PODA-
CI određen pom oću tog indeksa. Klasa SkupStavki se stara da njena velicina ne bude
veća od niza PODACI.
D rugi deo zamajca realizovan je u program u SkupStavki.Iterator. Umesto da se za sva-
ki par u nizu PODACI pravi objekat tipa Map.Entry, pravi se samo jedna stavka m ape
(Map.Entry) za svaki iterator. O bjekat Stavka se upotrebljava kao prozor u podatke; on
sadrži sam o indeks statičnog niza znakovnih nizova. Svaki p u t kada pozovete m etodu
n e x t( ) za taj iterator, indeks u objektu Stavka povećava se za 1, tako da pokazuje na sledeći
par elemenata, i potom se jedini objekat tipa Stavka tog Iteratora vraća iz m etode next( ).3
M etoda select( ) pravi objekat tipa FlyweightMapa koji sadrži SkupStavki željene veliči-
ne, a ovaj se koristi u m etodam a glavni grado vi( ) i im en a( ) pokazanim u metodi m a in ( ).
Za neka ispitivanja, ograničena veličina klase C ountries predstavlja problem . Na isti
način m ožem o napraviti inicijalizovane nam enske kontejnere koji im aju skup podataka
proizvoljne veličine. Sledeća klasa je lista proizvoljne veličine koju (zapravo) unapred ini-
cijalizujemo Integer podacim a:
//: net/mindview/uti1/BrojackaListaIntegera.java
// Lista proizvoljne dužine s probnim podacima.
package net.mindview.util;
import java.util
20 ,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
Mape u pakctu java.util prave g ru p n e kopije m eto d am a g etK ey () i g etV a lu e() za Mape, pa ovo radi.
Kada bi nam enska m apa sam o kopirala celu Map.Entry, ovakav p ristu p bi prouzrokovao problem .
642 Misliti na Javi
Evo jedne m ape koja sadrži unapred inicijalizovane jedinstvene objekte tipa In teg er i
String; i ona može biti proizvoljne veličine:
//: net/mindview/util/BrojackaMapaPodataka.java
// Mapa neograničene dužine s probnim podacima.
package net.mindview.util;
import java.util
} / * I s p is :
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8= 10 , 9 = J0 , 10=K0,
11 =L0, 12=M0, 13=N0, 14=00, 15=P0, 16=Q0, 17=R0, 18=S0, 19=T0, 20=U0,
21=V0, 22=W0, 23=X0, 24=Y0, 25=Z0, 26=A1, 27=B1, 28=C1, 29=D1, 30=E1,
31=F1, 32=G1, 33=H1, 34= 11, 35 = J1, 36=K1, 37=L1, 38=M1, 39=N1, 40= 01,
41=P1, 42=Q1, 43=R1, 44=S1, 45=T1, 46=U1, 47=V1, 48=W1, 49=X1, 50=Y1,
51=Z1, 52=A2, 53=B2, 54=C2, 55= 02, 56=E2, 57=F2, 58=G2, 59=H2}
* ///:-
lterator<T> ite rato rf) Vraća lterator<T> koji se može koristiti za kretanje kroz elemente kon-
tejnera.
boolean remove(Object) Ako se argument nalazi u kontejneru, uklanja sejedna instanca tog ele-
menta. Vraća true akoje nešto uklonjeno. (Opciona metoda).
boolean removeAII) Uklanja sve elemente koje sadrži argument. Vraća true ako je nešto
Kolekcija<?>) uklonjeno. (Opciona metoda).
boolean retainAllf Zadržava samo elemente koji se nalaze u argumentu (skupovni presek).
Kolekcija<?>) Vraća true ako se nešto promenilo. (Opciona metoda).
int size( J Vraća broj elemenata u kontejneru.
ObjectfJ to A rra y () Vraća niz koji sadrži sve elemente kontejnera.
<T> T[] toArray(T[J a) Vraća niz koji sadrži sve elemente kontejnera čijije tip isti kao tip niza a,
a ne prosto Object. (Niz se mora konvertovati u odgovarajući tip).
Im ajte u vidu da ne postoji m etoda g e t( ) za izbor elem enta nasum ičnim pristupom .
Razlog je to što interfejs Collection sadrži i Set koji održava svoj unutrašnji poredak (pa
bi pretraživanje nasum ičnim p ristu pom bilo besm isleno). Dakle, za pregled elemenata
kolekcije m orate upotrebiti iterator.
U narednom prim eru prikazane su sve te metode. Iako one rade sa svim klasama koje
realizuju interfejs Collection, kao „najmanji zajednički imenitelj" upotrebljen je ArrayList.
//: kontejneri/MetodeKolekcija.java
// Šta sve možete da uradite sa svim kolekcijama.
import java.util
import net.mindview.util.*;
import static n e t . mi nd vi ew .u ti l.Print.*;
print(c);
c.remove(Countries.PODACI[l] [0]);
print(c);
/ / Uklanjanje svih komponenata koje se nalaze
// u kolekciji - argumentu:
c.removeAll(c2);
print(c);
c. ad dA l1 (c 2);
print(c);
// Da 1 i se odredeni element nalazi u ovoj kolekciji?
String vre = C o u n t r i e s . P O D A C I [3][0];
print("c.contains(" + vre + ") = " + c. co nt ai ns (v re ));
// Da li je kolekcija u ovoj kolekciji?
print("c.containsAll(c2) = " + c. co nt ainsAll(c2));
Co ll ec tion<String> c3 =
( (Li st<Stri ng>)c).subList (3, 5);
// Zadržavanje samo onih elemenata koji se nalaze
// u skupovima c2 i c3 (presek s k u p o v a ) :
c 2 .r et ai nA l1 (c 3);
print(c2);
// Odbacivanje svih elemenata iz skupa c2
// koji se pojavljuju i u skupu c3:
c2.removeAl 1 (c 3);
print("c2.isEmpty() = " + c 2 .isEm pt y( ));
c = new Ar r a y L i s t < S t r i n g > ( ) ;
c.addAll(Countries.imena(6));
print(c);
c.clear(); // Uklanjanje svih elemenata
print("posle c.clear():" + c);
I
} /* Ispis:
[ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, B U RU ND I,
deset, jedanaest]
C o l 1ections.max(c) = deset
Collections.min(c) = ALGERIA
[ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, B U RU ND I, deset,
jedanaest, ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI]
[ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI, deset, jedanaest,
ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI]
[BENIN, B0TSWANA, BURKINA FASO, BURUNDI, deset, jedanaest,
ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI]
[deset, jedanaest]
[deset, jedanaest, ALGERIA, ANGOLA, BENIN, B0TSWANA,
BURKINA FASO, BURUNDI]
c.contains(BOTSWANA) = true
c. co nt ai ns Al1 (c2) = true
[ANGOLA, BENIN]
c2.isEmpty() = true
[ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI]
posle c . c l e a r O : []
* ///:-
646 Misliti na Javi
Prave se ArrayListe koje sadrže različite skupove podataka i bivaju svedene naviše na
Collection objekte, pa je jasno da se sem interfejsa Collection ne upotrebljava ništa drugo.
U m etodi m a in ( ), pom oču jednostavnih vežbi prikazane su sve m etode klase Collection.
N aredni odeljci u ovom poglavlju opisuju različite realizacije interfejsa List, Set i Map.
Zvezdicom (*) je uvek označena realizacija koju bi trebalo podrazum evano izabrati. Opisi
starih klasa Vector, Stack i H ashtable dati su tek na kraju p o g lav lja- iako ne bi trebalo da
ih sam i upotrebljavate, sretaćete se s njim a u starom kodu.
Opcione operacije
M etode koje obavljaju razne vrste dodavanja i uklanjanja jesu opcione operacije interfejsa
Collection. To znači da klasa koja realizuje interfejs ne m ora da sadrži funkcionalne de-
finicije tih m etoda.
To je veom a neobičan način definisanja interfejsa. Kao što ste videli, u objektno ori-
jentisanom program iranju interfejs je vrsta ugovora. Pom oću njega se kaže: „Bez obzira
na to kako odlučite da realizujete ovaj interfejs, jem čim da ovom objektu m ožete da šalje-
te ove poruke.“4 Ali opciona operacija krši to tem eljno obećanje: ne sam o da pozivanje
ovih m etoda neće uraditi ništa korisno, već će i izazvati izuzetak! Kao da sm o izgubili bez-
bednost tipova u vreme prevođenja.
Nije sve baš tako strašno. U kolekciji, listi, skupu ili m api, prevodilac vas još uvek ogra-
ničava na pozivanje isključivo onih m etoda koje se nalaze u tom interfejsu. To je ipak
bolje od dinam ičkih jezika koji pružaju m ogućnost pozivanja svake m etode za svaki ob-
jekat, da bi se tek pri pokretanju program a ustanovilo da li pozvana m etoda uopšte može
da radi5. O sim toga, m noge m etode čiji je argum ent kolekcija, isključivo čitaju tu kolek-
ciju: nijedna m etoda za čitanje kolekcije nije opciona.
Zašto bi iko zadao da je m etoda opciona? Ovakav pristup sprečava eksploziju broja in-
terfejsa. Drugačiji načini projektovanja biblioteke kontejnera uvek se završe sa nerazm r-
sivom gom ilom interfejsa koji opisuju sve varijacije glavne tem e i zbog toga ih je teško
savladati. Nije čak ni m oguće obuhvatiti sve specijalne slučajeve u obliku interfejsa, pošto
uvek m ogu da se izmisle novi interfejsi. P ristupom „operacija koje nisu podržane“ postiže
se važna svrha (avine biblioteke kontejnera: kontejneri se lako savladavaju i koriste; ope-
racije koje nisu podržane specijalan su slučaj koji se može naučiti i kasnije. M eđutim , da
bi takav pristup delovao, p otrebno je sledeće:
I . Izuzetak tipa U n su p p o rted O p eratio n E x cep tio n m ora da sejavlja retko, tj. u većini
ldasa trebalo bi da rade sve operacije, a sam o u specijalnim slučajevima operacija ne
bi trebalo da bude podržana. To važi u Javinoj biblioteci kontejnera pošto klase koje
najčešće koristite (A rrayList, LinkedList, H ashSet i H ashM ap), kao i druge kon
kretne realizacije, podržavaju sve operacije. Ovakav pristup om ogućuje da napravi-
te novu kolekciju, da pri tom ne obezbedite definicije svih m etoda u interfejsu
C ollection, a da je ipak uklopite u postojeću biblioteku.
4 Terrnin ,,interfejs“ je ovde u p o treb ljen tako da obuhvata i fo rm alnu rezervisanu reč in terface i opštije
značenje: „m eto đ e p o d ržan e u svim klasam a i potklasam a".
5 M ada ovo zvuči ču d n o i m ožda beskorisno kada je o p isan o na taj način, videli ste, naročito u pogla-
vlju Podaci o tipu, da takvo d inam ičko p on ašanje m ože biti veom a m očno.
Poglavlje 17: Detaljno razmatranje kontejnera 647
Nepodržane operacije
U Javi su čest izvor nepodržanih operacija kontejner i pripadna struktura podataka ne-
prom enljive dužine. Takav kontejner (u gornjem prim eru, listu) od niza proizvodi m eto-
da A rrays.asList( ). S druge strane, vi odlučujeteda li će bilo koji kontejner (uključujući tu
i m ape) generisati izuzetke U nsupportedO perationException zbog upotrebe nepro-
menljivih m etoda u Idasi Collections. P rim er koji sledi sadrži oba slučaja:
//: kontejneri/Nepodrzane.java
// Nepodržane operacije u Ja vi ni m kontejnerima.
import java.util
Pošto m etoda A rrays.asList( ) pravi listu od niza odredene dužine, ima smisla da
bud u podržane sam o one operacije koje ne menjaju d u žin u niza. Svaka m etoda koja bi
prom enila d u žinu pripadne struktu re podataka izazvala bi izuzetak UnsupportedO pera-
tionException koji ukazuje na poziv nepodržane m etode (grešku program era).
Vodite računa o tom e da svakoj kolekđji uvek možete da prosledite rezultat m etode
Arrays.asList( ) kao konstruktorski argum ent (ili da pozovete m etodu addA lI( ), ili sta-
tičnu m etodu C ollections.addA ll( )) da biste napravili pravi kontejner koji om ogućuje
upotrebu svih m etoda - to se vidi iz prvog poziva m etode test( ) u funkciji m a in ( ). Takav
poziv proizvodi novu strukturu podataka prom enljive dužine.
Nepromenljive m etode klase Collections omotavaju kontejner posrednikom koji izaziva
UnsupportedOperationF.xception ako izvršite bilo koju operaciju koja na bilo koji način
menja taj kontejner. Cilj korišćenja tih m etoda jeste pravljenje konstantnog kontejnerskog
objekta. Navešćemo kasnije oelokupan spisak nepromenljivih m etoda klase Collections.
Poglavlje 17: Detaljno razmatranje kontejnera 649
Funkcije liste
Kao što ste videli, prost tip List se prilično lako koristi. Iako najćešće koristite m etodu
a d d ( ) za um etanje objekata, m etodu g e t( ) za uzim anje objekata jedan po jedan i itera-
t o r ( ) za dobijanje iteratora niza, postoje i druge m etode koje m ogu da budu korisne.
Svaka od m etoda u prim eru koji sleđi obavlja drugu grupu aktivnosti: ono što svaka li-
sta m ože da uradi (osnovniTest( )), kretanje po listi pom oću iteratora
(k retanjeIteratoroin( )) u odnosu na izm enu objekata pom oću iteratora (prom enalte-
ratorom f )), prikaz dejstva obrade listi (vidljivTest( )), i operacije dostupne sam o u
ulanćanim listam a (LinkedList):
//: kontejneri/Liste.java
// Šta se sve može raditi s listama.
import ja v a . u t i 1 .
import net.mindview.util.*;
import static ne t. mindview.uti1 .P r i n t .*;
List<String> b = Countrie s. im en a( 25 );
print("b = " + b ) ;
a.addAl1 (b);
a.addAl1 (b);
p r in t( a);
// Umetanje, uklanjanje i zamena elemenata
// pomoću Listlteratora:
ListIterator<String> x = a.listIterator(a.size()/2);
x. a d d( "j ed an ");
pr i n t ( a ) ;
pr in t( x. ne xt () );
x.remove();
p r in t( x. ne xt () );
x . se t( "4 7" );
p r in t( a);
// Kretanje kroz listu unazad:
x = a. l i s t l t e r a t o r ( a . s i z e O ) ;
wbile(x.hasPrevious())
printnb(x.previous() + " ");
print();
print("metoda vidljivTest goto va”);
)
// Postoje operacije koje mogu obaviti
// samo ulančane liste:
public static void testirajUlancanu() {
LinkedList<String> 11 = new Li nk ed List<String>();
1 1 .addAll(Countri es .imena(25));
pri nt (11);
// Lista kao stek, stavljanje na stek:
1 1 .addFirst("jedan");
11.addFi r s t( "d va ");
pri nt (11 );
// "Zavirivanje" na vrh steka:
print (11 .getFi r s t ());
// Kao skidanje sa steka:
print(l 1 . r e m o v e F i r s t O ) ;
pri n t (11 .removeFi r s t ()) ;
// Lista kao red za čekanje,
// izvlačenje elemenata iz "repa":
pri n t (11.r e mo ve La st());
print(l 1);
}
public static void main(String[] args) {
// Pravljenje i popunjavanje nove liste svaki put:
osnovniTest(
new LinkedList<String>(Countries.imena(25)));
osnovniTest(
new Ar ra yList<String>(Countries.imena(25)));
652 Misliti na Javi
kretanjeIteratorom(
new Li nk ed Li st <S tr in g> (Co un tr ie s. im en a( 25 )) );
kretanjeIteratorom(
new ArrayLi st<Stri n g > ( C o u n t r i e s .i m e n a (25)));
promenaI te ra to ro m(
new Li nk ed Li st <S tr in g> (Co un tr ie s. im en a( 25 )) );
promenaIteratorom(
new A r r a y L i s t < S t r i ng >( Cou nt ri es .i me na (2 5) ));
vidljivTest(
new Li nk ed Li st <S tr in g> (Co un tr ie s. im en a( 25 )) );
te st ir aj Ul an ca nu ();
}
} /* (Pokrenite da biste videli rezultat)
* ///:-
Set |interfejs) Svaki element koji dodajete u skup mora da budejedinstven; u suprot-
nom, Set ne dodaje duplikat elementa. Objekii koji se dodaju u skup
moraju da definišu metodu eq u a ls[J kojom se utvrđuje jedinstvenost
objekta. Set ima istovetan interfejs kao Collettion. Interfejs skupa ne
garantuje održavanje elemenata u određenom rasporedu.
HashSet* Klasa koja se koristi za skupove gde je važno brzo pronalaženje ele-
menata. Objekti moraju da definišu i metodu hash C o d ef).
TreeSet Ureden skup u obliku stabla. Iz ovog skupa mo/cte da izdvojite niz
ureden u određenom redosledu. Elementi moraju da definišu i interfejs
Comparable.
LinkedHashSet Ima brzinu pretraživanja klase HashSet. ali iraerno održava redosled
kojim su elementi bili umetani (redosled umetania) pomoću ulančane
liste. Zato se rezultati pojavljuju u redosledu umeianja kada iterirate kroz
Set. Elementi moraju definisati i metodu h as h C o d e )).
Zvezdica pored HashSet pokazuje sledeće: ako nem a dri,;>ih ograničenja, to treba da
bude podrazum evani izbor zato što je optim alan po brzini.
O brazac za definiciju m etode hashC od e( ) biće opisan u instavku poglavlja. eq u als( )
m orate da definišete i za skladištenje s transform acijom kljvič u (heširanje) i za skladištenje
u stablu, ali je m etoda h ash C o d e( ) apsolutno neophodna saino ako će se klasa nalaziti u
skupu tipa HashSet (što je verovatno, pošto bi ova klasa ut- avnom trebalo da bude vaš
prvi izbor kao realizacija za Set) ili LinkedHashSet. M e đ u j'n , d obar stil program iranja
nalaže da se uvek redefiniše m etoda h ashC o de( ) ako se redeimiše eq u als( ).
Ovaj prim er ilustruje m etode koje m oraju biti definisane i ; bi se određeni tip uspešno
upotrebljavao sa određenom realizacijom interfejsa Set:
class TipSkupa {
i nt i ;
public TipSkupa(int n) { i = n; )
public boolean equals(Object o) {
return o instanceof TipSkupa && (i == ((TipSkupa)o).i);
1
public String toString() { return Integer.toStrin g ( i ); }
8, 8, 8, 6, 5, 1]
[0, 5, 5, 6, 5, 0, 3, 1, 9, 8, 4, 2, 3, 9, 7, 3, 4, 4, 0, 7, 1, 9, 6, 2,
1, 8, 2, 8, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9]
java.lang.ClassCastException: TipSkupa cannot be cast to
j a va.lang.Comparable
java.lang.ClassCastException: Ti pHesiranja cannot be cast to
j a va.lang.Comparable
* ///:-
Da bi se pokazalo koje m etode su neophodne za određeni skup i da bi se istovrem eno
izbeglo dupliranje koda, napravljene su tri klase. O snovna klasa TipSkupa prosto skla-
dišiti jedan int i ispisuje ga m etodom to S trin g ( ). Pošto sve klase uskladištene u skupovi-
ma m oraju im ati m etodu eq u als( ), i ona je uskladištena u osnovnoj klasi. Jednakost je
zasnovana na vrednosti celog broja i.
TipHesiranja nasleđuje TipSkupa i dodaje m etodu h ashC ode( ) neophodnu da bi ob-
jekat bio smešten u realizaciju interfejsa Set s transform isanim ključem.
T ipS tabla realizuje interfejs C om p arable koji je neophodan ako će objekat biti ko-
rišćen u uređenom kontejneru, kao što je SortedSet. (Jedina trenutno dostupna klasa
koja ga realizuje je TreeSet.) O bratite pažnju na to da u m etodi co m p areT o () nisam ko-
ristio jednostavnu i očiglednu konstrukciju r e tu rn i-i2. Iako je to česta greška u pro-
gram iranju, sve bi radilo kako treba sam o kada bi i i i2 bili neoznačeni celi brojevi (pod
uslovom da Java im a rezervisanu reč u n sig n ed za neoznačen ceo broj, što nije siučaj).
Greška se pojavljuje za označen ceo broj koji nije dovoljno velik da predstavi razliku dva
označena cela broja (tip int). Ako je i veliki pozitivan ceo broj, a j veliki negativan ceo
broj, i-j će dovesti do prekoračenja i daće negativnu vrednost, što nije ispravno.
O d m etode co in p areT o () obično očekujete da proizvodi prirodan redosled usaglašen
s m etodom e q u a ls ( ). Ako e q u a ls () daje tru e za određeno poređenje, onda bi
co m p areT o () trebalo da da nulu kao rezultat istog poređenja, a ukoliko e q u a ls () daje fal-
se za neko poređenje, onda bi co m p areT o () za isto poređenje trebalo da da rezultat raz-
ličit od nule.
U klasi TipoviZaSkupove, m etode f ill( ) i te s t ( ) definisane su pom oću generičkih
tipova da bi se sprečilo dupliranje koda. Ponašanje skupa (objekta tipa Set) m etoda te s t( )
proverava s tri poziva m etode fill() za ispitni skup, pri čem u pokušava da u njega ubaci
duplikate objekata. Metoda f ill() prim a skup proizvoljnog tipa i Class objekat istog tipa.
Pomoću objekta tipa Class pronalazi konstruktor koji prim a celobrojni argum ent i pozi-
va taj ko nstruktor da bi skupu dodavala elemente.
Iz rezultata vidite da H ashSet čuva elem ente u nekom m isterioznom poretku (koji
ćem o objasniti u nastavku poglavlja), L inkedH ashSet čuva elem ente u poretku kojim su
bili um etnuti, a TreeSet čuva elem ente u uređenom poretku (zbog načina realizacije me-
tode co m p areT o (), to je slučajno opadajući poredak).
656 Misliti na Javi
SortedSet
Elem enti u kontejneru tipa SortedSet zajemćeno su u uređenom poretku, zbog čega sle-
deće m etode u interfejsu SortedSet m ogu da pruže dod atn u funkcionalnost:
Com parator co m parato r( ): Proizvodi C om parator koji se upotrebljava za ovaj skup
(Set) ili null za prirodan poredak.
Object first(): Proizvodi najniži element.
Object Ia st( ): Proizvodi najviši element.
SortedSet subSet(odElement, doElement): Proizvodi prikaz ovog skupa sa elemen-
tim a od odElement, uključivo, do doEIement, isključivo.
SortedSet headSet(doElement): Proizvodi prikaz ovog skupa sa elem entim a m anjim
od doElement.
SortedSet tailSet(odElement): Proizvodi prikaz ovog skupa sa elem entim a jednakim
ili većim od elem enta odElement.
Evo jednostavnog prim era:
Iterator<Stn'ng> it = so rt ed Se t. it er at or ();
for(int i = 0; i <= 6; i++) {
if(i == 3) najmanji = it.next();
if(i == 6) najveci = it.next();
else i t . n ex t( );
}
print(najmanji);
p r in t( na jv ec i);
print(sorted Se t. su bSe t( na jm an ji, n a j v e c i ) ) ;
pr in t( so rt ed Se t. he adS et (n aj ve ci ));
pri n t ( s or te dS et.tai1Set(najmanj i));
}
} /* Ispis:
[cetiri, dva, jedan, osam, pet, sedam, sest, tri]
cetiri
tri
osam
tri
[osam, pet, sedam, sest]
[cetiri, dva, jedan, osam, pet, sedam, sest]
[osam, pet, sedam, sest, tri]
* ///:-
Redovi za čekanje
Seni u aplikacijama za paralelno izvršavanje, Java SE5 je realizovala Queue (red za čekanje)
samo u kontejnerim a LinkedList i PriorityQueue koji se razlikuju po ponašanju, a ne po
perform ansam a. Sledi elem entarni prim er s večinom realizacija reda za čekanje (koje u
ovom prim eru neće sve funkcionisati), uključujući tu i redove za čekanje napravljene za
paralelno izvršavanje. Elemente treba um etati od jednog kraja, a vaditi od drugog:
//: kontejneri/PonasanjeRedaZaCekanje.java
// Poredi ponašanje nekih redova za čekanje
import java.util.concurrent.*;
import j a v a .ut i 1.*;
import net.mindview.util.*;
658 Misliti na Javi
//: kontejneri/ListaZadataka.java
// Složenija upotreba kontejnera PriorityQueue.
import j a v a . u t i l .*;
Vežba 11: (2) Napravite klasu koja sadrži objekat tipa Integer inicijalizovan m etodom ja-
va.util.Random na vrednost izm edu 0 i 100. Realizujte Comparable pom oću tog Integer
polja. Popunite objekat tipa PriorityQueue objektim a vaše klase i izvucite te vrednosti
m etodom p o I l( ) da bi se pokazalo da kontejner proizvodi očekivani poredak.
p u b l i c c la s s Dvored<T> {
p r i v a t e L in ke d L is t< T > dvored = new Li nkedLi s t < T > ( ) ;
p u b l i c v o id a d d F i r s t ( T e) { d v o r e d . a d d F i r s t ( e ) ; }
p u b l i c vo id addLast(T e) { d v o r e d . a d d L a s t ( e ) ; }
p u b lic T g e tF ir s t() { re tu rn d v o r e d .g e tF ir s t( ) ; }
p u b lic T g e tLa st() { re tu rn d v o re d .g e tL a s t(); }
p u b l i c T r e m o v e F ir s t ( ) { r e t u r n d v o r e d . r e m o v e F i r s t ( ) ; }
p u b l i c T removeLast() { r e t u r n d v o r e d . r e m o v e L a s t( ) ; }
p u b lic i n t s iz e () { retu rn d v o re d .s iz e O ; }
p u b lic S trin g to S trin g O { re tu rn d v o re d .to S tr in g O ; }
// I druge metode po p o t r e b i . . .
} ///= -
Ako ovaj Dvored upotrebite u sopstvenim program im a, verovatno ćete m orati da do-
date još m etoda da bi postao upotrebljiv.
Sledi jednostavna provera klase Dvored:
p u b l i c c la s s DvoredTest {
s t a t i c v o id t e s t P o p u n ja v a n ja ( D v o re d < I n te g e r > dvored) {
f o r ( i n t i = 20; i < 27; i+ + )
d v o re d .a d d F irs t(i);
f o r ( i n t i = 50; i < 55; i++)
d v o re d .a d d L a s t(i);
}
Poglavlje 17: Detaljno razmatranje kontejnera 661
Elementi se ređe umeću na oba kraja i vade sa oba kraja, pa se Dvored koristi rede od
običnog reda za čekanje (Queue).
Iscrpnije o Mapama
Kao što ste saznali u poglavlju Č uvanje objekata, osnovna nam ena m ape ( asocijativnog ni-
za ) jeste da održava parove ključ-vrednost (asocijacije, pridruživanja, m apiranja, presli-
kavanja), tako da vrednost m ožete pronači pom oću ključa. Standardna Javina biblioteka
sadrži različite osnovne realizacije Mape: HashMap, TreeMap, LinkedHashMap, Weak-
HashMap, ConcurrentHashMap i IdentityHashMap. Svima im je zajednički osnovni
interfejs Map, a razlikuju se u ponašanju i efikasnosti, poretku čuvanja i prezentovanja
parova, roku čuvanja objekata u mapi, radu m ape u višenitnim program im a i načinu
utvrđivanja jednakosti ključa. Iz broja realizacija interfejsa Map trebalo bi da zaključite
koliko je ova alatka važna.
Da biste mogli bolje da shvatite M ape, pogledaćem o kako se pravi asocijativni niz. Evo
jedne izuzetno jednostavne realizacije:
p u b l i c c la s s A s o c i j a t i v n i N i z < K , V > {
p riv a te 0 b je c t [] [] p a ro vi;
p r i v a t e i n t in d e k s ;
p u b l i c A s o c i j a t i v n i N i z ( i n t du zin a ) {
p a ro v i = new O b je c t [ d u z in a ] [ 2 ] ;
}
p u b l i c v o id p u t ( K k l j u c , V v r e d n o s t) {
i f ( i n d e k s >= p a r o v i . d u z i n a )
th ro w new A r r a y I n d e x 0 u t 0 f B o u n d s E x c e p t io n ( ) ;
662 Misliti na Javi
Performanse
Perform anse su temeljni problem pri radu s m apam a, jer je linearno pretraživanje me-
todom g e t( ) veoma spor način traženja ključa. Ovde pom aže brži kontejner H ashM ap.
Umesto sporog traženja ključa, on upotrebljava posebnu vrednost nazvanu k lju č za heši-
ranjc (engl. hash codc). Ključ za heširanje predstavlja način da se određena inform acija u
objektu pretvori u „relativno jedinstven“ celi broj (in t) za taj objekat. M etoda
h a s h C o d e () je definisana u korenskoj klasi O bject, pa svi Java objekti m ogu da proizvedu
ključ za heširanje. Kontejner H ashM ap uzim a h a s h C o d e () objekta i njom e brzo traga za
ključem. Tim e se dobija ogrom no poboljšanje perform ansi.6
Navešćemo osnovne realizacije interfejsa Map. Zvezdica kod H ashM ape pokazuje da
bi je trebalo podrazum evano izabrati (ako nem a drugih ograničenja), zato što je optim i-
zovana po brzini. Ostale realizacije im aju naglašenija druga obeležja, te su sporije od
H ashM ape.
Transform acija ključeva je najčešći način skladištenja elem enata u mapi. Kasnije ćemo
objasniti kako se obavlja ta transform acija.
Za ključeve za Mapu postavljaju se isti zahtevi kao za elem ente skupa (Set). Upoznali
ste ih u prim eru TipoviZaSkupove.java. Svaki ključ m ora imati m etodu eq u a ls(). Ako se
ključ upotrebljava u Mapi transform isanih ključeva, on m ora imati i propisnu m etodu
h ash C od e(). Ključ koji se upotrebljava u TreeMapi m ora realizovati Comparable.
U narednom prim eru pokazaćem o op erađ je d ostupne preko interfejsa Map, uz po-
m oć prethodno definisanog skupa ispitnih podataka BrojackaMapaPodataka:
/ / : k o n t e j n e r i/ M a p e . ja v a
/ / Šta se može u r a d i t i s mapama.
im p o r t j a v a . u t i l . c o n c u r r e n t . * ;
im p o r t j a v a . u t i 1 . * ;
im p o r t n e t . m i n d v i e w . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s Mape {
p u b l i c s t a t i c v o id p r i n t K e y s ( M a p < I n t e g e r , S t r in g > mapa) {
p r i n t n b ( " V e l i č i n a = " + m a p a .s iz e O + " , " ) ;
p rin tn b ("K lju č e v i: " );
p r i n t ( m a p a . k e y S e t ( ) ) ; / / Daje skup k lju č e v a
}
p u b l i c s t a t i c v o id t e s t ( M a p < I n t e g e r , S t r i n g > mapa) {
p r i n t ( m a p a . g e t C l a s s ( ) .g e tS im p le N a m e O ) ;
m a p a .p u tA l1 (new Broja cka MapaPodataka(25)) ;
Poglavlje 17: Detaljno razmatranje kontejnera 665
* ///:-
666 Misliti na Javi
M etoda printK eys() pokazuje kako se pravi kolekcija (Collection) od Mape. M etoda
keyS et() proizvodi skup (Set) ključeva iz Mape. Rezultate m etode v a lu es() lako ćete
štam pati zbog poboljšane podrške za štam panje u Javi SE5; ta m etoda pravi kolekciju od
svih vrednosti u M api. (Vodite računa o tom e da ključne reči m oraju biti jedinstvene, dok
vrednosti m ogu im ati duplikate.) Pošto M apa održava te kolekcije, svaka izmena u kolek-
ciji odražava se u njoj pridruženoj Mapi.
O statak program a pruža jednostavne prim ere svih operacija s M apam a i testira sve
osnovne vrste M apa.
Vežba 14: (3) Pokažite da java.util.Properties radi u gornjem program u.
SortedMap
Ako im ate neku realizađju interfejsa SortedMap (od njih je dostupna sam o TreeMap),
ldjučevi su zajam čeno u uređenom poretku, zbog čega sledeće m etode interfejsa Sor-
tedMap m ogu da pruže d o d atn u funkcionalnost:
Comparator com p arator(): Proizvodi kom parator za upotrebu u ovoj Mapi ili null
za p rirodni poredak.
T firstK ey(): Proizvodi najm anji ključ.
T lastICey(): Proizvodi najveći Jdjuč.
SortedMap subMap(odKljuč, doKljuč): Proizvodi prikaz dela m ape s ključevima od
ključa odKljuč, ukJjučivo, do ključa doKljuč, isključivo.
SortedMap headMap(toKey): Proizvodi prikaz dela m ape s ključevima m anjim od
ključa doKIjuč.
SortedMap tailMap(fromKey): Proizvodi prikaz dela m ape s ključevima jednakim ili
većim od Jđjuča odKljuč.
Ovaj prim er liči na PrimerZaSortedSet.java i pokazuje to dodatno ponašanje
TreeMape:
/ / : k o n te j n e r i/ P r im e r Z a S o r t e d M a p . ja v a
/ / Šta se može u r a d i t i s mapom TreeMap.
im p o rt j a v a . u t i l . * ;
im p o r t n e t . m i n d v i e w . u t i 1 . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ;
p u b l i c c la s s PrimerZaSortedMap {
p u b l i c s t a t i c v o id m ain ( S t r i n g [ ] arg s) {
T re e M a p < I n t e g e r , S t r in g > sortedMap =
new T re e M a p < In te g e r ,S tr in g > ( n e w Bro ja cka M a p a Po d a ta ka(lO ));
p ri n t(sortedM ap);
I n t e g e r n a jm a n ji = s o r t e d M a p . f i r s t K e y ( ) ;
I n t e g e r n a jv e c i = s o rte d M a p .1a s t K e y ( ) ;
p ri n t(n a jm a n ji);
pri n t(n a jv e c i);
I t e ra to r < In te g e r> i t = sortedM ap.keyS et(). i t e r a t o r ( ) ;
f o r ( i n t i = 0; i <= 6; i+ + ) {
Poglavlje 17: Detajjno razmatranje kontejnera 667
i f ( i == 3) n a jm a n ji = i t . n e x t ( ) ;
i f ( i == 6) n a jv e c i = i t . n e x t ( ) ;
e ls e i t . n e x t ( ) ;
1
p rin t(n a jm a n ji);
p rin t(n a jv e c i);
p r i n t ( s o r t e d M a p . s u b M a p ( n a jm a n ji, n a j v e c i ) ) ;
p rin t( s o r t e d M a p . h e a d M a p ( n a j v e c i) ) ;
p r i n t ( s o r t e d M a p . t a i 1Map(najmanj i ) ) ;
}
} / * Is p is :
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10, 9=J0}
0
9
3
7
{3=D0, 4=E0, 5=F0, 6=G0}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 5=G0}
{3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10, 9=J0}
* ,///:-
LinkedHashMap
M apa LinkedHashMap transform iše sve ključeve radi brzine, ali tokom prolaska parove
daje u redosledu um etanja (System .out.println() iterira kroz m apu, pa rezultate prolas-
ka možete videti). Sem toga, realizacija LinkedHashMape može se u konstruktoru kon-
figurisati tako da upotrebljava algoritam najdavnijeg korišćenja (engl. least-recendy-used,
LRU) odnosno pristupanja, pa su na početku liste elementi kojima nije pristupano (te su
stoga kandiđati za uklanjanje). Tim e je olakšano pravljenje program a koji periodično či-
ste smeće cia bi se uštedeo m em orijski prostor. Evo jednostavnog prim era u kojem su pri-
kazane obe funkcionalnosti:
/ / : k o n t e j n e r i /PrimerZaLinkedHash Map.java
/ / Šta se može u r a d i t i s kontejn erom LinkedHashMap.
im p o r t j a v a . u t i 1 . * ;
im p o r t n e t . m i n d v i e w . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s PrimerZaLinkedHashMap {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
L in k e d H a s hM a p <In te g e r,S trin g > linkedMap =
new L in k e d H a s h M a p < In te g e r,S trin g > (
new B ro ja ckaM apaPodataka(9));
668 Misliti na Javi
pr in t(linkedMap);
// Poredak najdavnijeg korišćenja:
linkedMap =
new LinkedHashMap<Integer,String>(16, 0.75f, true);
linkedMap.putAll(new Broj ac ka Ma pa Po da ta ka( 9));
pr in t(linkedMap);
for(int i = 0 ; i <6; i++) // Prouzrokuj pristupanje:
linkedMap.get(i);
print(linkedMap);
1 inkedMap.get(O);
pr in t(linkedMap);
}
} /* Ispis:
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10}
{6=G0, 7=H0, 8=10, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0}
{6=G0, 7=H0, 8=10, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0}
* ///:-
Iz ispisa vidite da prolazak kroz m apu zaista daje parove u redosledu um etanja, čak i u
LRU verziji. M eđutim , nakon (samo) prvih šest pristupanja u LRU verziji, poslednje tri
stavke prelaze na početak liste. Zatim, kada se ponovo pristupi stavci 0, ona prelazi na
začelje liste.
//: kontejneri/Medved.java
// Izgleda uverljivo, ali ne radi kao ključ HashMape.
//: kontejneri/Prognoza.java
Poglavfje 17: Deta|jno razmatranje kontejnera 669
p u b l i c c la s s Prognoza {
p r i v a t e s t a t i c Random s lu c a ja n = new Random(47);
p r i v a t e boolean senka = s l u c a ja n . n e x t D o u b le ( ) > 0 . 5 ;
p u b lic S trin g to S tr in g ( ) {
if ( s e n k a )
r e t u r n "Još š e s t sedmica z i m e ! " ;
e ls e
r e t u r n "Rano p r o l e ć e l " ;
}
} ///:-
/ / : k o n te jn e ri/D e te k to rP ro le c a .ja v a
/ / Kakvo će b i t i vreme?
im p o r t j a v a . l a n g . r e f l e c t . * ;
im p o r t j a v a . u t i l . * ;
im p o rt s t a t i c n e t . i n i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s D e te k to r P r o le c a {
/ / K o r i s t i kla su Medved i l i neku njenu p o t k la s u :
p u b l i c s t a t i c <T extends Medved>
v o id o tk riv a n je P ro le c a (C 1 a s s < T > ty p e ) thro ws Exce p tio n {
Co n s tru c to r<T > meda = t y p e . g e t C o n s t r u c t o r ( i n t . c l a s s ) ;
Map<Medved,Prognoza> mapa =
new HashMap<Medved,Prognoza>();
f o r ( i n t i = 0; i < 10; i+ + )
m a p a .p u t (m e d a .n e w ln s t a n c e (i) , new P r o g n o z a O ) ;
p r i n t ( " m a p a = " + mapa);
Medved md = m e d a .n e w ln sta n c e (3 );
p r i n t ( " T r a ž e n j e prognoze za " + m d);
i f(m a p a .c o n ta i nsKey(md))
print(m ap a .g e t(m d ));
el se
p r i n t ( " N i j e pronađen k l j u č : " + md);
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n {
o t k r i v a n je P r o le c a ( M e d v e d . c la s s ) ;
}
} / * Is p is :
mapa= {Medved #3=Rano p r o l e ć e ! , Medved #7=Rano p r o l e ć e ! ,
Medved #5=Rano p r o l e ć e ! , Medved #9=Još š e s t sedmica z im e !,
Medved #8=Još š e s t sedmica z im e !, Medved #0=Još š e s t sedmica z im e !,
Medved #6=Rano p r o l e ć e ! , Medved #4=Još š e s t sedmica z im e !,
Medved #l=Još š e st sedmica z im e ! , Medved #2=Rano p r o l e ć e ! }
T ra že n je prognoze za Medved #3
N i j e pronađen k l j u č : Medved #3
* ///:-
670 Misliti na Javi
Svaki Medved dobija identifikacioni broj da biste u H ashM api mogli da pronadete
određeni objekat tipa Prognoza tako što ćete kazati: „Daj m i objekat tipa Prognozapri-
družen objektu #3 tipa Medved“. Klasa Prognoza sadrži objekat tipa boolean inicijalizo-
van m etodom java.util.random () i m etodu to S trin g() koja tum ači rezultate. M etoda
otkrivanjeProIeca() napravljena je procesom refleksije da generiše p rim erak klase Med-
ved ili njene potklase i da ga upotrebljava. To će nam dobro doći kasnije, kada za rešavanje
ovde prikazanog problem a nasledim o nov tip klase Medved.
HashMapa se popunjava objektim a tipa Medved i njim a prid ru žen im objektim a tipa
Prognoza. Ta HashMapa se ispisuje da biste videli da je popunjena. Zatim se Medved broj
3 koristi kao ključ za pronalaženje prognoze za objekat #3 tipa Medved (za koji m ožete vi-
deti da m ora biti u Mapi).
Sve izgleda veom a jednostavno, aii ne radi - ne m ože da se pronađe ključ za #3. Pro-
blem je to što se Medved autom atski nasleđuje od zajedničke korenske klase Object, pa se
za generisanje ključa za heširanje za svaki objekat upotrebljava m etoda h ash C o d e() klase
Object. O na za heširanje podrazum evano upotrebljava sam o adresu svog objekta. Zato
prvi prim erak Medved(3) ne proizvodi ključ za heširanje jednak ključu za heširanje dru-
gog prim erka Medved(3), koji sm o pokušali da upotrebim o za pretraživanje.
M ožda mislite da je dovoljno napisati odgovarajuću redefiniciju m etode h ash C od e().
Ali ni to neće funkcionisati sve dok ne uradite još nešto: redefinišete m etodu eq u a ls()
koja je takođe deo klase Object. HashMapa upotrebljava eq u a ls() prilikom utvrđivanja
da li je ključ jednak nekom ključu u tabeli.
Prava m etoda eq u als() m ora zadovoljiti sledećih pet uslova:
1. Refleksivna: Za svaki x, x.equals(x) treba da vraća true.
2. Simetrična: Za svaki x i y, x.equals(y) treba da vraća true ako i sam o ako
y.equals(x) vraća true.
3. Tranzitivna: Za svaki x, y i z, ako x.equals(y) vraća true i y.equals(z) vraća true,
onda x.equals(z) treba da vraća true.
4. Konsistentna: Za svaki x i y, svaki poziv x.equals(y) treba dosledno da vraća true ili
dosledno da vraća false, ukoliko se inform acije upotrebljene pri utvrđivanju jedna-
kosti objekata ne prom ene.
5. Za svaki x različit od null, x.equals(null) treba da vraća false.
Ponavljam, podrazum evana m etoda O bject.equals() jednostavno poredi adrese ob-
jekata, pa jedan Medved(3) nije jednak drugom objektu Medved(3). Dakle, da biste u
HashMapi mogli da upotrebljavate sopstvene klase kao ključeve, m orate redefinisati i
hashC od e() i eq u a ls(), kao što je urađeno u sledećem rešenju problem a s medvedom:
/ / : k o n t e jn e r i/ M e d v e d 2 . ja v a
/ / Klasa u p o t r e b lje n a kao k l j u č u HashMapi
/ / mora r e d e f i n i s a t i metode hashCode() i e q u a l s ( ) .
//: kontejneri/DetektorProleca2.java
// Ključ koji funkcioniše.
Prvo, prisetite se zašto transform išem o ključeve: hteli sm o m ogućnost da jedan obje-
kat pronadem o pom oću drugog. To se m ože postići i pom oću kontejnera TreeMap ili čak
i realizacijom sopstvene Mape. N asuprot realizaciji s transform isanjem ključeva, naredni
prim er realizuje Mapu p om oću p ara ArrayList kontejnera. Za razliku od prim era Asoci-
jativniNiz.java, u narednom se interfejs Map p o tp u n o realizuje, što objašnjava posto-
janje m etode en tryS et():
//: kontejneri/SporaMapa.java
// Mapa realizovana ArrayListama.
import java.util.*;
import net.mindview.util.*;
/ / : k o n t e j n e r i/ S t a v k a M a p e . ja v a
/ / Jednostavan i n t e r f e j s M ap.E ntry za p rim e re r e a l i z o v a n j a i n t e r f e j s a
Map.
im p o r t j a v a . u t i 1
}
p u b l i c boolean e q u a l s ( 0 b j e c t o) {
i f ( ! ( o in s t a n c e o f StavkaMape)) r e t u r n f a l s e ;
StavkaMape sm = (StavkaMape)o;
return
( k l j u c == n u l l ?
sm.getKey() == n u l l : k l j u c . e q u a l s ( s m . g e t K e y ( ) ) ) &&
(v re d n o st == n u l l ?
sm .getV alue()= = n u l l : v r e d n o s t . e q u a l s ( s m . g e t V a l u e ( ) ) ) ;
}
p u b lic S trin g to S tr in g O { r e tu r n k l ju c + " =" + vrednost; }
} ///:-
Ovde veoma jednostavna klasa StavkaMape čuva i vraća ključeve i vrednosti. To je u
m etodi entrySet() iskorišćeno za pravljenje skupa parova ključ - vrednost. O bratite
pažnju na to da entrySet() upotrebljava HashSet za čuvanje parova, pa StavkaMape jed-
nostavno koristi m etodu h ash C od e() objekta kljuc. Iako je ovo rešenje veom a jednostav-
no i naizgled funkcioniše u trivijalnom testu u m etodi SporaM apa.m ain(), to nije
korektna realizacija zato što se pravi kopija skupova kljucevi i vrednosti. Korektna reali-
zacija skupa en trySet() treba da pruži u v id u Mapu, a ne da pravi njenu kopiju, i taj uvid
će om ogućiti modifikaciju prvobitne m ape (što kopija ne om ogućuje). Priliku da rešite
ovaj problem naći ćete u vežbi 16.
Vodite računa o tom e da m etoda e q u a ls () klase StavkaM ape m ora da proverava i
ključeve i vrednosti. Značenje m etode h a s h C o d e () biće opisano uskoro.
Predstavu sadržaja klase SporaM apa u obliku znakovnog niza (String) autom atski
pravi m etoda to S trin g () definisana u klasi A bstractM ap.
U m etodi S p o ra M ap a .m a in () učitava se S poraM apa i zatim se prikazuje njen sadržaj.
Poziv m etode g e t( ) pokazuje da to funkcioniše.
Vežba 15: (1) Ponovite vežbu 13 uz upotrebu m ape SporaM apa.
Vežba 16: (7) Prim enite testove iz program a M aps.java na klasu S poraM apa da biste pro-
veriii kako funkcioniše. Popravite sve što u klasi SporaM apa ne funkcioniše korektno.
Vežba 17: (2) Realizujte ostatak interfejsa M ap za klasu SporaM apa.
Vežba 18: (3) Napravite SporSkup po uzoru na m apu SporaM apa.java.
Transform isanje ide još korak dalje jer im plicira da sam o želite da ključ smestite negde
gde se m ože brzo pronaći. Najbrža struk tura za skladištenje grupe elemenata je niz, pa će
on biti upotrebljen za predstavljanje inform acija o ključu (rekao sam „inform acija o
ključu“, a ne sam og ključa). Budući da je veličina niza neprom enljiva, im am o problem :
želimo da sm estim o neodređen broj vrednosti u M apu, ali ako je broj ključeva određen
veličinom niza, kako ćem o to postići?
O dgovor je da taj niz neće sadržati ključeve. Iz objekta ključa biće izveden broj - indeks
u tom nizu. Taj broj je k lju č za heširanje (engl. hash code) koji daje m etoda h a s h C o d e () (u
rečniku računarske nauke nju nazivam o heš fu n k c ija ilifu n k c ija za transform isanje ključe-
va), defm isana u klasi O bject i verovatno redefinisana u vašoj klasi.
Da bi se rešio problem niza neprom enljive veličine, više ključeva m ože dati isti indeks.
Dakle, m ogu nastati sudari (engl. collisions). Stoga nije važno koliki je niz; u njem u će biti
mesta za ključeve za heširanje svih objekata ključeva.
Zato postupak pronalaženja vrednosti počinje izračunavanjem ključa za heširanje koji
se upotrebljava kao indeks niza. Kada biste m ogli jem čiti da neće biti sudaranja (što je m o-
guće ako im ate neprom enljiv broj vrednosti), onda biste imali savršenu fu n k c iju za trans-
form isanje ključeva, ali to je poseban slučaj.7 U svim drugim slučajevima, sudaranjem se
bavi spoljno nadovezivanje (engl. externalchaining): niz ne pokazuje neposredno na vredn-
ost, nego na listu vrednosti. Za tim vrednostim a traga se m etodom e q u a ls() na linearan
način. Naravno, taj deo pretraživanja je m nogo sporiji, ali ako je funkcija za transfor-
misanje ključa dobra, u svakom odeljku liste biće tek nekoliko vrednosti. Stoga se umesto
pretraživanja cele liste, brzo skače na odeljak u kojem za pronalaženje vrednosti treba upo-
rediti tek nekoliko stavki. To je m nogo brže i zato je kontejner H ashM ap tako brz.
Pošto sada znate osnove transform isanja ključeva, m ožem o da realizujemo jednostav-
nu M apu s transform isanim ključevima:
/ / : k o n t e jn e ri/ J e d n os ta vn a H a sh M a p a .ja v a
/ / Prim e r mape s t r a n s f o r m is a n im k lju č e v im a .
im p o r t j a v a . u t i 1
im p o r t n e t . m i n d v i e w . u t i l . * ;
l ' Javi SE5, savršena funkcija za tran sfo rm isan je ključeva (heš funkcija) realizovana je u kontej-
n erim a E num M ap i EnumSet, zato što enum definiše neprom enljiv broj instanci. Videti poglavlje
Nabrojoni tipovi.
676 Misliti na Javi
i f ( k o f e [ i n d e k s ] == n u l l )
k o f e [ in d e k s ] = new L in k e d L i s t< S ta v k a M a p e < K ,V » ( ) ;
Lin ke d Lis t< S ta v ka M a p e< K ,V » kofa = k o f e [ i n d e k s ] ;
StavkaMape<K,V> par = new StavkaM ape<K,V >(klju c, v r e d n o s t ) ;
boolean pronadjen = f a l s e ;
L is t I t e r a t o r < S t a v k a M a p e < K , V » i t = k o f a . l i s t I t e r a t o r ( ) ;
w h ile (it.h a s N e x t()) {
StavkaMape<K,V> iP a r = i t . n e x t ( ) ;
if(iP a r.g e tK e y () .e q u a ls (k lju c )) {
sta ra V re d n o st = i P a r . g e t V a l u e ( ) ;
i t . s e t ( p a r ) ; / / Zameni s t a r o novim
pro nadje n = t r u e ;
bre ak;
)
}
if(Ip ro n a d je n )
k o fe [in d e k s ].a d d (p a r);
r e t u r n s t a ra V re d n o s t;
}
p u b l i c V g e t ( O b je c t k l j u c ) {
i n t indeks = M a t h . a b s (k lju c .h a s h C o d e O ) % VELICINA;
i f ( k o f e [ i n d e k s ] == n u l l ) r e t u r n n u l l ;
for(StavkaMape<K,V> iP a r : k o f e [ i n d e k s ] )
i f(iP a r.g e tK e y (). e q u a ls (k lju c ))
re tu rn iP a r .g e tV a lu e () ;
retu rn n u ll;
}
p u b l i c S e t< M ap .E ntry< K ,V » e n t r y S e t ( ) {
S e t< M ap .E ntry < K ,V » skup= new HashSet<Map. E n t r y < K , V » ( ) ;
fo r(L in k e d L is t< S ta v k a M a p e < K ,V » ko fa : k o fe ) {
i f ( k o f a == n u l l ) c o n t in u e ;
for(StavkaMape<K,V> mpar : kofa)
s k u p .ad d (m p a r);
}
r e t u r n skup;
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) {
Jednosta vnaHashMapa<Strin g,String> m =
new J e d n o s ta v n a H a s h M a p a < S trin g ,S trin g > ();
m.putAl 1 ( C o u n t r i e s . g l a v n i j r a d o v i ( 2 5 ) ) ;
S y s te m .o u t.p rin tln (m );
S y s t e m . o u t . p r in t ln ( m . g e t ( " E R I T R E A " ) ) ;
S ys te m .o u t.p rin tln (m .e n try S e t());
}
} / * Is p is :
{CAMEROON=Yaounde, C0NG0=Brazzavi11e, CHAD=N1djamena, COTE D'IVOIR
(IVORV COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui,
GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo,
ANGOLA=Luanda, BURKINA FAS0=0uagadougou, ERITREA=Asmara,
THE GAMBIA=Banjul, KENYA=Nairobi, GAB0N=Librevi11e, CAPE VERDE=Praia,
Poglav[je 17: Detaljno razmatranje kontejnera 677
s Ispostavlja se d a p rim broj zapravo nije idealan kao veličina kofa za heširanje, i novije realizacije heši-
ranja u Javi upotrebljavaju veličinu jednaku nekom step en u b roja dva (a to je rezultat opsežnog ispi-
tivanja). Deljenje i o sta ta k o d deljenja su najsporije o p eracijek o jeo b av ljaju savrem eni procesori. Ako
je d u ž in a ta b e le je d n a k a n e k o m s te p e n u b ro ja d v a , u m e s to d e lje n ja m o ž e se u p o tr e b iti m a s k ira n je .
P o što je get( ) n ajćešća o p e ra c ija , d e lje n je (% ) p ro u z ro k u je \ eliki d e o tro š k o v a k o je e lim in iš e p ris tu p
„ ste p e n b ro ja d v a" (ali m o ž e d a u tić e i na n eke h a s h C o d e ( ) m e to d e ).
678 Misliti na Javi
Vežba 21: (2) Prepravite program JednostavnaHashMapa tako da prijavljuje broj ,,pro-
ba“ p otrebnih kada dođe do sudara. D rugim rečima, koliko p u ta se m o ra pozvati n e x t()
za Iteratore koji prolaze ulančanim listam a u potrazi za p o d u d arn im elementima?
Vežba 22: (4) Realizujte m etode clea r() i rem ove() za kontejner JednostavnaHashMapa.
Vežba 23: (3) Realizujte ostatak interfejsa Map za kontejner JednostavnaHashMapa.
Vežba 24: (5) Po uzoru na prim er u program u JednostavnaHashMapa.java, napravite i
testirajte JednostavanHashSkup.
Vežba 25: (6) U m esto da za svaku kofu upotrebljavate po jedan Listlterator, prepravite
klasu StavkaMape tako da bude sam ostalna jednostruko ulančana lista (svaka Stavka-
Mape treba da im a vezu unapred ka sledećoj klasi StavkaMape). Prepravite ostatak koda
u program u JednostavnaHashMapa.java tako da novi pristup ispravno funkcioniše.
//: k o n t e j n e r i/ K 1 ju č e v iZ a T ra n s f o r m is a n je Z n a k o v n ih N iz o v a . ja v a
p u b l i c c la s s K 1 ju če viZ a T ra ns fo rm isan je Z n ak o vn ih N iz o v a {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
S t r i n g [ ] dvazdravo = "Zdravo Z d r a v o " . s p l i t ( " " ) ;
S y s t e m . o u t . p r in t ln ( d v a z d r a v o .h a s h C o d e O ) ;
S y s t e m . o u t . p r i n t l n ( d v a z d r a v o .h a s h C o d e O ) ;
}
} /* Is p is : ( p r im e r)
69609650
69609650
* ///:-
Očigleđno je da se hashC od e() za tip String izračunava na osnovu sadržaja datog zna-
kovnog niza (objekta tipa String).
Dakle, da bi m etoda hashC od e() bila delotvorna, m ora biti brza i sm isaona, tj. m ora
generisati vrednost na osnovu sadržaja objekta. Ne zaboravite da ta vrednost ne m ora biti
jedinstvena — bavite se brzinom , a ne jedinstvenošću - ali kom binacija m etoda
h ash C od e() i eq u a ls() m ora u potpunosti odrediti identitet objekta.
Pošto se hashC od e() dodatno obraduje pre pretvaranja u indeks kofa, opseg vredno-
sti rezultata te m etode nije važan; dovoljno je da ona generiše ceo broj (int).
Ima tu još nešto: dobra m etoda hashC ođ e() treba da daje ravnom erno raspodeljene
vređnosti. Ako se te vrednosti negde gomilaju, onda će kontejner HashMap ili HashSet
na nekim mestim a biti gušće popunjen i neće biti onoliko brz kao što m ože biti uz ravno-
m erno raspodeljene rezultate funkcije za heširanje.
U knjizi Efikasno program iranje na Javi (M ikro knjiga, 2004), Joshua Bloch daje osnov-
ni recept za generisanje pristojne m etode hashC od e():
1. U celobrojnu prom enljivu (tipa int) nazvanu rezultat smestite neki broj različit od
nule, recimo 17.
2. Za svako značajno polje f u objektu (tj. svako polje koje m etoda eq u a ls() uzim a u
obzir) izračunajte celobrojni (int) hashC od e() c:
p u b l i c c la s s Pre b ro ja n Z n a ko v n iN iz {
p r i v a t e s t a t i c L i s t < S t r i n g > n a p r a v lje n o =
new A r r a y L i s t < S t r i n g > ( ) ;
p r i v a t e S t r i n g s;
p r i v a t e i n t i d = 0;
p u b l i c P r e b r o ja n Z n a k o v n iN iz ( S t r in g znn) {
s = znn;
n a p ra v lje n o .a d d (s );
/ / i d j e ukupan b r o j i n s t a n c i datog znakovnog
/ / n iz a k o je k o r i s t i P re b ro ja n Z n a k o v n iN iz :
f o r ( S t r i n g s2 : n a p r a v lje n o )
if( s 2 .e q u a ls ( s ))
i d++;
}
p u b lic S trin g to S trin g O {
r e t u r n "Znakovni n i z : " + s + “ i d : " + id +
11 hashCode(): " + hashCode();
}
p u b l i c i n t hashCode() {
/ / Veoma je d n o sta va n p r i s t u p :
/ / r e t u r n s.hashCode() * i d ;
/ / Upotrebićemo r e c e p t Joshue Blocha:
i n t r e z u l t a t = 17;
r e z u l t a t = 37 * r e z u l t a t + s .h a s hC od e ();
r e z u l t a t = 37 * r e z u l t a t + i d ;
retu rn r e z u lt a t ;
}
p u b l i c boolean e q u a l s ( 0 b j e c t o) {
r e t u r n o in s t a n c e o f P re b ro ja n Z n a k o v n iN iz &&
s . e q u a l s ( ( ( P r e b r o j a n Z n a k o v n i N i z ) o ) . s ) &&
i d == ( ( P r e b r o ja n Z n a k o v n iN iz ) o ) . i d ;
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
M a p < P r e b ro ja n Z n a k o v n iN iz ,In te g e r> mapa =
new H a s h M a p < P r e b ro ja n Z n a k o v n iN iz ,In te g e r > () ;
P r e b r o ja n Z n a k o v n iN iz [ ] pzn = new P re b ro ja n Z n a k o v n iN iz ;
f o r ( i n t i = 0; i < p z n . d u z in a ; i+ + ) {
Poglavlje 17: Detaljno razmatranje kontejnera 681
p u b l i c c la s s Je d in ka implements Comparable<Jedinka> {
p r i v a t e s t a t i c long b r o j a c = 0;
p r i v a t e f i n a l long i d = b ro ja c + + ;
682 Misliti na Javi
p r i v a t e S t r i n g ime;
p u b l i c J e d in k a ( S t r in g ime) { t h i s . i m e = ime; }
/ / 'im e ' i s opciono:
p u b l i c J e d in k a () {}
p u b lic S trin g to S trin g O {
r e t u r n g e tC la ss ().g e tS im p le N a m e () +
(ime == n u l l ? " " : “ " + im e );
}
p u b l i c long i d ( ) { r e t u r n i d ; }
p u b l i c boolean e q u a ls ( O b je c t o) {
r e t u r n o i n s t a n c e o f J e d in k a &&
i d == ( ( J e d i n k a ) o ) . i d ;
}
p u b l i c i n t hashCode() {
i n t r e z u l t a t = 17;
i f ( i m e != n u l l )
r e z u l t a t = 37 * r e z u l t a t + im e .h a sh C o d e ();
r e z u l t a t = 37 * r e z u l t a t + ( i n t ) i d ;
return re z u lta t;
}
p u b l i c i n t compareTo(Jedinka a rg ) {
/ / Prvo se porede imena k ia s a :
S t r i n g prv o = g e t C la s s ( ) . g e tS im p l e N a m e ( ) ;
S t r i n g a rg P rv i = a r g . g e t C l a s s ( ) .g e tS im pleN am e ();
i n t p rv o P o re d je n je = p rv o . c o m p a r e T o ( a rg P rv i) ;
i f ( p r v o P o r e d j e n j e != 0)
r e t u r n p rv o P o re d je n je ;
i f ( i m e != n u l l && a rg .im e != n u l l ) {
i n t d ru g o P o re d je n je = im e .c o m p a re T o (a r g .im e ) ;
i f ( d r u g o P o r e d j e n j e != 0)
r e t u r n d ru g o P o re d je n je ;
}
r e t u r n ( a r g . i d < i d ? -1 : ( a r g . i d == i d ? 0 : 1 ) ) ;
}
} lll--~
M etoda co m p areT o () im a hijerarhiju poređenja, tako da proizvodi sekvencu uređenu
prvo po stvarnom tipu, zatim po im enu ako ono postoji, i najzad po redosledu pravljenja.
U ovom prim eru videćete kako sve to radi:
/ / : k o n t e j n e r i / T e s t i r a n j e J e d i n k i . ja v a
im p o rt h o ld in g .M a p O f L i s t;
im p ort p o d a c i o t i p u . l j u b i m c i
im p o rt j a v a . u t i l
p u b l i c c la s s T e s t i r a n j e J e d i n k i {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) {
Set<Jedinka> I j u b i m c i = new T r e e S e t < J e d i n k a > ( ) ;
f o r ( L i s t < ? extends LJubimac> 11 j :
Poglavlje 17: Deta[jno razmatranje kontejnera 683
M a p O f L is t . l j u b i t e l j i L j u b i m a c a . v r e d n o s t i { ) )
f o r(L J u b im a c l j : l l j )
1ju b im c i .a d d ( l j ) ;
S y s te m .o u t.p rin tln (lju b im c i);
}
} / * Is p is :
[Cat E l s i e May, Cat P in k o la , Cat S h a c k le t o n , Cat S t a n f o rd aka
S t i n k y el Negro, Cymric M o l l y , Dog M a r g r e t t , Mutt Sp ot, Pug Louie
aka L ouis S n o r k e ls t e i n Dupree, Rat F i z z y , Rat F r e c k ly , Rat. Fuzzy
* ///:-
Pošto svi ovi ljubim ci im aju im ena, uređuje se prvo po tipu, zatim po im enu i najzad
po im enu u n u ta r njihovog tipa.
N apisati odgovarajuće m etode h ash C od e() i eq u als() za novu klasu um e da bude za-
petljano. Alatke koje će vam pri tom e pom oći pronaći ćete u Apache projektu „Jakarta
C om m ons“ na adresi jakarta.apache.org/com m ons, p o d „lan g “ (ovaj projekat im a i m no-
go drugih potencijalno korisnih biblioteka, pa izgleda da je to odgovor Javine zajednice
na lokaciju u'mv.froosf.org zajednice korisnika C + + -a).
Vežba 26: (2) U program PrebrojanZnakovniNiz dodajte char polje koje se takođe inici-
jalizuje u konstruktoru i prepravite m etode h ash C od e() i eq u a ls() tako da obuhvate
vrednost ovog polja.
Vežba 27: (3) Prepravite m etodu h ash C od e() u program u PrebrojanZnakovniNiz.java
tako što ćete ukloniti kom binaciju sa id, i pokažite da PrebrojanZnakovniNiz i dalje radi
kao ključ. Zašto ovaj pristup ne valja?
Vežba 28: (4) Dodavanjem m etoda h a sh C o d e(), eq u a ls() i realizovanjem interfejsa
Comparable za svaki tip N_torke prepravite net/m indview/util/N_torka.java tako da
postane klasa opšte nam ene.
Izbor realizacije
Dosad je trebalo da shvatite sledeće: iako postoje sam o ćetiri osnovna tipa kontejnera
- M ap, List, Set i Q ueue - svaki od tih interfejsa im a više realizacija. Ako vam treba funk-
cionalnost određenog interfejsa, kako da izaberete njegovu realizaciju?
Svaka realizacija ima svoja obeležja, prednosti i nedostatke. Na prim er, na slici s poćet-
ka ovog poglavlja vidite da je obeležje klasa Hashtable, Vector i Stack to što su nasleđene
iz prethodnih verzija Jave (engl. legacy), kako se stari kod ne bi rušio zbog prevođenja no-
vim prevodiocim a (ali ih ne treba koristiti u novim program im a).
Vrste redova za čekanje u (Q ueues) u Javinim bibliotekam a razlikuju se sam o po
načinu prim anja i vraćanja vrednosti (važnost toga objasnićem o u poglavlju Paralclno
izvršavanje).
Razlika između kontejnera često se svodi na to kakvi su ,,u pozadini“- tj. koje strukture
podataka fizički realizuju željeni interfejs. Na prim er, pošto ArrayList i LinkedList reali-
zuju interfejs List, elem entarne List operacije su iste bez obzira na to koju od njih upo-
trebite. M eđutim , u pozadini A rrayListe je niz, dok je LinkedList realizovana na način
uobičajen za dvostruko ulančanu listu, kao grupa pojedinačnih objekata koji sadrže
684 Misliti na Javi
podatke i reference na prethodni i sledeći elem ent u listi. Upravo zato, ako nam eravate da
m nogo um ećete i uklanjate elem ente usred liste, koristite LinkedList. (LinkedList im a i
dodatn u funkcionalnost, utvrđenu u listi AbstractSequentialList.) Ali ako vam to nije
potrebno, uzm ite listu ArrayList koja je obično brža.
Kao drugi prim er, Set se može realizovati kao TreeSet, HashSet ili LinkedHashSet.9
Ponašanje svakog od tih skupova je drugačije: HashSet je podesan za tipičnu u p o treb u i
najbrže obavlja pretraživanje, LinkedHashSet čuva parove u poretku um etanja, a u po-
zadini skupa TreeSet nalazi se m apa TreeMap koja uvek daje uređen skup. Realizaciju bi-
rate u skladu s ponašanjem koje vam je potrebno.
Ponekad različite realizacije odredenogkontejnera im aju neke zajedničke operacije, ali
su perform anse tih operacija različite. U tom slučaju, realizaciju birate na osnovu toga
koliko često koristite određenu operaciju i koliko brza ona m ora da bude. U takvim sluča-
jevim a se o razlikama izm eđu realizacija kontejnera m ože zaključivati i kroz ispitivanje
perform ansi.
/ / : k o n te jn e ri/T e s t.ja v a
/ / S t r u k t u r a za vremensko i s p i t i v a n j e k o n t e jn e r a .
p u b l i c a b s t r a c t cla ss Test<C> {
S t r i n g ime;
p u b l i c T e s t ( S t r i n g ime) { t h i s . i m e = ime; }
/ / R e d e f i n i š i t e ovu metodu za svaku v r s t u i s p i t i v a n j a .
/ / Vraća s t v a r n i b r o j p o n a v l ja n ja i s p i t i v a n j a .
a b s t r a c t i n t i s p i t ( C k o n t e j n e r , Pa ra m ls p it a p i ) ;
} III--
Svaki objekat tipa Test skladišti ime tog testa (ispitivanja). Kada pozovete m etodu
ispit( ), m orate joj dati kontejner koji treba ispitati i ,,prenosioca“ ili „objekat za prenos
podataka“ koji sadrži sve param etre tog ispitivanja. To su param etri velicina, jednak bro-
ju elem enata u kontejneru, i petlje, koji određuje broj iteracija za to ispitivanje. Ti para-
m etri se m ogu koristiti u svakom ispitivanju ali i ne m oraju.
Svaki kontejner će biti podvrgnut nizu poziva m etode is p it(), svaki p u t s različitim
param etrim a ispitivanja (objektom tipa Paramlspita), pa Paramlspita sadrži i dve sta-
tične m etode n iz ( ) za lako pravljenje nizova Paramlspita objekata. Prva verzija m etode
n iz ( ) p rim a listu prom enljivih argum enata koja naizm enično sadrži vrednosti velicina i
petlji, a druga verzija prim a listu iste vrste, sem što su vrednosti sm eštene u znakovne ni-
zove (objekte tipa String) - pa se može upotrebljavati za raščlanjivanje argum enata na
kom andnoj liniji:
p u b l i c c la s s Pa ra m ls p it a (
p u b lic fin a l i n t v e lic in a ;
p u b lic f in a l i n t p e t lje ;
p u b lic P a ra m ls p ita (in t v e lic in a , in t p e tlje ) {
th is .v e lic in a = v e lic in a ;
th is . p e t lje = p e tlje ;
}
/ / P r a v l j e n j e n iz a o b je k a ta t i p a Pa ra m ls p it a od sekvence p r o m e n l j i v i h
/ / argumenata:
p u b l i c s t a t i c P a r a m ls p it a [ ] n i z ( i n t . . . v r e d n o s t i ) {
in t v e lic in a = v re d n o s ti.le n g th /2 ;
P a r a m ls p it a [ ] r e z u l t a t = new P a r a m l s p i t a [ v e l i c i n a ] ;
i n t n = 0;
f o r ( i n t i = 0; i < v e l i c i n a ; i+ +)
r e z u l t a t [ i ] = new P a r a m I s p i t a ( v r e d n o s t i [ n + + ] , v r e d n o s t i [ n + + ] ) ;
retu rn r e z u lta t;
}
/ / P r e t v o r i n iz o b je k a ta t i p a S t r i n g u n i z o b je k a ta t i p a P a r a m ls p it a :
p u b l i c s t a t i c P a r a m ls p it a [ ] n i z ( S t r i n g [ ] v r e d n o s t i ) {
i n t [ ] v r d n s t i = new i n t [ v r e d n o s t i . l e n g t h ] ;
f o r f i n t i = 0; i < v r d n s t i . 1 e n g th ; i++)
v r d n s t i[ i] = In te g e r.d e c o d e (v re d n o s ti[i]);
retu rn n iz ( v r d n s t i) ;
}
} ///:-
S truk turu za ispitivanje koristite tako što određenoj m etodi Tester.pokreni() (to su
prigodne preklopljene generičke m etode zahvaljujući kojima ne m orate toliko da kucate)
prosledite kontejner koji treba ispitati i Listu Test objekata. Tester.pokreni() poziva od-
govarajući preklopljeni konstruktor, a zatim poziva m etodu vrem enskoIspit() koja
izvršava svako ispitivanje navedeno u listi za taj kontejner. vrem enskoIspit() ponavlja
svako ispitivanje za svaki od Paramlspita objekata u listi listaParam. Pošto se listaParam
inicijalizuje pom oću statičnog niza podrazumevaniParam, zadavanjem novih vrednosti
niza podrazumevaniParam možete da prom enite param etre listaParam za sva ispiti-
686 Misliti na Javi
vanja, a za jedno ispitivanje param etre listaP aram m ožete da prom enite prosleđivanjem
liste listaP aram prilagođene za to ispitivanje:
/ / : k o n te jn e ri/T e s te r.ja v a
/ / P r im e n ju je Test o b je k t e na l i s t e r a z l i č i t i h k o n t e jn e r a .
im p o r t j a v a . u t i l
p u b l i c c la s s Tester<C> {
p u b l i c s t a t i c i n t s i r i n a P o l j a = 8;
p u b l i c s t a t i c P a r a m ls p it a G podrazumevaniParam= P a r a m l s p i t a . n i z (
10, 5000, 100, 5000, 1000, 5000, 10000, 50 0 );
/ / R e d e f i n i š i t e ovo da b i s t e m o d i f i k o v a l i i n i c i j a l i z a c i j u
/ / pre i s p i t i v a n j a :
protected C i n i c i j a l i z u j ( i n t v e lic in a ) { re tu rn ko n tejne r; }
protected C ko n tejne r;
p r i v a t e S t r i n g n a s lo v =
p riv a te L ist< T e st< C » is p it iv a n ja ;
p riv a te s t a t i c S trin g p o lje S tr in g () {
r e t u r n "%" + s i r i n a P o l j a + " s " ;
}
p riv a te s t a t ic S trin g p o lje B ro jO {
r e t u r n "%" + s i r i n a P o l j a + " d " ;
}
p r i v a t e s t a t i c i n t s i r i n a V e l i c i n e = 5;
p r i v a t e s t a t i c S t r i n g p o l j e V e l i c i n e = "%" + s i r i n a V e l i c in e + " s " ;
p r i v a t e P a r a m ls p it a [ ] lis t a P a r a m = podrazumevaniParam;
p u b l i c T e ste r(C k o n t e j n e r , L i s t < T e s t < C » i s p i t i v a n j a ) {
t h is . k o n t e jn e r = k o ntejner;
t h i s . is p itiv a n ja = is p itiv a n ja ;
i f ( k o n t e j n e r != n u l 1)
n a slo v = k o n t e j n e r . g e t C l a s s ( ) .getSim ple N am e();
}
p u b l i c T e s te r(C k o n t e j n e r , L i s t < T e s t < C » i s p i t i v a n j a ,
P a r a m ls p it a [ ] lis t a P a r a m ) {
th is (k o n te jn e r, is p itiv a n ja ) ;
t h i s . 1istaParam = lis t a P a r a m ;
}
p u b l i c v o id z a d a jN a s lo v ( S t r in g novNaslov) {
n a slo v = novNaslov;
}
/ / Pomoćne g e n e r ič k e metode :
p u b l i c s t a t i c <C> v o id pokre ni (C k n t n r , L i s t < T e s t < C » i s p i t i va nja ) {
new T e s t e r < C > ( k n t n r , i s p i t i v a n j a ) . v r e m e n s k o ls p it ( ) ;
}
p u b l i c s t a t i c <C> vo id p o kre n i( C k n t n r ,
L i s t < T e s t < C » i s p i t i v a n j a , P a r a m ls p it a [ ] lis ta P a r a m ) {
new T e s t e r < C > ( k n t n r, i s p i t i v a n j a , 1is t a P a r a m ) . v r e m e n s k o ls p it ( ) ;
}
Poglavlje 17: Detaljno razmatranje kontejnera 687
p r i v a t e v o id p r i k a z i Z a g l a v l j e ( ) {
/ / I z r a č u n a j š i r i n u i dopuni znacima
in t s ir in a = s irin a P o lja * is p it iv a n ja . s iz e ( ) + s irin a V e lic in e ;
i n t d u z in a C r t ic a = s i r i n a - n a s l o v . l e n g t h ( ) - 1;
S t r i n g B u i l d e r z g l a v l j e = new S t r i n g B u i l d e r ( s i r i n a ) ;
f o r ( i n t i = 0 ; i < d u z i n a C r t i c a / 2 ; i+ + )
z g la v lje .a p p e n d ( '- ');
z g la vlje .a p pe n d O ' ) ;
z g la v lje .a p p e n d (n a s lo v );
z g la v lje .a p p e n d (' ' ) ;
f o r ( i n t i = 0; i < d u z i n a C r t i c a / 2 ; i+ + )
z g la v lje .a p p e n d ( '- ');
S y s te m .o u t.p rin tln (z g la v l j e ) ;
/ / I s p i s i v a n j e z a g l a v l j a ko lo n a :
S ys te m .o u t.fo rm a t(p o lje V e lic in e , " v e l . " ) ;
fo r(T e s t i s p i t : is p itiv a n ja )
S ys te m .o u t.fo rm a t(p o lje S trin g (), is p it . im e ) ;
S y s te m .o u t.p rin tln ();
}
/ / Pokreni i s p i t i v a n j a za ovaj k o n t e j n e r :
p u b l i c v o id v r e m e n s k o I s p i t( ) {
p rik a z iZ a g la v lje O ;
f o r ( P a r a m I s p i t a param : li s t a P a r a m ) {
S y s t e m . o u t . f o r m a t ( p o l j e V e l i ci ne, p a ra m . v e li c i n a ) ;
f o r(T e s t< C > i s p i t : i s p i t i v a n j a ) {
C kkontejner = i n i c i j a l i z u j(p a ra m .v e li c in a ) ;
lo ng s t a r t = System.nanoTime();
/ / Poziv r e d e f i n i s a n e metode:
i n t p o n a v l ja n ja = i s p i t . i s p i t ( k k o n t e j n e r , param);
long t r a j a n j e = System.nanoTime() - s t a r t ;
lo ng vremeZaJednoPonavljanje = t r a j a n j e / p o n a v l ja n ja ;
/ / Nanosekunde
S y s t e m . o u t . f o r m a t ( p o l j e B r o j ( ) , vrem eZaJednoPonavlja nje );
}
S y s te m .o u t.p rin tln ();
}
}
} ///:-
M etode p oljeString() i p oljeB roj() proizvode znakovne nizove za form atiranje rezul-
tata prilikom ispisa. Standardnu širinu za form atiranje m enjate m odifikovanjem statične
vrednosti sirinaPolja. Metoda prikaziZaglavlje() form atira i ispisuje zaglavlje s podaci-
ma iz svakog ispitivanja.
Ako vam je potrebna specijalna inicijalizacija, redefinišite m etodu in icijalizu j(). O na
proizvodi inicijalizovan objekat kontejner odgovarajuče veličine - možete modifikovati
postojeći objekat kontejner ili napraviti novi. U m etodi isp it() vidite da se rezultat hvata
u lokalnoj referenci nazvanoj kontejner, što om ogućuje da uskladišteni član kontejner
zam enite p otp u n o drugačije inicijalizovanim kontejnerom .
688 Misliti na Javi
Povratna vrednost svake m etode T est.ispit() m ora biti broj operacija obavljenih to-
kom tog ispitivanja, što se koristi za izračunavanje broja nanosekundi potrebnih za svaku
operaciju. Imajte u vidu da m etoda S ystem .nanoT im e() obično daje vrednosti čija gra-
nularnost prem ašuje jedan (i m enja se u zavisnosti od računara i operativnog sistema), a
to će prouzrokovati određenu količinu šum a u rezultatim a.
Rezultati se m ogu m enjati u zavisnosti od računara na kojem se ispitivanja obavljaju;
ova ispitivanja su nam enjena sam o za relativno poređenje perform ansi različitih kontej-
nera, a ne za apsolutno m erenje tih perform ansi.
p u b l i c c la s s P e rfo rm a n seL is ta {
s t a t i c Random s lu c a ja n = new Random();
s t a t i c i n t p o n a v l ja n ja = 1000;
s t a t ic L is t< T e s t< L is t< In te g e r» > i s p it iv a n ja =
new A r r a y L is t < T e s t < L i s t < I n t e g e r > » ( ) ;
s t a t i c L i s t < T e s t < L i n k e d L i s t < I n t e g e r > » is p it iv a n ja R e d a Z a C e k a n je =
new A r r a y L i s t < T e s t < L i n k e d L i s t < I n t e g e r > » ( ) ;
s ta tic {
is p itiv a n ja .a d d (n e w T e s t< L is t< In te g e r » ( " a d d " ) {
i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) {
in t p e tlje = p i . p e tlje ;
i n t v e lic in a L iste = p i . v e l i c i n a ;
f o r ( i n t i = 0 ; i < p e t l j e ; i+ + ) {
1i s t a . c l e a r ( ) ;
f o r ( i n t j = 0; j < v e l i c i n a L i s t e ; j+ + )
lis ta .a d d (j);
}
return p e t lje * v e lic in a L is te ;
}
});
is p i t i v a n j a . a d d ( n e w T e s t < L i s t < I n t e g e r » ( " g e t " ) {
i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) {
i n t p e t lje = p i . p e t l j e * p o n a v lja n ja ;
in t v e lic in a L is te = l is t a . s iz e ( ) ;
f o r ( i n t i = 0; i < p e t l j e ; i++)
Poglavlje 17: Detafjno razmatranje kontejnera 689
1i s t a . g e t ( s l u c a j a n . n e x t I n t ( v e l i c i naLi s t e ) ) ;
return p e t lje ;
}
});
is p itiv a n ja .a d d (n e w T e s t< L is t< In te g e r » ( " s e t" ) {
i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a ra m ls p ita p i ) {
i n t p e t l j e = p i . p e t l j e * p o n a v lja n ja ;
in t v e lic in a L is te = lis t a . s iz e ( ) ;
f o r ( i n t i = 0; i < p e t l j e ; i+ + )
1i s t a . s e t ( s l u c a j a n . n e x t l n t ( v e l i c i n a L i s t e ) , 4 7 );
retu rn p e t lje ;
}
});
is p itiv a n ja .a d d (n e w T e s t< L is t< In te g e r » ( " ite ra d d " ) {
i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p it a p i ) {
f i n a l i n t PETLJE = 1000000;
i n t p o lo v in a = l i s t a . s i z e ( ) / 2;
L is tIte ra to r< In te g e r> i t = lis t a . lis t lt e r a t o r ( p o lo v in a ) ;
f o r ( i n t i = 0; i < PETLJE; i+ + )
it.a d d (4 7 );
r e t u r n PETLJE;
}
});
is p itiv a n ja .a d d (n e w T e s t< L is t < I n t e g e r » ( " in s e r t " ) {
i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) {
in t p e tlje = p i.p e tlje ;
f o r ( i n t i = 0; i < p e t l j e ; i++)
l i s t a . a d d ( 5 , 4 7 ) ; / / M in im iz o v a n je t r o š k o v a
/ / nasumičnog p r i s t u p a n j a
return p e t lje ;
}
});
i s p itiva n ja .ad d (n ew T e s t< L is t< In te g e r» ("re m o v e ") {
i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a ra m ls p ita p i ) {
in t p e t lje = p i.p e tlje -,
in t v e lic in a = p i.v e lic in a ;
f o r ( i n t i = 0; i < p e t l j e ; i+ + ) {
1i s t a . c l e a r ( ) ;
1i s t a . a d d A l 1 (new B r o j a c k a L i s t a l n t e g e r a ( v e l i c i n a ) ) ;
whi 1e ( l i s t a . s i z e O > 5)
1i s t a . r e m o v e ( 5 ) ; / / M in im iz o v a n je tr o š k o v a
/ / nasumičnog p r i s t u p a n j a
}
retu rn p e t lje * v e lic in a ;
}
});
/ / I s p i t i v a n j a ponašanja reda za če ka n je :
is p it iv a n ja R e d a Z a C e k a n je .a d d (n e w T e s t < L in k e d L i s t<
In te g e r» ("a d d F irs t") {
690 Misliti na Javi
}
return p e t lje * v e lic in a ;
}
});
}
s t a t i c c la s s I s p i t i v a c L i s t a extends T e s t e r < L i s t < I n t e g e r » {
p u b lic Is p itiv a c L is ta ( L is t< In te g e r > k o n tejne r,
L is t< T e s t< L is t< In te g e r> » is p itiv a n ja ) {
s u p e r ( k o n t e jn e r , i s p i t i v a n j a ) ;
}
/ / Pre svakog i s p i t i v a n j a popuni do o d g ova ra ju će v e l i č i n e :
@0verride p r o t e c t e d L i s t < I n t e g e r > i n i c i j a l i z u j ( i n t v e l i c i n a ) {
k o n te jn e r.c le a r();
k o n t e j n e r . a d d A ll( n e w B r o j a c k a L i s t a I n t e g e r a ( v e l i c i n a ) ) ;
return kontejner;
}
/ / Pomočna metoda:
p u b l i c s t a t i c v o id p o k r e n i ( L i s t < I n t e g e r > l i s t a ,
L is t< T e s t< L is t< In te g e r» > is p it iv a n ja ) {
new I s p i t i v a c L i s t i ( 1 i s t a , i s p i t i v a n j a ) . v r e m e n s k o I s p i t ( ) ;
}
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
i f ( a r g s . 1 ength > 0)
Tester.podrazumevaniParam = P a r a m l s p i t a . n i z ( a r g s ) ;
/ / Na n iz se mogu p r i m e n i t i samo sle deća dva i s p i t i v a n j a :
T e s te r< L is t< In te g e r» is p itN iz a =
new T e s t e r < L i s t < I n t e g e r » ( n u l l , i s p i t i v a n j a . p o d L i s t a ( l , 3 ) ) {
/ / Ovo će b i t i pozvano pre svakog i s p i t i v a n j a .
/ / P r o iz v o d i l i s t u n e p ro m e n ljiv e v e l i č i n e s nizom u p o z a d i n i :
@0verride p r o t e c t e d
L is t < I n t e g e r > i n i c i j a l i z u j ( i n t v e l i c i n a ) {
In te g e r[] ia = G e n e ra te d .n iz (In te g e r.c la s s ,
new C o u n t ingG enerator. I n t e g e r O , s i z e ) ;
return A r r a y s . a s L is t ( ia ) ;
}
};
is p i t N i z a . z a d a j N a s l o v ( " N i z kao l i s t a " ) ;
is p itN iz a .v re m e n s k o Is p it();
Tester.podrazu meva ni Param= Pa ra m ls p it a . n i z (
10, 5000, 100, 5000, 1000, 1000, 10000, 20 0 );
i f ( a r g s . l e n g t h > 0)
Tester.podrazumevaniParam = P a r a m l s p i t a . n i z ( a r g s ) ;
Is p itiv a c L is ta .p o k re n i(n e w A rra y L is t< In te g e r> (), i s p i t i v a n j a ) ;
I s p i t i v a c L i s t a . p o k r e n i (new L i n k e d L i s t < I n t e g e r > ( ) , i s p i t i v a n j a ) ;
I s p i t i v a c L i s t a . p o k r e n i (new V e c t o r < I n t e g e r > ( ) , i s p i t i v a n j a ) ;
T e s t e r . s i r i n a P o l j a = 12;
T e s t e r < L i n k e d L i s t < I n t e g e r » is p it iv a n je R e d a Z a C e k a n je =
692 Misliti na Javi
/ / : k o n t e j n e r i / G r a n i ceOdRandom.java
/ / Daje l i metoda Math.random() b ro je v e 0 . 0 i 1.0?
/ / {RunByHand}
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s GraniceOdRandom {
s t a t i c v o id u p o tr e b a ( ) {
p rin t("U p o tre b a :");
p rin t ( " \ tG r a n ic e 0 d R a n d o m d o n j a " ) ;
p rin t ( " \ tG r a n ic e 0 d R a n d o m g o r n j a " ) ;
S y s te m .e x it(l);
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
i f ( a r g s . l e n g t h != 1) u p o t r e b a ( ) ;
if(a rg s .e q u a ls ("d o n ja ")) {
w h i1e(Math .random() != 0 .0 )
; / / Pokušavaj i d a l j e
p rin t("D a la 0 .0 !" ) ;
}
e ls e i f ( a r g s . e q u a l s ( " g o r n j a " ) ) {
w h i1e (M a th . random() != 1.0)
; / / Pokušavaj i d a l j e
p rin t("D a la 1 .0 !" ) ;
}
el se
u p o tr e b a O ;
}
) III--
Program pokrečete upisivanjem jedne od sledečih instrukcija na kom andnu liniju:
ja v a GraniceOdRandom donja
ili
ja v a G r a n iceOdRandom g o rn ja
696 Misliti na Javi
/ / : k o n te jn e r i/P e r fo rm a n s e S k u p o v a .ja v a
/ / Pokazuje r a z l i č i t e performanse Skupova.
/ / { A rg s : 100 5000} Malo, da b i t e s t i r a n j e b u i l d a b i l o k r a t k o
im p o r t j a v a . u t i l . * ;
p u b l i c c la s s PerformanseSkupova {
s t a t i c L is t < T e s t < S e t < I n t e g e r » > i s p i t i v a n j a =
new A r r a y L i s t < T e s t < S e t < I n t e g e r > » ( ) ;
s ta tic {
is p itiv a n ja .a d d (n e w T e s t< S e t< In te g e r» ("a d d ") {
i n t i s p i t ( S e t < I n t e g e r > skup, P a r a m ls p ita p i ) {
in t p e tlje = p i.p e tlje ;
in t v e lic in a = p i. v e lic in a ;
f o r ( i n t i = 0; i < p e t l j e ; i+ + ) {
s k u p .c le a r();
f o r ( i n t j = 0; j < v e l i c i n a ; j+ + )
s k u p .a d d (j);
}
retu rn p e t lje * v e lic in a ;
}
});
is p itiv a n ja .a d d (n e w T e s t< S e t< In te g e r » ("c o n ta in s ") {
i n t i s p i t ( S e t < I n t e g e r > skup, P a r a m ls p ita p i ) {
i n t p e t l j e = pi . p e t l j e ;
i n t opseg = p i . v e l i c i n a * 2;
f o r ( i n t i = 0; i < p e t l j e ; i+ + )
f o r ( i n t j = 0; j < opseg; j+ + )
s k u p .c o n ta in s (j);
r e t u r n p e t l j e * opseg;
}
});
is p itiv a n ja .a d d (n e w T e s t< S e t < I n t e g e r » ( " it e r a t e " ) {
i n t i s p i t ( S e t < I n t e g e r > skup, P a r a m ls p it a p i ) {
i n t p e t l j e = p i . p e t l j e * 10;
f o r ( i n t i = 0; i < p e t l j e ; i+ + ) {
Poglavlje 17: Detaljno razmatranje kontejnera 697
Perform anse skupa HashSet po pravilu su bolje od onih skupa TreeSet, naročito pri
dodavanju elem enata i traganju za njim a (m etodom co n ta in s()), što su dve najvažnije
operacije. TreeSet postoji zato što svoje elem ente održava u uređenom poretku, pa ga
upotrebljavam o sam o onda kada nam treba uređen Set. Zbog unutrašnje stru k tu re pot-
rebne za uređivanje i zato što se iteriranje češče izvodi, ono je obično brže u kontejneru
tipa TreeSet nego tipa HashSet.
O bratite pažniu na to da su um etanja u skupu LinkedHashSet dugotrajnija nego u
skupu HashSet; to važi zato što održavanje ulančane Iiste prouzrokuje dođatne troškove,
jer se dodaje onim a za kontejner s transform isanjem ključa.
Vežba 34: ( 1) Izm enite PerformanseSkupova.java tako da objekti tipa Set skladište zna-
kovne nizove (String), a ne Integere. Za pravljenje ispitnih vrednosti upotrebite Genera-
to r i/ poglavlja I\ri:o \’i.
698 Misliti na Javi
/ / : ko n te jn e r i/P e r fo rm a n s e M a p a .ja v a
/ / Pokazuje r a z l i č i t e performanse r a z n ih Mapa.
/ / { A r g s : 100 5000} Malo, da bi t e s t i r a n j e b u i l d a b i l o k r a t k o
im p o rt j a v a . u t i l
p u b l i c c la s s PerformanseMapa {
s t a t i c L is t < T e s t < M a p < I n t e g e r , I n t e g e r » > i s p i t i v a n j a =
new A r r a y L i s t < T e s t < M a p < I n t e g e r , I n t e g e r > » ( ) ;
s ta tic {
is p i t i v a n j a . a d d ( n e w T e s t < M a p < I n t e g e r , I n t e g e r » ( " p u t " ) {
i n t i s p i t ( M a p < I n t e g e r , I n t e g e r > mapa, P a ra m ls p it a p i ) {
in t p e tlje = p i.p e tlje ;
in t v e lic in a = p i. v e lic in a ;
f o r ( i n t i = 0; i < p e t l j e ; i+ + ) {
m a p a .c le a r();
f o r ( i n t j = 0; j < v e l i c i n a ; j+ + )
m a p a . p u t ( j, j ) ;
}
retu rn p e t lje * v e lic in a ;
}
});
is p i t i v a n j a . a d d ( n e w T e s t < M a p < I n t e g e r , I n t e g e r » ( " g e t " ) {
i n t i s p i t ( M a p < I n t e g e r , I n t e g e r > mapa, P a r a m ls p ita p i ) {
in t p e tlje = p i . p e tlje ;
i n t opseg = p i . v e l i c i n a * 2;
f o r ( i n t i = 0; i < p e t l j e ; i+ + )
f o r ( i n t j = 0; j < opseg; j+ + )
m a p a .g e t(j);
r e t u r n p e t l j e * opseg;
}
f);
i s p i t i v a n j a . a d d ( n e w Test<Map<Integer, I n t e g e r » ( " i t e r a t e " ) {
i n t i s p i t (M a p < I n t e g e r ,I n t e g e r> mapa, Pa ra m ls p it a p i ) {
i n t p e t l j e = p i . p e t l j e * 10;
f o r ( i n t i = 0; i < p e t l j e ; i ++) {
I t e r a t o r i t = mapa. e n t r y S e t ( ) . i t e r a t o r ( ) ;
w h i1e ( i t . hasNext( ) )
it.n e x t();
)
r e t u r n p e t l j e * m a p a . s iz e ( ) ;
}
});
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
i f ( a r g s . l e n g t h > 0)
Poglavfje 17: Detaljno razmatranje kontejnera 699
Tester.podrazumevaniParam = P a r a m l s p i t a . n i z ( a r g s ) ;
T e s t e r . p o k r e n i(n e w T r e e M a p < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ;
T e s t e r . p o k r e n i(n e w H a s h M a p < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ;
T e s t e r . p o k r e n i(n e w L in k e d H a s h M a p < I n t e g e r , I n t e g e r > ( ) . i s p i t i v a n j a ) ;
T e s te r.p o k re n i(
new I d e n t i t y H a s h M a p < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ;
T e s t e r . p o k r e n i(n e w W eakH a sh M a p < In te g e r,In te g e r> (), i s p i t i v a n j a ) ;
T e s t e r . p o k r e n i( n e w H a s h t a b l e < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ;
}
} /* Is p is : (p r im e r)
v e l. put get i t e r a t e
10 748 168 100
100 506 264 76
1000 771 450 78
10000 2962 561 83
-------- HashMap —
v e l. put get i t e r a t e
10 281 76 93
100 179 70 73
1000 267 102 72
10000 1305 265 97
-- LinkedHashMap
v e l. put get i t e r a t e
10 354 100 72
100 273 89 50
1000 385 222 56
10000 2787 341 56
• IdentityHashMap ----------
v e l. put get i t e r a t e
10 290 144 101
100 204 287 132
1000 508 336 77
10000 767 266 56
WeakHashMap -
vel . put get i t e r a t e
10 484 146 151
100 292 126 117
1000 411 136 152
10000 2165 138 555
----- Hashtable - -
v e l. put get i t e r a t e
10 264 113 113
100 181 105 76
1000 260 201 80
10000 1245 134 77
*///:-
700 Misliti na Javi
Uslužne metode
Za kontcjnere postoji više sam ostalnih uslužnih m etoda, napisanih u obliku statičnih me-
toda klase java.util.Collections. Već ste u po /n ali neke od njih, kao što su ad d A ll(),
reverscO rder() i binarySearch(). I ’ sledećoj tabeli navodim o ostale (sinhronizovane i
neprom enljive uslužne m etode biće opisane u narednim odeljcima). U tabeli su generički
tipovi upotrebljeni sam o tam o gde su relevantni:
//: kontejneri/UsluzneMetode.java
// Jednostavni primeri usiužnih metoda klase Collections.
import ja v a . u t i 1 .*;
import static net.mindview.uti 1 .P r i n t .* ;
p u b l i c c la s s L is t S o r t S e a r c h {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) {
L is t< S trin g > l i s t a =
new A r r a y L i s t < S t r i n g > ( U t i 1i t i e s . 1 i s t a ) ;
1i s t a . a d d A l 1 ( U t i 1i t i e s . 1i s t a ) ;
p rin t(lis ta );
C o l l e c t i o n s . s h u f f l e ( l i s t a , new Random(47));
p rin t("Is p re tu ra n a : " + lis t a ) ;
/ / U k la n ja n je p o s l e d n j i h elemenata pomoću L i s t l t e r a t o r a :
L i s t I t e r a t o r < S t r i n g > i t = 1i s t a . 1i s t l t e r a t o r ( l O ) ;
w h i l e ( i t . hasN ext( ) ) {
it.n e x t();
it.re m o v e ();
}
p rint("S kraćena: " + l i s t a ) ;
C o lle c tio n s .s o rt(1i s t a ) ;
print("U re đ e n a : " + l i s t a ) ;
S trin g k lju c = 1i s t a . g e t (7 );
i n t indeks = C o l1e c t i o n s . b i n a r y S e a r c h ( l i s t a , k l j u c ) ;
p r i n t ( " M e s t o k l j u č a " + k l j u c + " j e " + indeks +
" , l i s t a . g e t ( " + indeks + " ) = " + 1 i s t a . g e t ( i n d e k s ) ) ;
C o l l e c t i o n s . s o r t ( l i s t a , String.CASE_INSENSITIVE_ORDER);
p r i n t ( " U r e đ e n a bez o b z ir a na v e l i k a i mala s lo v a : " + l i s t a ) ;
k l j u c = 1i s t a . g e t ( 7) ;
indeks = C o l l e c t i o n s . b i n a r y S e a r c h ( l i s t a , k l j u c ,
String.CASE INSENSITIVE ORDER);
706 Misliti na Javi
Kao pri pretraživanju i uređivanju nizova, i ovde važi sledeće: ukoliko listu uredite po-
m oću nekog kom paratora, pom oću istog kom paratora m orate obaviti i b inarno pretraži-
vanje - m etodom b in a ry S ea rc h ().
U ovom program u prikazana je i m etoda sh u ffle() klase C ollections koja nasum ično
m enja redosled elemenata liste. Jedan L istlterator je napravljen na određenom mestu ispre-
turane (engl. shuffleđ) liste i upotrebljen za uklanjanje elemenata od tog mesta do kraja liste.
Vežba 40: (5) Napravite klasu s dva objekta tipa S tring i realizujte u njoj interfejs Com -
p arab le tako da poređenje uzima u obzir sam o prvi objekat tipa String. Popunite jedan
niz i jedan objekat tipa A rrayList objektim a vaše klase, koristeći generator RandoinG e-
nerator. Pokažite da se sortiranje obavlja ispravno. Potom napravite C o m p arato r koji
uzim a u obzir samo drugi objekat tipa S trin g i pokažite da je sortiranje opet ispravno.
Obavite i binarno pretraživanje pom oću svoje realizacije interfejsa C om parator.
V ežba41: (3) Izmenite klasu iz prethodne vežbe tako da radi s kontejnerim a tipa H ashSet
i kao ključ u kontejnerim a tipa H ashM ap.
V ežba42: (2) Izmenite vežbu 40 tako da lista bude sortirana abecedno.
/ / : k o n t e j n e r i /S a m o Za C it a n je .ja v a
/ / Pomoću metoda C o l1e c t i o n s . u n m o d i f i a b l e.
im p o rt j a v a . u t i l . * ;
im p o rt n e t . m i n d v i e w . u t i l . * ;
Poglavlje 17: Detafjno razmatranje kontejnera 707
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s SamoZaCitanje {
s t a t i c C o l l e c t i o n < S t r i n g > podaci =
new A r r a y L i s t < S t r i n g > { C o u n t r i e s . i m e n a ( 6 ) ) ;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
C o lle c tio n < S trin g > c =
C o lle c tio n s .u n m o d ifia b le C o lle c tio n (
new A r r a y L i s t < S t r i n g > ( p o d a c i ) ) ;
p r i n t ( c ) ; / / Č ita n je fu n k c io n iš e
/ / ! c . a d d ( " j e d a n " ) ; / / Nema menjanja
S e t < S t r in g > s = C o l l e c t i o n s . u n m o d i f i a b l e S e t (
new H a s h S e t < S t r in g > ( p o d a c i) ) ;
p r i n t ( s ) ; / / Č i t a n j e f u n k c io n i š e
/ / ! s . a d d ( " j e d a n " ) ; / / Nema menjanja
/ / Za S o rte dS e t:
S e t < S t r in g > ss = C o l l e c t i o n s . u n m o d i f i a b l e S o r t e d S e t (
new T re e S e t < S t r in g > ( p o d a c i) ) ;
/ / Za SortedMap:
M a p < S t r in g , S t r in g > sm =
C o l1e c t i ons.unmodi f ia b le S o r te d M a p (
new T r e e M a p < S t r in g . S t r in g > ( C o u n t r ie s . g l a v n i _ g r a d o v i ( 6 ) ) ) ;
}
} / * Is p is :
[ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI]
ALGERIA
[BURKINA FASO, BURUNDI, BOTSWANA, BENIN, ANGOLA, ALGERIA]
{BURKINA FASO=Ouagadougou, BURUNDI=Bujumbura, BOTSWANA=Gaberone,
BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers}
* ///:-
Poziv neprom enljivc m etode za određeni tip ne prouzrokuje proveru u vreme pre-
vodenja, ali nakon što se ta transform acija obavi, poziv bilo koje m etode koja menja
sadržaj tog kontejnera prouzrokuje U n su pp orted O peratio n E x cep tio n .
708 Misliti na Javi
U svakom slučaju, kontejner m orate p o p u n iti sm islenim podacim a pre nego što ga
pretvorite u verziju samo za čitanje. N akon učitavanja, najbolje je zam eniti postojeću re-
ferencu kontejnera referencom koju proizvodi poziv ,,unm odifiable“ m etode. Time izbe-
gavate rizik od nehotičnog pokušaja m enjanja sadržaja nakon što ste ga učinili
neprom enljivim . S druge strane, ova alatka om ogućuje i da prom enljivi kontejner zadrži-
te u obliku privatnog člana određene klase i da iz poziva neke m etode vratite referencu tog
kontejnera sam o za čitanje. Dakle, kontejner m ožete izm eniti iznutra iz klase, a svi drugi
m ogu sam o da ga čitaju.
p u b l i c c la s s S i n h r o n i z a c i j a {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) {
C o lle c tio n < S trin g > c =
C o l1e c t i o n s. s ynchro ni zedCol 1e c t i o n (
new A r r a y L i s t < S t r i n g > ( ) ) ;
L i s t < S t r i n g > l i s t a = C o l1e c t i o n s . s y n c h r o n i z e d L i s t (
new A r r a y L i s t < S t r i n g > ( ) ) ;
S e t < S t r in g > s = C o l1e c t i o n s . s y n c h r o n iz e d S e t (
new H a s h S e t < S t r in g > ( ) ) ;
S e t < S t r in g > ss = C o l1e c t i o n s . s y n c h ro n iz e d S o r te d S e t(
new T r e e S e t < S t r i n g > ( ) ) ;
M a p < S t r in g , S t r in g > m = C o l1e ctio n s .s y n c h r o n iz e d M a p (
new H a s h M a p < S t r in g , S t r in g > ( ) ) ;
M a p < S t r in g , S t r in g > sm =
C o l1e c t io n s . s y n c h r o n i zedSortedMap(
new T r e e M a p < S t r in g , S t r i n g > ( ) ) ;
}
} ///:-
Brzo otkazivanje
Javini kontejneri imaju i m ehanizam koji sprečava da više procesa istovremeno menja
sadržaj kontejnera. Problem nastaje ako ste usred iteriranja kroz kontejner, a neki drugi
proces uskoči i um etne, ukloni ili izmeni neki objekat u tom kontejneru. Možda ste taj ele-
Poglavfje 17: Detaljno razmatranje kontejnera 709
m ent kontejnera već prošli, m ožda je on ispred vas, m ožda će se kontejner sm anjiti nakon
što pozovete m etodu s iz e () - scenarija za katastrofu im a koliko hoćete. Biblioteka Java
kontejnera upotrebljava m ehanizam brzog otkazivanja (engl. fail-fast); u kontejneru traži
izm ene koje nije prouzrokovao vaš proces lično. Ako otkrije da neko drugi m enja sadržaj
kontejnera, odm ah generiše izuzetak ConcurrentModificationException. Taj aspekat se
naziva „brzo otkazivanje“ - problem se ne traži naknadno, nekim složenijim algoritm om .
M ehanizam brzog otkazivanja je sasvim lako videti u praksi - dovoljno je napraviti ite-
rato r i zatim nešto dodati kolekciji na koju iterator pokazuje:
/ / : ko n te jn e ri/B rz o O tk a z iv a n je .ja v a
/ / Pokazuje ponašanje nazvano " b rz o o t k a z i v a n j e " .
im p o r t j a v a . u t i l . * ;
p u b l i c c la s s B r z o O tk a z iv a n je {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
C o l l e c t i o n < S t r i n g > c = new A r r a y L i s t < S t r i n g > ( ) ;
Ite ra to r< S trin g > i t = c . i t e r a t o r ( ) ;
c.add("N eki o b je k a t" ) ;
try {
S trin g s = i t . n e x t ( ) ;
} c a t c h ( C o n c u r r e n t M o d if ic a t io n E x c e p t io n e) {
S y s te m .o u t.p rin tln (e );
}
}
} / * Is p is :
j a v a . u t i 1 .Concu rrentModi f i c a t i o n E x c e p t i o n
* ///:-
Izuzetak se đogodio zato što je nešto sm ešteno u kontejner nakon pribavljanja iteratora
od tog kontejnera. M ogućnost da dva dela program a m enjaju isti kontejner proizvodi
neodređeno stanje, pa vas izuzetak obaveštava kako bi trebalo da izmenite kod —u ovom
slučaju, pribavite iterator tek nakon dodavanja svih elem enata u kontejner.
K ontejneri C oncu rren tH ash M ap , C opyO nW riteA rrayList i C opyO nW riteA rraySet
koriste tehnike kojim a se izbegava generisanje izuzetka C o n c u rre n t-
M odificationExceptions.
Čuvanje referenci
Biblioteka java.lan g .ref sadrži skup klasa koje om ogućuju veću fleksibilnost u
sakupljanju smeća. Te klase su naročito korisne kada radite s velikim objektim a koji m ogu
iscrpeti m em oriju. O d apstraktne klase Reference nasledene su tri klase: SoftReference,
W eakR eference i P hantom R eference. Svaka od njih sakupljaču smeća pruža drugačiji
nivo indirekcije ukoliko je objekat o kojem se radi dostižan sam o preko jednog od tih Re-
ference objekata.
Ako je objekat d ostiža n , m ože se pronaći negde u program u. To m ože značiti da im ate
običnu referencu steka koja upućuje pravo na objekat, ali mogli biste im ati i referencu
objekta koji sadrži referencu objekta o kojem se radi; tih međuveza m ože biti mnogo.
710 Misliti na Javi
Ukoliko je objekat dostižan, sakupljač smeća ga ne m ože osloboditi jer ga program još
uvek koristi. Ako objekat nije dostižan, ne postoji način da ga program upotrebi, pa je
bezbedno sakupiti ga u smeće.
Objekte tipa Reference koristite kada i dalje hoćete da čuvate referencu određenog ob-
jekta - taj objekat hoćete da dosegnete - ali takođe hoćete da om ogućite sakupljaču smeća
da taj objekat oslobodi. Dakle, postoji način da objekat koristite, ali ako se ukaže mo-
gućnost da se m em orija iscrpe, dozvoljavate da taj objekat bude oslobođen.
To se postiže korišćenjem objekta tipa Reference kao posrednika (engl. p roxy ) između
vas i obične reference. Sem toga, taj objekat ne sme im ati obične reference (one koje nisu
om otane u Reference objekte). Ako sakupljač smeća otkrije da je određeni objekat do-
stižan preko obične reference, taj objekat neće biti oslobođen.
Reference tipa SoftReference, VVeakReference i PhantomReference sve su ,,slabije“ i
odgovaraju različitim nivoima dostižnosti. Meke reference (engl. soft references) služe za
realizovanje keša, tj. ostava koje čuvaju m em oriju. Slabe reference (engl. weak references)
služe za realizovanje „kanonizujućih m apiranja (preslikavanja)" - gde se instance objekata
m ogu istovrem eno upotrebljavati na više mesta u program u, da bi se uštedela m em orija -
što ne sprečava da njihovi ključevi (ili vrednosti) budu sakupljeni u smeće. Fantomske re-
ference (engl. p h a n to m references) služe za planiranje operacija čišćenja „pre (memorijske)
sm rti“ na fleksibilniji način nego što bi to bilo moguće s Javinim m ehanizm om finalizacije.
Reference tipa SoftReference i WeakReference m ožete ali i ne m orate smestiti u ,,red
referenci za čekanje“ (ReferenceQueue) - tako je nazvan uredaj za akcije čišćenja ,,pre
(m em orijske) sm rti“) - dok se PhantomReference m ože napraviti sam o u tom Refe-
renceQueue. Evo jednostavnog prim era:
/ / : ko n te jn e ri/R e fe re n c e .ja v a
/ / Prim er o b je k a ta t i p a Reference
im p o r t j a v a . l a n g . r e f
im p o r t j a v a . u t i l . * ;
c la s s VeomaVeliki {
p r i v a t e s t a t i c f i n a l i n t VELICINA = 10000;
p r i v a t e long'[] l a = new 1ong[V ELICINA];
p r iv a te S trin g id e n t;
p u b l i c V e o m a V e lik i( S t r in g i d ) { id e n t = i d ; }
p u b lic S trin g to S tr in g O { re tu rn id e n t; }
p r o t e c t e d v o id f i n a l i z e ( ) {
S y s t e m . o u t . p r i n t l n ( " F i n a l i zo va n je " + i d e n t ) ;
}
}
p u b l i c c la s s Reference {
p r i v a t e s t a t i c ReferenceQueue<VeomaVeli k i> rq =
new ReferenceQueue<VeomaVeli k i > ( ) ;
p u b l i c s t a t i c v o id checkQueue() {
Reference<? extends VeomaVeliki> urd = r q . p o l l ( ) ;
i f ( u r d != n u l l )
Poglavlje 17: Detaljno razmatranje kontejnera 711
Kada pokrenete ovaj program (preusm erite njegov izlaz u tekstualnu datoteku da biste
rezultate mogli da gledate stranicu po stranicu), videćete da objekti bivaju sakupljani u
smeće iako im i dalje m ožete pristupati preko objekta tipa Reference - referencu objekta
pribavljate m etodom g e t ( ). Videćete i to da ReferenceQueue uvek proizvodi null objekat
tipa Reference. Da bi taj objekat postao upotrebljiv, nasledite određenu klasu Reference,
a novoj klasi dodajte upotrebljivije metode.
712 Misliti na Javi
Kontejner WeakHashMap
Biblioteka kontejnera im a posebnu m apu za skladištenje slabih referenci: to je W eak-
HashMap. Ta klasa olakšava pravljenje kanonizovanih m apiranja (preslikavanja). Takvim
m apiranjem štedite m em oriju jer pravite sam o jednu instancu određene vrednosti. Kada
program u zatreba ta vrednost, on potraži postojeći objekat u tom m apiranju i upotrebi
njega (um esto da ga pravi od nule). M apiranje m ože napraviti vrednosti u sklopu svoje
inicijalizacije, ali se vrednosti češće prave tek na zahtev.
Pošto je ovo tehnika za uštedu m em orije, baš je podesno što WeakHashMap om o-
gućuje skupljaču smeća da autom atski čisti njene ključeve i vrednosti. S ključevima i
vrednostim a koje želite da smestite u m apu WeakHashMap ne m orate da radite ništa po-
sebno; sam a m apa ih autom atski om otava u objekte tipa WeakReference. O kidač koji
dozvoljava čišćenje jeste činjenica da se ključ više ne upotrebljava, kao što je prikazano u
narednom prim eru:
/ / : k o n t e j n e r i/ K a n o n s k o M a p ir a n j e . ja v a
/ / Prim e r s kontejnerom WeakHashMap.
im p o r t j a v a . u t i l
c la s s Element {
p r iv a te S trin g id e n t;
p u b l i c E le m e n t (S tr in g i d ) { i d e n t = i d ; }
p u b l i c S t r ir . g t o S t r i n g ( ) { r e t u r n i d e n t ; }
p u b l i c i n t hashCode() { r e t u r n i d e n t .h a s h C o d e ( ) ; }
p u b l i c boolean e q u a ls (O b je c t r ) {
r e t u r n r i n s t a n c e o f Element &&
i d e n t . e q u a l s ( ( ( E le m e n t)r ) . i d e n t ) ;
}
p r o t e c t e d v o id f i n a l i z e ( ) {
System .out. p r i n t ln ( " F in a liz o v a n je " +
g e t C l a s s ( ) .getSimpleName() + " " + i d e n t ) ;
}
}
c la s s K l j u c extends Element {
p u b lic K lju c (S trin g id ) { s u p e r(id ); }
}
p u b l i c c l a s s KanonskoMapir a n je {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) {
i n t v e l i c i n a = 1000;
/ / I l i i z a b e r i t e v e l i č i n u na komandnoj l i n i j i :
i f ( a r g s . l e n g t h > 0)
v e l i c i n a = new l n t e g e r ( a r g s [ 0 ] ) ;
Poglavlje 17: Detaljno razmatranje kontejnera 713
K l j u c [ ] k l j u c e v i = new K l j u c [ v e l i c i n a ] ;
WeakHashMap<Kljuc,Vrednost> mapa =
new WeakHashMap<Kljuc,Vrednost>();
f o r ( i n t i = 0; i < v e l i c i n a ; i+ + ) {
K l j u c k = new K l j u c ( I n t e g e r . t o S t r i n g ( i ) ) ;
Vrednost v = new V r e d n o s t ( I n t e g e r . t o S t r i n g ( i ) ) ;
i f ( i % 3 == 0)
k l j u c e v i [ i ] = k; / / Sačuvaj kao " p ra v e " r e f e r e n c e
m ap a .p u t(k , v ) ;
)
Syste m .g c();
}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta te ) * / / / : -
Vector i Enumeration
Jedini niz koji je mogao da se proširuje u Javi 1.0/1.1 bio je Vector,pa se dosta koristio. Nje-
govi nedostaci su suviše brojni da bi se ovde naveli (potražite prvo izdanje ove knjige na lo-
kaciji www.RruceEckel.coin). U osnovi, to je bio ArrayList s dugim , nezgodnim im enim a
metoda. U biblioteci kontejnera nove Jave, Vector je prilagođen da bi mogao da radi i kao
kolekcija i kao lista. To je pomalo čudno zato što bi neki ljudi mogli pom isliti kako je klasa
Vector poboljšana, a ona u stvari postoji samo da bi podržavala stari Java kod.
Verzija iteratora u Javi 1.0/1.1 drugačije se zvala - Enum eration - tj. nije korišćen ter-
min koji je svima poznat. Interfejs Enumeration je m anji od interfejsa Iterator i sadrži
sam o dve m etode s dugačkim imenima: boolean hasMoreElements( ) koja vraća true
ako kontejner sadrži još elemenata, i Object nextElement( ) koja vraća sledeći element
kolekcije ako on postoji (inače generiše izuzetak).
Enumeration je samo interfejs bez realizacije, pa ga čak i nove biblioteke ponekad ko-
riste, što nije preporučljivo ali je barem bezopasno. Iako bi prilikom pisanja novih pro-
gram a uvek trebalo koristiti Iterator, m orate znati da postoje biblioteke koje žele da vam
proslede objekat tipa Enumeration.
714 Misliti na Javi
O sim toga, pom oću m etode C o Ile ctio n s.e n u m eratio n () možete da napravite objekat
tipa E n u m e ra tio n za bilo koju kolekciju, kao u ovom prim eru:
/ / : k o n te jn e ri/N a b ra ja n je .ja v a
/ / Java 1 . 0 / 1 . 1 k la s e V e c t o r i Enumeration.
im p o r t j a v a . u t i l . * ;
im p o rtn e t.m in d v ie w .u til.*;
p u b l i c c la s s N a b ra ja n je {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
V e c t o r < S t r in g > v = new V e c t o r < S t r in g > ( C o u n t r ie s . n a m e s ( lO ) ) ;
En u m e ra tio n < Stri ng > e = v . e l e m e n t s ( ) ;
w h i1e ( e . hasMoreElements( ) )
S yste m .o u t.p rin t(e .n e x tE le m e n t() + ", " ) ;
/ / Pra vi Enumeration od k o l e k c i j e :
e = C o lle c t io n s . e n u m e r a t io n ( n e w A r r a y L i s t < S t r i n g > ( ) ) ;
}
} / * Is p is :
ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI,
CAMEROON, CAPE VERDE, CENTRAL AFRICAN REPUBLIC, CHAD
* ///:-
Da biste napravili objekat tipa Enumeration, pozovite m etodu elem en ts(), koju po-
tom m ožete da upotrebite za prolazak kroz listu.
U poslednjem redu pravi se objekat klase ArrayList i koristi m etoda en u m eration ()
za dobijanje objekta tipa Enumeration na osnovu iteratora klase ArrayList. Dakle, m ože-
te da koristite nove kontejnere i sa starim kodom u kom e se upotrebljava Enumeration.
Klasa Hashtable
Kao što ste videli u delovima ovog poglavlja posvećenim upoređivanju perform ansi, klasa
Hashtable je veoma slična klasi HashMap, čak su i imena m etoda ista. Zato nem a razloga
da se u novim program im a um esto klase HashMap koristi Hashtable.
Klasa Stack
Pojam steka objašnjen je ranije, u odeljku o klasi LinkedList. Klasa Stack iz Jave 1 .0 /1 .1
čudna je: um esto da koristi klasu Vector i kom poziciju, ona je izvedena (nasleđena) iz kla-
se Vector. Zato im a sva obeležja i ponašanje kiase Vector, uz dodatne funkcije steka. Teško
je razaznati da li su projektanti sm atrali da je to posebno korisno, ili se radi o neznanju;
ipak, jasno je da projekat nije revidiran pre puštanja u distribuciju, pa se i tako loš dizajn
jo š uvek sreće (ali ga vi nem ojte koristiti).
Evo jednostavnog prim era steka na koji se stavlja svaki red iz niza objekata tipa String.
Pokazano je kako je kao stek jednako lako koristiti ulanćanu Iistu (objekat tipa Linked-
List) ili stek napravljen u poglavlju Čuvcmje objekata:
Poglavfje 17: Detaljno razmatranje kontejnera 715
/ / : k o n t e j n e r i / S t e k o v i . ja v a
/ / P r i k a z i v a n j e k la s e Stack.
im p o r t j a v a . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s S te ko vi {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
S ta c k < S tr in g > s t k = new S ta c k < S trin g > ( ) ;
for(M esec m : M e s e c .va lu e s O )
s tk .p u s h (m .to S trin g ());
p rin t("s te k = " + s tk ) ;
/ / Stek kao V e cto r:
s tk . a d d E le m e n t ( " P o s le d n ji r e d " ) ;
p rin t("e le m e n t 5 = " + s tk .e le m e n tA t(5 ));
p r i n t ( " V a d i m e le m e n t e : " ) ;
w h i1e ( ! s t k . e m p t y ( ) )
p rin tn b (s tk .p o p () + " " ) ;
/ / Ulančana l i s t a kao s te k :
L i n k e d L i s t < S t r in g > l s t e k = new L i n k e d L i s t < S t r in g > ( ) ;
fo r(M esec m : M ese c .va lu e s ( ) )
1s te k .a d d F i r s t ( m . t o S t r i n g ( ) ) ;
p r i n t ( " 1 s te k = " + l s t e k ) ;
w h i1e ( ! 1s t e k . i sEmpty( ) )
p r i n t n b (1 s t e k . r e m o v e F ir s t ( ) + " " ) ;
Klasa BitSet
Klasa BitSet se koristi za efikasno čuvanje velike kolićine inform acija tipa u k lju č e n o -is-
ključeno. O na je efikasna sam o s tačke gledišta veličine; ako vam je potreban brz pristup,
treba da znate da je ova klasa nešto sporija od nizova prirodnog tipa.
Osim toga, m inim alna veličina skupa bitova je long, tj. 64 bita. To znači da klasa BitSet
neće biti efikasna za čuvanje kraćih podataka, npr. onih od 8 bitova. U tom slučaju, bolje
je da napravite sopstvenu klasu, ili sam o niz u koji ćete sm estiti podatke ako vam je bitna
veličina. (To će biti slučaj sam o ako pravite m nogo objekata koji sadrže liste inform acija
tipa uključeno-isključeno, a odluku treba doneti tek na osnovu rezultata program a z.a op-
timizaciju i ostalih m erenja. Ukoliko ste to odlućili sam o zato što ste sami nešto proglasili
za preveliko, imaćete veom a složen program i izgubićete m nogo vrem ena.)
O bičan kontejner se širi kada m u đodajete nove elem ente, a to radi i BitSet. Sledeći
prim er pokaz.uje kako radi klasa BitSet:
/ / : k o n t e j n e r i / B i t o v i . ja v a
/ / P r i k a z i v a n j e k la s e B i t S e t .
im p ort j a v a . u t i l
im p o r t s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ;
p u b l i c c la s s B i t o v i {
p u b l i c s t a t i c v o id i spi s i Bi t o v e ( B i t S e t b) {
p rin t( " b ito v i: " + b );
S t r i n g B u i 1d e r b b i t o v i = new S t r i n g B u i 1d e r ( ) ;
f o r ( i n t j = 0; j < b . s i z e ( ) ; j+ + )
b b i t o v i . a p p e n d ( b . g e t ( j ) ? "1" : " 0 " ) ;
p rin t("b it-m a s k a : " + b b it o v i) ;
)
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) {
Random rand = new Random(47);
/ / Uzmi b a j t najmanje t e ž i n e od n e x t l n t ( ) :
b yte b t = ( b y t e ) r a n d . n e x t l n t ( ) ;
B it S e t bb = new B i t S e t ( ) ;
f o r ( i n t i = 7; i >=0; i - - )
i f ( ( ( l « i ) & b t ) != 0)
b b .s e t(i);
el se
bb. c l e a r ( i );
Poglavlje 17: Detaljno razmatranje kontejnera 717
in t i t = ra n d .n e x tln t();
B i t S e t bi = new B i t S e t ( ) ;
f o r ( i n t i = 31; i >=0; i - - )
i f ( ((1 « i ) & it) != 0)
b i . s e t( i );
el se
b i . c le a r( i);
p rin t("v re d n o s t tip a in t : " + it);
is p is iB i to v e (b i);
/ / T e s t i r a j skupove b i t a >= 64 b i t a :
B i t S e t bl27 = new B i t S e t O ;
b l 2 7 . s e t (127);
p r i n t ( " b i t b r o j 127 j e u k l j u č e n : " + b l 2 7 ) ;
B i t S e t b255 = new B i t S e t ( 6 5 ) ;
b255. s e t ( 25 5 );
p r i n t ( " b i t b r o j 255 j e u k lj u č e n : " + b 2 5 5 ) ;
B i t S e t b1023 = new B i t S e t ( 5 1 2 ) ;
b 1023. s e t (1023);
b l 0 2 3 . s e t ( 1024);
p r i n t ( " b i t b r o j 1023 j e u k lj u č e n : " + b l0 2 3 ) ;
} / * Is p is : }
v r e d n o s t t i p a b y t e : -107
b i t o v i : { 0 , 2, 4, 7}
bi t - m a s k a :
1 0 101 0 010 0 000 0 000 0 000000000000 000000000000000000000000000000000000000000000
vr e d n o s t t i p a s h o r t : 1302
b i t o v i : { 1 , 2, 4, 8 , 10}
b it - m a s k a :
101010010000000000000000000000000000000000000000000000000000000000000000000
v r e d n o s t t i p a i n t : -2014573909
b i t o v i : { 0 , 1, 3, 5, 7, 9, 11, 18, 19, 21, 22, 23, 24, 25, 26, 31}
b it - m a s k a :
1010 1 001 0 000 0 000 0 0000000000000000000000000000000000000000000000000000000000
b i t b r o j 127 j e u k lj u č e n : {127}
b i t b r o j 255 j e u k l j u č e n : {255}
b i t b r o j 1023 j e u k lj u č e n : {1023, 1024}
* ///= -
718 Misliti na Javi
Sažetak
Ima onih koji tvrde da je biblioteka kontejnera najvažnija biblioteka objektno orijentisanog
jezika. U većini programa kontejneri se upotrebljavaju više od ijedne druge kom ponente
biblioteke. Neki jezici (Python, na prim er) imaju ugrađene čak i osnovne kontejnerske
kom ponente (liste, mape i skupove).
Kao što ste videli u poglavlju Č uvanje objekata, pom oću kontejnera se može uraditi
m nogo toga zanimljivog, a bez m nogo napora. M eđutim , doći će trenutak kada ćete m o-
rati da znate više o kontejnerim a kako biste mogli pravilno da ih upotrebljavate - kon-
kretno, m orate znati dovoljno o operacijam a transform isanja ključeva (heširanja) da
biste napisali sopstvenu m etodu h a s h C o d e () (a m orate znati i kada je to neophodno), i
m orate znati đovoljno o raznim realizacijama kontejnera da biste izabrali onu koja vam
treba. U ovom pogiavlju objasnili sm o te pojm ove i razm otrili još neke korisne detalje o
biblioteci kontejnera. U ovom tren utku trebalo bi da ste prilično dobro priprem ljeni za
korišćenje Java kontejnera u svakodnevnim program erskim zadacima.
Biblioteku kontejnera je teško projektovati (to važi za većinu dizajnerskih problem a s
bibliotekama). U C ++-u, kontejnerske klase su obuhvatale m nogo različitih klasa. ’I'o je
bilo bolje od onoga što je postojalo pre kontejnerskih klasa jezika C ++ (a to je ništa), ali se
nije moglo lepo prevesti u Javu. Druga je krajnost kontejnerska biblioteka koja se sastoji od
jedne jedine klase, container, koja istovrem eno radi kao linearna sekvenca i asocijativni
niz. Javina biblioteka kontejnera održava neku ravnotežu: ima svu funkcionalnost koju
očekujete od zrele biblioteke kontejnera, ali ju je lakše naučiti i lakše se koristi od kontej-
nerskih klasa jezika C++ i ostalih sličnih biblioteka kontejnera. Rezultat ponegde izgleda
čudno. Za razliku od nekih drugih odluka sprovedenih u starim Java bibliotekama, te čud-
novatosti nisu nastale slučajno, nego su posledica odluka pažljivo odvaganih kako bi se po-
stigao kom prom is po pitanju složenosti.
R ešenja o d a b ra n ih vežbi d a ta su u e le k tro n sk o m d o k u m e n tu Thc Thinking in Jtiva Annotatcd Sohi-
tion Guide , koji sc m o že k u p iti na lok aciji wwiv.MindVicw.cinn.
Javin ulazno-izlazni sistem
Stvaranje dobrog ulaztio-izlaznog (U /I) sistem a jed a n je od težih zadataka za projektanta je-
zika. Posledica toga je veliki broj različitih pristupa.
Klasa File
Pre nego što se pozabavim o klasama koje i čitaju i upisuju podatke u tokove, proučićem o
uslužne klase koje postoje u biblioteci, a pom ažu u radu s datotekam a.
Klasa File ima zbunjujuće ime: možete se prevariti i pomisliti da se odnosi na datoteku.
Bolje ime za klasu bilo bi FilePath. O na može da predstavlja !»;eodređenedatoteke ili im ena
skupa datoteka u direktorijum u. Ako je reč o skupu datoteka, možete da ga potražite meto-
dom list( ) koja vraća niz podataka tipa String. Vraćanje niza um esto neke prilagodljive
kontejnerske klase opravdano je zato što je broj elemenata nepromenljiv, a ako želite dru-
gačiji spisak sađržaja direktorijum a, treba da napravite drugačiji objekat klase File. U ovom
odeljku prikazuje se korišćenje ove klase i njoj pridruženog interfejsa FilenameFilter.
Listanje direktorijuma
Pretpostavićem o kako hoćete da vidite spisak sadržaja direktorijum a. Objekat klase File
možete upotrebiti na dva načina. Ako pozovete m etodu lis t ( ) bez argum enata, dobićete
kom pletnu listu datoteka u direktorijum u koji opisuje taj objekat. Ukoliko želite ograni-
čenu listu, npr. sve datoteke s nastavkom .java, upotrebite „filtar direktorijum a“, klasu
koja pokazuje kako se biraju objekti klase File koji će se prikazivati.
720 Misliti na Javi
Evo kratkog prim era. O bratite pažnju na to da se rezultati veom a lako uređuju po abe-
cednom redosledu pom oću m etode java.utii.Arrays.sort( ) i kom paratora String.
CASE_INSENSITIVE_ORDER:
p u b l i c c la s s L i s t a n j e D i r e k t o r i j u m a {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
F i l e p u t a n ja = new F i l e ( " . “ ) ;
S trin g [] lis t a ;
i f ( a r g s . l e n g t h == 0)
lis ta = p u ta n ja .lis t();
el se
lista = putanja.list(new Filt ar lm en a( ar gs ));
A r r a y s . s o r t ( l i s t a , String.CASE_INSENSITIVE_ORDER);
f o r ( S t r i n g s t a v k a D ir : l i s t a )
System.out.pri ntln(stavkaDir);
}
}
class Filtarlmena implements Fi1en am eF i1ter {
private Pattern uzorak;
public FiltarImena(String regiz) {
uzorak = Pattern.compi1e ( r e g i z ) ;
}
public boolean accept(File dir, String ime) {
return uzorak .m at ch er (i me ).m at ch es( );
}
} /* Ispis:
PrimerZaDi rektorijum.java
Li stanjeDi rektorijuma.java
ListanjeDi rektorijumaZ.java
Li stanjeDi rektorijuma3.java
* ///:-
Klasa F iita rlm en a realizuje interfejs Filenam eFilter. Korisno je videti koliko je inter-
fejs F ilenam eFilter jednostavan:
Ova klasa postoji sam o da bi m etodi li s t ( ) pružila m etodu a c c e p tf), da bi list( ) mogla
„povratno da pozove“ a c c e p t() i tako utvrdi koja im ena datoteka bi trebalo da se nađu u
listi. Stoga se ovakva struktura često naziva p o vra tn i poziv (engl. callback). Konkretnije,
ovo je prim er projektnog obrasca Strategy (Strategija), pošto li s t ( ) realizuje tek osnovnu
funkcionalnost, a vi dajete Strategy u obliku interfejsa F ilenam eFilter koji dovršava algo-
Poglavlje 18: Javin ulazno-izlazni sistem 721
ritam potreban metodi lis t() za pružanje usluge. Pošto je argument metode lis t() objekat
klase FilenameFilter, to znači da joj možete proslediti objekat bilo koje klase koja reali-
zuje interfejs FilenameFilter, i tako (čak i u vreme izvršavanja) odrediti način ponašanja
metode lis t( ). Svrha Strategije je da se obezbedi prilagodljivost koda.
Argument metode accept() m ora da bude objekat klase File koji predstavlja direkto-
rijum, kao i String koji sadrži ime datoteke. Zapamtite da metoda lis t( ) poziva metodu
accep t() za svako ime datoteke u direktorijumu, da bi videla koje od njih treba uključiti
u listu. Na to ukazuje rezultat metode accept() koji je tipa boolean.
Metoda accept() koristi metodu m atcher(ime) da bi proverila da li se regularni izraz
regiz podudara sa imenom datoteke. Rezultat metode lis t() je niz koji se postepeno pravi
pomoču metode accept().
A n o n im n e unutrašnje klase
Ovaj primer je savršen za prepravljanje pomoću anonimne unutrašnje klase (takve klase
su opisane u poglavlju Unutrašnje klase). Kao prva promena, uvodi se metoda filter( )
koja vraća referencu na FilenameFilter:
Obratite pažnju na to da argument metode filte r() mora da bude označen kao final.
To je neophodno da bi anonimna unutrašnja klasa mogla da koristi objekat koji je izvan
njene oblasti važenja.
Ovakav pristup je bolji zato što je klasa koja realizuje interfejs FilenameFilter sada čvr-
sto povezana s klasom ListanjeDirektorijuma2. Međutim, ovaj pristup se može dodatno
poboljšati kada se definiše anonimna unutrašnja klasa kao argument metode Iist( ); u
tom slučaju, program je još kraći:
//: ui/ListanjeDirektorijuma3.java
// Pravljenje anonimne unutrašnje klase "na licu mesta".
// { A r g s : "D.*\.java"}
import java.util.regex.*;
import java.io.*;
import j a v a . u t i l .*;
public class ListanjeDirektorijuma3 {
public static void main(final String[] args) {
File putanja = new File(".");
S trin g [] lis t a ;
i f ( a r g s . 1 ength == 0)
lis ta = p u ta n ja .lis t();
el se
l i s t a = p u ta n ja .lis t( n e w F ile n a m e F ilte r() {
p r i v a t e P a t t e r n uzorak = P a t t e r n . compi1e ( a r g s ) ;
p u b l i c boolean a c c e p t ( F i l e d i r , S t r i n g ime) {
r e t u r n u z o r a k . m a tc h e r ( i m e ) .m atches( ) ;
}
});
A r r a y s . s o r t ( 1 i s t a , S t r i n g . CASE I NSENSITIVE_0RDER);
fo r(S trin g s t a v k a D ir : lis ta )
S y s te m .o u t.p rin tln (s ta v k a D ir);
}
} / * Is p is :
PrimerZaDi r e k t o r i j u m . j a v a
Li s t a n je D i r e k t o r i j u m a . ja v a
Li s t a n je D i r e k t o r i j u m a 2 . j a v a
Li s t a n j e D i r e k t o r i j u m a 3 . j a v a
* ///:-
Argument metode m a in () sada je označen kao final, pošto anonimna unutrašnja kla-
sa direktno koristi args.
Ovo pokazuje kako anonimne unutrašnje klase omogućuju pravljenje speđfičnih,
unikatnih klasa za brzo rešavanje problema. Prednost ovog pristupa jeste to što je kod koji
rešava odredeni problem izolovan na jednom mestu. S druge strane, kod nije uvek lako
čitljiv, pa morate mudro da ga koristite.
Vežba 1: (3) Izmenite program ListanjeDirektorijuma.java (ili neku od njegovih varija-
nata) tako da FilenameFilter otvara i čita svaku datoteku (uslužnom metodom net.m in-
dview.util.TextFile) i prihvata je na osnovu toga sadrži li ona bilo koji od argumenata
s komandne linije koji slede iza imena datoteke.
Poglavlje 18: Javin ulazno-izlazni sistem 723
/ / : n e t / m i ndvi e w / u t i 1 / D i r e k t o r i j u m . j a v a
/ / P r o iz v o d i sekvencu F i l e o b je k a ta k o j i odgovaraju
/ / regularnom i z r a z u , b i l o u lo ka lnom d i r e k t o r i j u m u ,
/ / b i l o prolasko m kroz s t a b lo d i r e k t o r i juma.
package n e t . m i n d v i e w . u t i l ;
im p o r t j a v a . u t i 1 . r e g e x . * ;
im p o r t j a v a . i o . * ;
im p o rt j a v a . u t i l
p u b l i c f i n a l c la s s D i r e k t o r i j u m j
p u b lic s t a t ic F ile [ ]
lo k a l ( F i l e d i r , f i n a l S t r i n g r e g i z ) {
r e t u r n d i r . 1i s t F i 1es(new F i 1enameFi1t e r ( ) {
p r i v a t e P a t t e r n uzorak = P a t t e r n . c o m p i l e ( r e g i z ) ;
p u b l i c boolean a c c e p t ( F i l e d i r , S t r i n g ime) {
r e t u r n u z o ra k .m a tc h e rf
new Fi 1e ( i m e ) . getNameO) .matches ( ) ;
}
I);
}
publ i c s t a t i c Fi 1e [ ]
1oka1( S t r i n g p u t a n ja , f i n a l S t r i n g r e g i z ) { // P r e k lo p lje n o
r e t u r n lo k a l( n e w F i l e ( p u t a n j a ) , r e g i z ) ;
}
/ / Dvojka za v r a ć a n je para o b je k a ta :
p u b l i c s t a t i c c la s s I n f o S t a b la implements I t e r a b l e < F i l e > {
p u b l i c L i s t < F i l e > d a to te k e = new A r r a y L i s t < F i l e > ( ) ;
p u b l i c L i s t < F i l e > d ir m i = new A r r a y L i s t < F i l e > ( ) ;
/ / Podrazumevani i t e r a b i l n i element j e l i s t a d a to te k a :
p u b l i c I t e r a t o r < F i 1e> i t e r a t o r ( ) {
retu rn d a t o t e k e . it e r a t o r ( ) ;
724 Misliti na Javi
više informacija u svakoj rekurziji. Da bismo mogli da razlikujemo obične datoteke od di-
rektorijuma, povratna vrednost je zapravo ,,n-torka“ objekata - jedna lista koja sadrži
obične datoteke i druga koja sadrži direktorijume. Polja su namerno deklarisana kao jav-
na (public), pošto je svrha InfoStabla da prikupi objekte - da vračate samo jednu listu,
ne biste je napravili privatnom, pa ni vraćanje para objekata ne znači da ih morate napra-
viti privatnim. Obratite pažnju na to da InfoStabla realizuje Iterable<FUe> koji proizvo-
di datoteke, pa je „podrazumevana iteracija" po listi datoteka, dok iteraciju po
direktorijumima zadajete sa dirm i.
Metoda InfoStabla.toString() upotrebljava klasu ,,pretty printer" da bi se ispis lakše
pregledao. Podrazumevane metode to S trin g () za kontejnere, štampaju sve elemente jed-
nog kontejnera u istom redu. U velikim kolekcijama to postaje teško čitljivo, pa bi se mo-
glo poželeti i neko drugo formatiranje. Sledeća alatka dodaje prelaske u novi red
i uvlačenja za svaki element:
//: net/'mindview/util/PPrint.java
// Pretty-printer za kolekcije
package net.mindview.util;
import java.uti1
//: io/PrimerZaDirektorijum.java
// Primer upotrebe uslužnih metoda za Direktorijum.
import java.io.*;
import net.mindview.util.*;
import static ne t. mi nd vi ew .u ti l.Print.*;
.\TestE0F.java
A T r a n s f e r T o . java
.\ x f i1es\ThawAlien.java
AF r e e z e A l i e n . c l a s s
.\GZIPcompress.class
.\Zi pC ompress.class
* ///:-
p u b l i c c la s s N a p r a v i D i r e k t o r i j u m e {
p r i v a t e s t a t i c v o id u p o tr e b a ( ) {
S y s te m .e rr.p rin tln (
"K o riš ć e n je :N a p ra v iD ire k to riju m e p u ta n ja l . . . \ n " +
" P r a v i sve p u t a n je \ n " +
" K o r i š ć e n j e : N a p r a v i D i r e k t o r i jume -d p u t a n j a l . . . \ n " +
" B r i š e sve p u t a n je \ n " +
" K o r i š ć e n j e : N a p r a v i D i r e k t o r i j u m e - r p u t a n j a l p u t a n ja 2 \ n " +
"Pre im e n u je p u t a n j u l u p u t a n j u 2 " ;
S y s te m .e x it(l);
}
p r i v a t e s t a t i c v o id p o d a c i D a t o t e k e ( F i l e f ) {
S y s te m .o u t.p rin tln (
" A p s o lu tn a p u t a n j a : " + f . g e t A b s o l u t e P a t h ( ) +
" \ n može da se č i t a : " + f .c a n R e a d () +
" \ n može da se u p i s u j e : " + f . c a n W r i t e ( ) +
" \ n ime: " + f.g e tN a m e () +
“ \n r o d i t e l j : " + f . g e t P a r e n t ( ) +
"\n putan ja : " + f.g e tP a th ( ) +
" \ n d u ž in a : " + f . l e n g t h ( ) +
” \ n datum p o s le d n j e promene: " + f . 1a s t M o d i f i e d ( ) ) ;
Poglavlje 18: Javin ulazno-lzlazni sistem 729
Ulaz i izlaz
U U/I bibliotekama često se koristi apstraktan pojam - tok podataka (engl. stream). Tok
predstavlja neki izvor ili ponor podataka u obliku objekta koji je u stanju da daje ili prima
delove podataka. Tok skriva detalje o tome šta se dešava s podacima unutar stvarnog U/I
uređaja.
Klase Javine biblioteke za U/I podeljene su na ulazne i izlazne, što možete videti u hi-
jerarhiji Javinih klasa u dokumentaciji na Webu. Zbog nasleđivanja, sve klase izvedene iz
InputStream ili Reader imaju osnovnu metodu re a d () za čitanje jednogbajta ili niza baj-
tova. Slično, sve klase izvedene iz O utputStream ili W riter imaju osnovnu metodu write(
) za upisivanje jednog bajta ili niza bajtova. Međutim, obično nećete koristiti te metode,
jer one postoje da bi ih koristile druge klase koje obezbeđuju korisnije metode. Retko ćete
praviti objekat toka korišćenjem samo jedne klase; umesto toga, grupisaćete više objekata
da biste postigli željenu funkcionalnost. Mogućnost da se napravi više objekata radi do-
bijanja jednog toka, glavni je krivac za nerazumljivost Javine biblioteke tokova.
Korisno je kategorizovati klase prema njihovoj funkcionalnosti. U Javi 1.0 projektanti
biblioteka su se vodili idejom da se sve klase koje imaju veze sa ulazom, izvođe iz klase In-
putStream , a da sve klase povezane sa izlazom nasleđuju klasu OutputStream .
Kao i dosad u knjizi, pokušaću da prikažem klase, ali ću podrazumevati da ćete sve de-
talje, npr. kompletne spiskove metoda, tražiti u dokumentaciji na Webu.
4. cev (engl. pipe) koja radi kao prava cev: podaci se stavljaju na jednom kraju, a izla-
ze na drugom
5. niz drugih tokova koji se mogu objediniti u jedan tok
6. drugi izvori, npr. veza sa Internetom (o ovome se govori u knjizi Thinking in Enter-
prise Java, dostupnoj na adresi www.MindView.net).
Svaki od ovih tipova ulaza povezan je sa određenom potklasom klase InputStream.
Pored toga, FilterlnputStream takođe je vrsta ulaznog toka koji služi kao osnova za do-
punske klase, tj. za dodeljivanje atributa ili korisnih interfejsa ulaznim tokovima. O ovo-
me će biti reči u nastavku.
Ipak, nije tako. Iako su neki elementi prvobitne biblioteke tokova zastareii (ako ih kori-
stite, dobićete upozorenje od prevodioca), klase InputStream i O utputStream i dalje
obezbeduju važne funkcije binarno orijentisanog ulaza/izlaza, dok klase Reader i W riter
obezbeduju znakovno orijentisani Unicode ulaz/izlaz. Pored toga:
1. U lavu 1.1 dodate su nove klase u hijerarhiji ulaznih i izlaznih tokova, pa je očigled-
no da klase InputStream i O utputStream nisu zamenjene.
2. Postoje prilike kada morate da koristite klase iz binarne hijerarhije u kombinaciji
s klasama u znakovnoj hijerarhiji. Da bi se to postiglo, postoje posredničke klase:
InputStreamReader konvertuje InputStream u Reader, a O utputStream W riter
konvertuje O utputStream u Writer.
Hijerarhije klasa Reader i W riter postoje prvenstveno zbog internacionalizacije. Stara
U/I hijerarhija podržava samo osmobitne binarne tokove, odnosno ne radi dobro sa še-
snaestobitnim Unicode znakovima. Pošto se Unicode koristi za internacionalizaciju (a Ja-
vin izvorni tip char je šesnaestobitni Unicode), hijerarhije klasa Reader i W riter su
dodate da bi se podržao standard Unicode u svim U/I operacijama. Pored toga, projekto-
vane su nove biblioteke koje rade brže nego stare.
PipedlnputStream PipedReader
PipedOutputStream PipedW riter
U većini slučajeva otkrićete da su interfejsi dve različite hijerarhije slični, ako ne i istovetni.
736 Misliti na Javi
Jedno pravilo je sasvim jasno: kad god želite da koristite readL ine(), to više ne bi tre-
balo da radite pomoću klase D atalnputStream (o tome ćete dobiti upozorenje tokom
prevođenja), već pomoću klase BufferedReader. U svim ostalim slučajevima, D atalnput-
Stream se i dalje preporučuje.
Da bi se olakšao prelazak na korišćenje klase PrintW riter, njeni konstruktori prihva-
taju sve objekte tipa O utputStream , ali i Writer. Medutim, PrintW riter ne omogućuje
ništa bolje formatiranje od klase PrintStream ; njihovi interfejsi su doslovno isti.
Konstruktor klase PrintW riter takode ima opciju za automatsko pražnjenje internih
bafera, što se dešava nakon svakog poziva metode p rin tln () ako je indikator postavijen u
konstruktoru.
//: io/BaferisanaUlaznaDatoteka.java
import java.io.*;
M ctoda close( ) biće pozvana au to m atsk i to k o m izvršavanja m etode finalize( ), što bi trebalo da se
desi pri izlasku iz pro g ram a (b e z o b z ira na to da li ć e se sm e ć e sk u p iti). M e đ u tim .n a dru g im m estim a
u knjizi objašn jen o je da to ne rad i kako su pro jek tan ti Jave oćekivali (tj. prosto ne radi), pa je jedini
bezbedan p ristu p da se eksplicitno pozove m eto d a c lo s e ( ) za datoteke.
Poglavfje 18: Javin ulazno-izlazni sistem 739
Vežba 10: (2) Promenite vežbu 8 tako da kao dodatne argumente s komandne linije pri-
hvata reči koje će tražiti u datoteci. Ispišite sve redove u kojima se reči pronađu.
Vežba 11: (2) U primeru unutrasnjeklase/UpravljanjeStaklenomBastom.java, Kontro-
lerStakleneBaste sadrži fiksiran skup događaja. Promenite program tako da čita događaje i
njihova relativna vremena iz tekstualne datoteke. (nivo težine 8): Za pravljenje događaja
upotrebite projektni obrazac Factory M ethod (Proizvodna metoda) pronaći ćete ga u knjizi
Thinking in Patterns (with Java) koja se može preuzeti s lokacije www.MindView.net.
Čitanje iz memorije
U ovom odeljku se na osnovu String rezultata metode BufferedInputFile.read() pravi
objekat klase StringReader. Potom se poziva metoda re a d () koja čita jedan po jedan znak
i šalje ga na konzolu.
p u b l i c c la s s C i t a n je lz M e m o r i je {
p u b l i c s t a t i c v o iđ m ain ( S t r i n g [] args)
throws I 0 E x c e p tio n {
S trin g R e a d e r i n = new S trin g R e a d e r (
B u f f e r e d l n p u t F i 1e . r e a d ( " C i t a n j e l z M e m o r i j e . j a v a " ) ) ;
i n t c;
w h i l e ( ( c = i n . r e a d ( ) ) != -1)
S y s te m .o u t.p rin t((c h a r)c );
}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta t) * / / / : -
Obratite pažnju na to da metoda re a d () vraća sledeći bajt kao ceo broj (int) i stoga se
on mora konvertovati u tip char da bi se pravilno ispisivao.
//: i o / F o r m a t ir a n U la z I z M e m o r ije . ja v a
im p o r t j a v a . i o . * ;
p u b l i c c la s s F o r m a t ir a n llla z I z M e m o rije {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] arg s)
throws I0 E x c e p tio n {
try {
D a taln putS tre am u la z = new D a ta InputStream (
new B yte A rr a y In p u tS tr e a m (
B a f e r i sa n a U la zn aD a to te ka .re a d (
74 0 Misliti na Javi
"FormatiranUlazIzMemorije.java"),getBytes()));
while(true)
System.out.print((char)ulaz.readByte());
} catch(EOFException e) {
System.err.println("Kraj toka");
}
}
} /* (Pokrenite da biste videli rezultat) *///:~
Da bi se objekat klase String pretvorio u niz bajtova, što je potrebno za klasu ByteAr-
rayInputStream , koristi se metoda getB ytes() klase String. Nakon toga imate odgovara-
jući ulazni tok koji ćete proslediti klasi D atalnputStream .
Ako ćitate znakove iz toka tipa D atalnputStream bajt po bajt pomoću metode read-
B y te(), svaka vrednost može biti važeći rezultat, pa se povratna vrednost ne može kori-
stiti za otkrivanje kraja ulaznih podataka. Umesto toga možete da upotrebite metodu
available() da biste saznali koliko još znakova ima. Evo primera koji pokazuje kako se čita
datoteka bajt po bajt:
/ / : u i/P ro v e ra K ra ja .ja v a
/ / Provera k r a j a d a to te k e tokom njenog č i t a n j a b a j t po b a j t .
im p o r t j a v a . i o . * ;
p u b l i c c la s s Prov e ra K ra ja {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
throws IOExce ption {
Data ln putStre am u la z =
new Data InputStream(
new B u ffe r e d In p u tS tr e a m (
new F i l e I n p u t S t r e a m ( " P r o v e r a K r a j a . j a v a " ) ) ) ;
w h i1e ( u l a z . a v a i 1a b l e ( ) != 0)
S y s te m .o u t. p r i n t ( ( c h a r ) u l a z . r e a d B y t e ( ) ) ;
}
} /* ( P o k r e n it e da b i s t e v i d e l i r e z u l t a t ) * / / / : -
/ / : io /O s n o v e P is a n ja U D a to te k u .ja v a
im p o r t j a v a . i o . * ;
p u b l i c c la s s OsnovePisanjaUDatoteku {
s t a t i c S t r i n g d a to te k a = "O sno v e P is a n ja U D a to te k u .o u t";
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
throvvs IOException {
BufferedRe ader u la z = new B u fferedReader(
new S trin g R e a d e r(
B a f e r is a n a U la z n a D a t o t e k a . r e a d (" O s n o v e P is a n ja U D a to t e k u . ja v a ") ) ) ;
P r i n t W r i t e r out = new P r i n t W r i t e r (
new B u ffe r e d W r ite r( n e w F i l e W r i t e r ( d a t o t e k a ) ) ) ;
i n t brojRedova = 1;
S t r i n g s;
w h i l e ( ( s = u l a z . r e a d L i n e O ) != nul 1 )
ou t.prin tln (b ro jR e d o va+ + + " + s);
o u t.c lo s e O ;
/ / P r ik a ž i u s k la d iš t e n u d a to te k u :
S y s t e m . o u t . p r i n t l n ( B a f e r i sanaUlaznaDatoteka. r e a d ( d a t o t e k a ) ) ;
}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta t) *///:-
Dok se redovi upisuju u datoteku, uvećava se ukupan broj redova. Obratite pažnju na
to da nisam upotrebio klasu LineN um berlnputStream , pošto je sasvim beskorisna. Kao
što je ovde pokazano, veoma je lako pratiti ukupan broj redova.
Kada se ulazni tok iscrpe, metoda readLine( ) vraća null. Videćete eksplicitan poziv
metode cIose( ) za datoteku out. Ako ne pozovete metodu close( ) za izlaznu datoteku,
može se desiti da bateri ne budu ispražnjeni i da se zato izgube podaci.
/ / : i o / P r e c i caZaPi s a n je u D a to te k u . ja v a
im p o r t j a v a . i o . * ;
p u b l i c c la s s P re c ic aZaP is anjeuDatoteku {
s t a t i c S t r i n g d a to te k a = " P r e c ic a Z a P is a n je u D a t o t e k u . o u t " ;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
throws IOException {
BufferedReader u la z = new BufferedReader(
new S trin g R e a de r(
B a fe r is a n a U la z n a D a to te k a .r e a d ( "PrecicaZaPi s a n je u D a to t e k u . j a v a " ) ) ) ;
/ / Evo p r e č ic e :
P r i n t W r i t e r o u t = new P r i n t W r i t e r ( d a t o t e k a ) ;
i n t brojRedova = 1;
S t r i n g s;
w h i l e ( ( s = u l a z . r e a d L i n e O ) != n u l l )
742 Misliti na Javi
Baferisanje je i dalje tu, samo ne morate sami da ga radite. Nažalost, za ostale uobiča-
jene programerske zadatke nisam napisao prečice, pa tipičan U/I i dalje zahteva mnogo
redundantnog teksta. Međutim, u ovoj knjizi se upotrebljava uslužna metoda TextFile
koja če biti definisana malo kasnije u ovom poglavlju - ona pojednostavljuje te uobičaje-
ne programerske zadatke.
Vežba 12: (3) Promenite vežbu 8 tako da se otvara tekstualna datoteka u koju tekst može
i da se upisuje. Upišite u datoteku redove koji se nalaze u ulančanoj listi (LinkedList) i nji-
hove brojeve. (Nemojte koristiti klase LineNumber.)
Vežba 13: (3) Promenite vežbu OsnovePisanjaUDatoteku.java tako da za praćenje broja
redova upotrebljava LineNumberReader. Obratite pažnju na to koliko je lakše pratiti taj
broj programski.
Vežba 14: (2) Na osnovu programa OsnovePisanjaUDatoteku.java, napišite program
koji poredi performanse upisivanja u datoteku pri korišćenju baferisanog i nebaferisanog
ulaza/izlaza.
//: io/CuvanjelRekonstruisanjePodataka.java
import java.io.*;
Vežba 15: (4) Pročitajte HTML đokum entaciju klasa D ataO utputStream i D atalnputSt-
ream na Webu. Na osnovu programa CuvanjelPronalazenjePodataka.java, napišite
program koji skladišti i zatim rekonstruiše sve moguće tipove s kojima rade klase Data-
O utputStream i D atalnputStream . Dokažite da se svi tipovi ispravno skladište i rekon-
struišu.
//: io /KoriscenjeRandomAccessFi 1e . j a v a
im p o r t j a v a . i o . * ;
p u b l i c c la s s KoriscenjeRa ndomAccessFi1e {
s t a t i c S t r i n g d a to te k a = " r t e s t . d a t " ;
s t a t i c v o id d i s p l a y ( ) throws IO Exce ption {
RandomAccessFi1e r f = new RandomAccessFile (datoteka, "r");
f o r ( i n t i = 0; i < 7; i+ + )
S y s t e m . o u t . p r i n t l n(
"Vre dn o st " + i + " : " + r f . re a d D o u b le () ) ;
S y s t e m . o u t . p r i n t l n ( r f . rea d U T F()) ;
rf,c lo s e ();
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
throws I 0 E x c e p t io n {
RandomAccessFile r f = new RandomAccessFi1e ( d a t o t e k a , " r w " ) ;
f o r ( i n t i = 0 ; i < 7 ; i+ + )
r f .w r it e D o u b le ( i*1.4 1 4 );
rf.w rite U T F ("K ra j d a to te k e ");
r f ,c lo s e ();
d is p la y ();
r f = new R a ndom A ccessFile (datoteka, " r w " ) ;
r f . se e k ( 5 * 8 ) ;
rf.w rite D o u b le (4 7 .0 0 0 1 ) ;
rf.c lo s e ();
di s p la y ( ) ;
}
} / * Is p is :
Vrednost 0: 0 .0
Vrednost 1: 1.414
Vrednost 2: 2.828
Vrednost 3: 4.242
Poglavlje 18: Javin ulazno-izlazni sistem 745
Vrednost 4: 5.656
Vrednost 5: 7.069999999999999
Vrednost 6: 8.484
Kra j d a to te k e
Vrednost 0: 0.0
Vre d n ost 1: 1.414
Vrednost 2: 2.828
Vrednost 3 : 4.242
V re d n ost 4: 5.656
V re d n ost 5: 47.0001
V rednost 6: 8.484
Kra j d a to te k e
* ///:-
Metoda d isplay() otvara datoteku i prikazuje njenih sedam elemenata kao vrednosti
tipa double. U metodi m ain( ), datoteka se pravi, otvara i menja. Pošto svaki broj tipa
double (uvek) zauzima osam bajtova, da biste metodom seek() pronašli šestu vrednost
po redu, morate izračunati 5*8 i dobićete poziciju njenog početnog bajta u datoteci.
Kao što je ranije pomenuto, klasa RandomAccessFile je potpuno izdvojena od ostatka
hijerahije U/I tokova, osim što realizuje interfejse D atalnput i DataOutput. Ona ne po-
država dekorisanje, pa je zbog toga ne možete kombinovati ni s jednim aspektom potkla-
sa InputStream i O utputStream . Morate pretpostaviti da je klasa RandomAccessFile
ispravno baferisana, pošto tu funkciju ne možete da joi dodate.
ledina opcija koja vam stoji na raspolaganju jeste drugi argument konstruktora; datote-
ku s nasumičnim pristupom možete da otvorite radi čitanja (r) ili čitanja i upisivanja (rw).
Imajte u vidu da umesto klase RandomAccessFile možete koristiti nio datoteke pres-
likane u memoriju.
Vežba 16: (2) Pročitajte HTM LdokumentacijuklaseRandomAccessFilenaW ebu. Na os-
novu programa KoriscenjeRandomAccessFile.java, napišite program koji smešta i zatim
rekonstruiše sve moguće tipove s kojima radi klasa RandomAccessFile. Dokažite da se svi
tipovi ispravno skladište i rekonstruišu.
Cevovodi
Klase PipedlnputStream , PipedO utputStream , PipedReader i PipedW riter pomenute
su u ovom poglavlju samo ukratko. Nemojte na osnovu toga zaključiti da nisu korisne.
Njihova korisnost nije očigledna dok ne počnete da isprobavate višenitni rad, jer se cevo-
vodi upotrebljavaju za komunikaciju između niti. O ovome će, uz primer, biti reči u po-
glavlju Paralelno izvršavanje.
još gore, zbog dekoratora je prilično teško zapamtiti kako se otvaraju datoteke. Dakle, bilo
bi dobro dodati biblioteci pomoćne klase koje će te elementarne operacije raditi umesto
nas. Java SE5 je klasi PrintW riter dodala prigodni konstruktor koji olakšava otvaranje
tekstualne datoteke za upisivanje. Međutim, ostalo je još mnogo drugih poslova koji se
često ponavljaju, pa bi valjalo napisati kod za njih jednom zauvek.
Sledi klasa TextFile koju smo već u prethodnim primerima iz ove knjige koristili da
pojednostavimo upisivanje i čitanje datoteka. Ona sadrži statične metode za čitanje i upi-
sivanje tekstualnih datoteka u obliku jednog znakovnog niza, a možete napraviti i Text-
FUe objekat koji redove te datoteke čuva u listi ArrayList (pa tokom rada sa sadržajem
datoteke imate na raspolaganju svu njenu funkcionalnost):
/ / : n e t / m i n d v i e w / u t i l / T e x t F i 1e . ja v a
/ / S t a t i č n e f u n k c i j e za u p i s i v a n j e i č i t a n j e t e k s t u a l n i h d a t o t e k a u o b l i k u
/ / jednog znakovnog n i z a , i t r e t i r a n j e d a t o t e k e kao je d n e A r r a y L i s t l i s t e .
/ / x je d n e A r r a y L i s t l i s t e .
package n e t . m i n d v i e w . u t i l ;
im p o rt j a v a . i o . * ;
im p ort j a v a . u t i l . * ;
o u t.c1o se ();
}
} c a tc h (IO E x c e p tio n e) {
throw new R u n t im e E x c e p t io n ( e ) ;
}
}
/ / U č i t a j d a t o t e k u , i z d e l j e n u na r e č i pomoču b i l o koje g reg u la rn o g i z r a z a :
p u b l i c T e x t F i l e ( S t r i n g imeDa toteke , S t r i n g podela) {
s uper(A rrays.asLi s t( r e a d ( im e D a t o t e k e ) . s p lit ( p o d e la ) ) ) ;
/ / Zbog podele metodom s p lit ( ) p o m o ć u r e g u la rn o g i z r a z a , na
/ / p r v o j p o z i c i j i č e sto o s t a j e prazan znakovni n i z :
i f ( g e t (0) . e p u a l s C " 1) ) remove(O);
}
/ / Obično se č i t a red po re d :
p u b l i c T e x t F i l e ( S t r i n g imeDatoteke) {
t h is ( i m e D a t o t e k e , " \ n " ) ;
}
p u b l i c v o id w r i t e ( S t r i n g im eDatoteke) {
try {
P r i n t W r i t e r o u t = new P r i n t W r i t e r (
new F i 1e ( i m e D a t o t e k e ) . g e t A b s o lu t e F i1e ( ) ) ;
try {
f o r ( S t r i n g stavka : t h i s )
o u t.p rin tln (s ta v k a );
) fin a lly {
o u t.c lo s e ();
}
} c a t c h (IO E xce p ti on e) {
th ro w new R u n tim e E x c e p tio n ( e ) ;
}
}
/ / Jednostavna p ro v e r a :
p u b l i c s t a t i c v o id m ain ( S t r i n g [ ] args) {
S t r i n g d a to te k a = r e a d ( " T e x t F i 1e . j a v a " ) ;
w r i t e ( " t e s t . t x t " , da tote ka );
T e x t F i l e t e k s t = new T e x t F i l e C t e s t . t x t " ) ;
te k s t.w rite C te s t2 .tx t");
/ / Podela na je d i n s t v e n e r e č i u uređenoj l i s t i :
T re e S e t< S trin g > r e c c i = new T re e S e t< S tr in g > (
new T e x t F i 1e ( " T e x t F i 1e . j a v a " , " \ \ W + " ) ) ;
/ / P r i k a ž i r e č i s v e l i k i m početnim slovom:
S y s t e m . o u t . p r i n t l n ( r e c c i . headSet( " a " ) ) ;
}
} / * Is p is :
[0 , A r r a y L i s t , A r r a y s , Break, Buffe red Re a d e r, B u f f e r e d W r i t e r , Clean, F i l e ,
F ile R e a d e r, F i l e W r i t e r , IO Exce p tio n , Jednostavn a, Obično, O u tp ut, P r i k a ž i ,
P r i n t W r i t e r , Read, R e g u la r, Ru ntim eExce ptio n, S t a t i c , S t r i n g , S t r i n g B u i l d e r ,
System, T e x t F i l e , T o o ls , Tre eSet, W, W r it e ]
* ///:-
748 Misliti na Javi
Metoda re a d () objektu tipa StringBuilder dodaje svaki red i zatim znak za prelazak u
novi red, jer se oni uklanjaju tokom čitanja. Potom ona kao svoj rezultat vraća znakovni
niz koji sadrži celu datoteku. Metoda w rite () otvara tekstualni znakovni niz i upisuje ga
u datoteku.
Vodite računa o tome da svaki program koji otvara neku datoteku čuva njen poziv me-
tode clo se() unutar bloka finaUy, da bi datoteka bila zajemčeno zatvorena.
Konstruktor pretvara datoteku u znakovni niz pomoću metode re a d (), zatim poziva
S tring.split() da bi taj rezultat podelio na redove (uz znakove za prelazak u novi red kao
graničnike; ako ovu klasu često upotrebljavate, mogli biste da prepravite taj konstruktor
tako da bude delotvorniji). Nažalost, ne postoji odgovarajuća metoda ,,spajanja“ redova u
znakovni niz, pa nestatičnom m etodom w rite () moramo da ispisujemo red po red ručno.
Pošto je ova klasa napisana zato da bi do krajnosti pojednostavila postupak čitanja i
pisanja datoteke, svi IOException izuzeci pretvoreni su u RuntimeException izuzetke,
kako korisnik ne bi morao da upotrebljava try-catch blokove. Međutim, možda ćete mo-
rati da napišete drugu verziju koja IOException izuzetke prosleđuje pozivaocu.
U metodi m a in () obavlja se elementarna provera ispravnosti rada spomenutih metoda.
Nije trebalo mnogo koda da bi se napisala ova uslužna klasa, a ona će nam uštedeti
mnogo vremena i olakšati život, u šta ćete se uveriti u nekima od narednih primera u
ovom poglavlju.
Problem s čitanjem tekstualnih datoteka može se rešiti i na drugi način, klasom ja-
va.util.Scanner uvedenom u Javu SE5. Medutim, ona služi samo za čitanje datoteka, ne i
za pisanje u njih. Ta alatka (koja nije stavljena u paket java.io) prvenstveno je namenjena
za pravljenje analizatora programskih jezika (engl. scanners) ili „malih jezika“.
Vežba 17: (4) Pomoću klase TextFile i mape M ap<Character,Integer> napišite program
koji prebrojava znakove u datoteci. (Dakle, ako u nekoj datoteci ima 12 primeraka slova
a, 12 treba da sadrži objekat tipa Integer pridružen objektu tipa Character koji sađrži a u
mapi).
Vežba 18: (1) Izmenite program TextFile.java tako da izuzetke IOExceptions prosleduje
pozivaocu.
p u b l i c c la s s Bin a rn a D a to te ka {
p u b l i c s t a t i c b y t e [ ] r e a d ( F i l e bDatoteka) throws IO Exceptio n{
B u f f e r e d ln p u t S t r e a m bD = new B u ffe r e d In p u tS tr e a m (
new F i 1e ln p u tS t r e a m ( b D a t o t e k a ) ) ;
try {
Poglavlje 18: Javin ulazno-izlazni sistem 749
b y t e [ ] data = new b y t e [ b D . a v a i l a b l e ( ) ] ;
b D .re a d (d ata );
r e t u r n d a ta ;
} fin a lly {
b D .c lo s e ();
}
}
p u b lic s t a t ic b yte [ ]
r e a d ( S t r i n g bDa toteka) throws IOExce ptio n {
r e t u r n read(new F ile ( b D a t o t e k a ) . g e t A b s o l u t e F i l e O ) ;
}
} III--
Jedna preldopljena metoda prima argument tipa File; druga prima argument tipa
String, što je ime te datoteke. Obe vraćaju rezultujući niz tipa byte.
Metodom available() pravi se niz odgovarajuće veličine, a baš ova verzija preklopljene
metode re a d () popunjava taj niz.
Vežba 19: (2) Pomoću klase BinarnaDatoteka i mape Map<Byte,Integer> napišite pro-
gram koji prebrojava različite bajtove u datoteci.
Vežba 20: (4) Pomoću metode Direktorijum .prolazak( ) i klase BinarnaDatoteka, do-
kažite da sve .class datoteke u stablu direktorijuma počinju heksadecimalnim kodovima
znakova CAFEBABF.
/ / : u i/E h o .ja v a
/ / Kako se č i t a sta n d a rd n i u la z n i t o k .
/ / {RunByHand}
im p o r t j a v a . i o . * ;
p u b l i c c la s s Eho {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
throws IO Exce ptio n {
Buffe redReader s t d i n = new B u fferedReader{
new InputStre am Reader(S ystem.i n ) ) ;
S t r i n g s;
w h i l e ( ( s = s t d i n . r e a d L i n e ( ) ) != n u l l && s . 1e n g th ( ) != 0)
S y s te m .o u t.p rin tln (s );
/ / Program završa va prazan red i l i C t r l - Z
}
> III--
Razlog za specifikaciju izuzetka u metodi m a in () jeste to što metoda readL ine() može
da generiše izuzetak IOException. Vodite računa o tome da System.in obično treba da
bude baferisan, kao i većina tokova.
Vežba 21: (1) Napišite program koji prima standardan ulaz, pretvara sva primljena
slova u velika, a zatim rezultate šalje na standardan izlaz. Preusmerite u ovaj program
sadržaj neke datoteke. (Postupak preusmeravanja se menja u zavisnosti od operativnog
sistema.)
p u b l i c c la s s OmotavanjeSystemOut {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) {
P r i n t W r i t e r o u t = new P r i n t W r i t e r ( S y s t e m . o u t , t r u e ) ;
o u t.p rin tln ("Z d ra v o , s v e te ");
}
} / * Is p is :
Zdravo , sv ete
* ///-.-
Važno je da se koristi verzija konstruktora 7,a klasu PrintW riter koja ima dva argu-
menta i da drugi argument bude true, da bi se omogućilo automatsko pražnjenje izlaznih
bafera, inače se izlaz neće prikazivati red po red.
Poglavlje 18: Javin ulazno-izlazni sistem 751
/ / : u i/ P r e u s m e r a v a n je . ja v a
/ / P r ik a z u je standardno U / I preusmeravanje.
im p o r t j a v a . i o . * ;
c l a s s Preusmeravanje {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
thro ws IO Exception {
P r in t S t r e a m ko nzola = Syste m .o ut;
B u ff e r e d ln p u t S t r e a m i n = new B u ffe r e d In p u tS tr e a m (
new F i 1e I n p u tS tre a m (" P re u s m e ra v a n je .j a v a " ) ) ;
P r in t S t r e a m o u t = new P r in tS tr e a m (
new B u fferedO utputStre am (
new F i 1e O u t p u t S t r e a m ( " t e s t . o u t " ) ) ) ;
S y s te m .s e tln (in );
S y s te m .s e tO u t( o u t ) ;
S y s te m .s e tE rr(o u t);
Bu fferedReader b r = new BufferedReader(
new I n p u tS tre a m R e a d e r(S y s te m .in )) ;
S t r i n g s;
w h i l e ( ( s = b r . r e a d L i n e ( ) ) != n u l l )
S y s te m .o u t.p rin tln (s );
o u t . c l o s e ( ) ; / / Ne z a b o r a v it e ovo!
S y s t e m . s e tO u t ( k o n z o la ) ;
}
1 ///:-
Ovaj program povezuje datoteku na standardni ulaz i preusmerava standardni izlaz i
standardni tok za greške u drugu datoteku. Obratite pažnju na to da se na početku pro-
grama skladišti referenca prvobitnog System.out objekta i da se na kraju standarđni izlaz
vraća tom objektu.
U/I preusmeravanje radi s tokovima bajtova, a ne s tokovima znakova, pa se koriste
klase InputStream i O utputStream umesto klasa Reader i VVriter.
Upravljanje procesima
Često ćete biti u prilici da iz Jave pokrećete druge programe operativnog sistema i da
upravljate ulazom i izlazom tih programa. Javina biblioteka ima klase za takve operacije.
Uobićajen zadatak je pokretanje programa i slanje rezultujućeg izlaza na konzolu.
U ovom odeljku napisaćemo uslužnu klasu koja će pojednostaviti taj zadatak.
U ovoj uslužnoj klasi mogu nastati dve vrste grešaka: uobičajene greške koje prouzro-
kuju izuzetke - za njih ćemo samo ponovo generisati RuntimeException - i greške pri
izvršavanju samog procesa. Te greške ćemo prijavljivati posebnim izuzetkom:
/ / : n e t / m i n a v i e w / u t i 1/ O S I z v r s e n j e . j a v a
/ / Iz v r š a v a komandu o p e ra t iv n o g s is te m a i i z l a z š a l j e na k onzolu .
package n e t . m i n d v i e w . u t i l ;
im p o rt j a v a . i o . * ;
p u b l i c c la s s O SIzvrsenje {
p u b l i c s t a t i c v o id komanda(String komanda) {
boolean greska = f a l s e ;
try {
Process proces =
new Pr'ocessBui 1der(komanda. spl i t (" " ) ) . p o c e t a k ( ) ;
BufferedReader r e z u l t a t i = new Buffe red Re a d e r(
new I n p u t S t r e a m R e a d e r ( p r o c e s , g e t ln p u t s t r e a m ( ) ) ) ;
S t r i n g s;
w h i l e ( ( s = r e z u l t a t i . r e a d L i n e ( ) ) != n u l l )
S y s te m .o u t.p rin tln (s );
BufferedReader greske = new Buffe red Re a d e r(
new I n p u t S t r e a m R e a d e r ( p r o c e s . g e t E r r o r S t r e a m ( ) ) ) ;
/ / Ako ima problema, p r i j a v i g re ške i pozivajuće m
/ / procesu v r a t i v re d n o s t r a z l i č i t u od n u le :
w h i l e ( ( s = g r e s k e . r e a d L i n e O ) != nul 1) {
S y s te m .g re s k a .p r i n t l n ( s ) ;
greska = t r u e ;
}
} c a t c h (E x c e p t io n e) {
/ / Zbog Windowsa 2000, k o j i g e n e r iš e
/ / iz u z e t a k za podrazumevani s a d rž a j komandne l i n i j e :
Poglavlje 18: Javin ulazno-izlazni sistem 753
/ / ; u i/ P r i m e r O S I z v r s e n j a . ja v a
/ / P r ik a z u je standardno preusmeravanje U / I .
im p o r t n e t . m i n d v i e w . u t i l . * ;
/ ' / : u i/ D a j K a n a l . ja v a
/ / P r a v l j e n j e kanala od tokova
im p o rt j a v a . n i o . * ;
im p o rt j a v a . n i o . c h a n n e l s . * ;
im p o r t j a v a . i o . * ;
p u b l i c c la s s DajKanal {
p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n {
/ / I s p i š i datotekU:
FileChannel f c =
new F i 1e O u tp u tS tre a m ("p o d a c i. t x t " ) .getChannel ( ) ;
f c . w r i t e ( B y t e B u f f e r . w r a p ( “ Nekakav t e k s t " . g e t B y t e s ( ) ) ) ;
fc .c lo s e ();
/ / Dodaj na k r a j d a to te k e :
fc =
new RandomAccessFi1e ( " p o d a c i . t x t " , " r w " ) .getChannel ( ) ;
f c . p o s i t i o n ( f c . s i z e ( ) ) ; / / Pređi na k r a j
f c . w r i te (B yte B u ffer.w ra p("Jo š m alo",g e tB yte s( ) ) ) ;
fc .c lo s e ();
/ / Č i t a j d a to te k u :
f c = new F i l e I n p u t S t r e a m ( " p o d a c i . t x t " ) . g e t C h a n n e l ( ) ;
B y t e B u f f e r b a f = B y t e B u f f e r . a l 1ocate(VELBAF);
fc .re a d (b a f);
baf . f l i p ( ) ;
w h ile (b a f.h a s R e m a in i n g ( ) )
Poglavlje 18: Javin ulazno-izlazni sistem 755
Metoda getC hannel() za svaku prikazanu klasu tokova pravi objekat tipa FileChan-
nel. Kanal je prilično jednostavan: može m u se predati objekat tipa ByteBuffer radi či-
tanja ili pisanja, i delove datoteke možete zaključati, odnosno ostvariti isključivo pravo
pristupanja (to če biti opisano u nastavku).
Jedan od načina smeštanja bajtova u ByteBuffer jeste da ih neposredno umetnete u
njega nekom od put metoda, i tako upišete jedan ili više bajtova ili vrednosti prostih tipo-
va. Međutim, iz programa vidite da metodom w ra p () možete postojeći niz tipa byte da
omotate objektom tipa ByteBuffer. Kada to uradite, pripadajući niz se ne kopira, nego
koristi kao skladište za generisani ByteBuffer. Kažemo da je taj ByteBuffer poduprt ni-
zom.
Datoteku podaci.txt ponovo otvara klasa RandomAccessFile. Vodite računa o tome
da FileChannel možete premeštati po datoteci; ovde sam ga premestio na kraj, tako da se
novi upisi dodaju na kraj postojećih (engl. append).
Kada primenjujete pristup samo za čitanje, memorijski prostor za ByteBuffer morate
eksplicitno dodeliti statičnom metodom allo cate(). Svrha nio klasa je brzo premeštanje
velikih količina podataka, pa nije nevažno koju veličinu bafera ByteBuffer zadate - za-
pravo, ovde će vam 1 K verovatno mnogo manje značiti nego inače (moraćete da ekspe-
rimentišete s tekućom aplikacijom da biste odredili najbolju veličinu bafera).
U potrazi za još većom brzinom, mogli biste metodom allocateD irect() umesto me-
todom allocate() napraviti direktan bafer koji je još tešnje spregnut sa operativnim siste-
mom. Međutim, režijski troškovi takve dodele su veći, a njena realizacija se menja u
zavisnosti od operativnog sistema, pa ćete opet morati da eksperimentišete s tekućom ap-
likacijom da biste odredili donose li vam direktni baferi ikakvo ubrzanje.
Nakon što pozovete read( ) da biste kanalu FileChannel saopštili da upiše bajtove u
ByteBuffer, morate pozvati metodu flip( ) za taj bafer kako biste mu saopštili da se pri-
premi za vađenje bajtova iz njega. (To jeste malo nezgrapno, ali ne zaboravite da se radi o
veoma niskom nivou i da se tako postiže maksimalna brzina). A kada bismo taj bafer
upotrebili za još neku operaciju re a d (), pre svakog učitavanja sađržaja u bafer morali bi-
smo da pozovemo metodu clear( ) kao pripremu. To vidite u ovom jednostavnom pro-
gramu za kopiranje datoteka:
/ / : u i/ K o p ir a n je K a n a lo m . ja v a
/ / K o p ir a n je d a to te k e pomoću kanala i b a f e r a
/ / { A rg s: K o p ir a n je K a n alom .ja v a t e s t . t x t }
im p o r t j a v a . n i o . * ;
im p o r t j a v a . n i o . c h a n n e l s . * ;
im p o rt j a v a . i o . * ;
p u b l i c c la s s KopiranjeKanalom {
p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n {
756 Misliti na Javi
if(a rg s .le n g th != 2) {
S y s te m .o u t.p rin tln ("a rg u m e n ti: d a tiz v o r d a to d re d is te ");
S y s te m .e x it(l);
}
FileCh annel
u la z = new F i l e I n p u t S t r e a m ( a r g s [ 0 ] ) . g e t C h a n n e l ( ) ,
i z l a z = new F i l e O u t p u t S t r e a m ( a r g s [ l ] ) . g e t C h a n n e l ( ) ;
B y t e B u f f e r b a f e r = B y t e B u f f e r . a l lo c a t e ( V E L B A F ) ;
w h i l e ( u l a z . r e a d ( b a f e r ) != -1) {
b a f e r . f l i p ( ) ; / / P rip re m i za p is a n je
iz la z .w r ite (b a fe r) ;
b a f e r . c l e a r ( ) ; / / Prip rem i za č i t a n j e
}
}
} ///= -
Jedan FileChannel otvorili smo za čitanje, a drugi za pisanje. Objektu tipa ByteBuffer
dodeljuje se memorija, i kada metoda FileChannel.read( ) vrati -1 (nema sumnje, ta
vrednost je zaostatak iz Unixa i C-a), to znači da smo đošli do kraja ulaza. Nakon svakog
poziva metode re a d () koja u bafer smešta podatke, flip () priprema bafer da bi metoda
w rite () mogla da vadi podatke iz njega. Podaci ostaju u baferu i nakon čitanja metodom
w rite (), i tek metoda c le a r() resetuje sve interne pokazivače tako da bafer postaje spre-
man da od metode re a d () primi nove podatke.
Prethodni program nije idealan za obavljanje te vrste operacija. Postoje posebne me-
tode transferT o() i tran sferF ro m () za neposredno povezivanje jednog kanala s drugim:
/ / : u i / T r a n s f e r T o . ja v a
/ / P o ve z iv a n je kanala metodom t r a n s f e r T o ( )
/ / { A r g s : T r a n s f e r T o . ja v a T r a n s f e r T o . t x t }
im p o r t j a v a . n i o . c h a n n e l s . * ;
im p o r t j a v a . i o . * ;
p u b l i c c la s s T ra n s fe rT o {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) throws Exce p tio n {
i f ( a r g s . l e n g t h != 2) {
S y s te m .o u t.p rin tln ("a rg u m e n ti: d a tiz v o r d a to d re d is te ");
S y s te m .e x it(l);
}
F i 1eChannel
u la z = new F ile l n p u t S t r e a m ( a r g s ) . g e t C h a n n e l ( ) ,
i z l a z = new F i 1e O u t p u t S tr e a m ( a rg s ) .g e tC h a n n e l( ) ;
u la z .tra n s fe rT o (0 , u l a z . s i z e ( ) , i z l a z ) ;
//III:
// i z l a z . t r a n s f e r F r o m ( u l a z , 0, u l a z . s i z e O ) ;
}
} ///:-
Ovako nešto retko će vam trebati, ali dobro je to znati.
Poglavlje 18: Javin ulazno-izlazni sistem 757
Konverzija podataka
Ako se osvrnete na program DajKanal.java, videćete da prilikom ispisivanja datoteke
podatke vadimo bajt po bajt, pa svaki byte zasebno pretvaramo u char. To je primitivno
- pročitajte dokumentaciju klase java.nio.CharBuffer i videćete da se u njoj za metodu
to S trin g () kaže: „Vraća znakovni niz koji sadrži znakove bafera“. Pošto se ByteBuffer na-
kon obrade metodom asC harB uffer() može smatrati baferom CharBuffer, zašto ne bis-
mo upotrebili njega? Kao što vidite iz prvog reda ispisa rezultata sledećeg programa, to
ipak ne radi kako smo očekivali:
/ / : u i/B a fe rU T e kst.ja va
/ / K o n v e r z ija t e k s t a u B y t e B u f f e r i i z njega
im p o r t j a v a . n i o . * ;
im p o r t j a v a . n i o . c h a n n e l s . * ;
im p o r t j a v a . n i o . c h a r s e t . * ;
im p o r t j a v a . i o . * ;
p u b l i c c la s s BaferLITekst {
p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws Exce p tio n (
File Channel f c =
new Fi 1e 0 u tp u t S t r e a m ( " p o d a c i2 . t x t " ) . g e t C h a n n e l ( ) ;
fc .w rite (B y te B u ffe r.w ra p ("N e k i te k s t".g e tB y te s ( ) ) ) ;
fc .c lo s e O ;
f c = new Fi l e I n p u t S t r e a m C p o d a c i 2 . t x t " ) .getChannel ( ) ;
B y t e B u f f e r b a f = B y t e B u f f e r . a l lo c a t e ( V E L B A F ) ;
fc .re a d (b a f);
b a f. f l ip ( ) ;
/ / Ne r a d i :
S ystem .out. p ri n tln ( b a f.a s C h a rB u ffe r ( ) ) ;
/ / D e k o d ira j po podrazumevanoj šemi k o d ir a n ja ovog sis tem a:
b a f.re w in d ();
S t r i n g k o d i r a n j e = S y s t e m . g e t P r o p e r t y ( " f i 1e . e n c o d i n g " ) ;
S y s t e m . o u t . p r i n t l n ( " D e k o d i r a n o po šemi " + k o d i r a n j e + “ : "
+ C h a r s e t . forName(kodi r a n j e ) . d e c o d e ( b a f ) ) ;
/ / I I i da kodiramo po šemi k o ja se može o d š ta m p a ti:
f c = new F i 1e 0 u tp u tS tr e a m ( " p o d a c i2 . t x t " ) . g e t C h a n n e l ( ) ;
f c . w r i t e ( B y t e B u f f e r . w r a p ( " N e k i t e k s t " . g e tB y te s ("U T F -1 6 B E ")) ) ;
fc .c lo s e O ;
/ / Sada ponovo p o k u š a jte da č i t a t e :
f c = new Fi 1e I n p u t S t r e a m C p o d a c i 2 . t x t " ) .getChannel ( ) ;
b a f.c le a r();
fc .re a d (b a f);
b a f. f l ip ( ) ;
S y s te m .o u t.p rin tln (b a f.a s C h a rB u ffe r());
/ / Pisaćemo kroz C h a rB u ffe r :
f c = new F ile 0 u t p u t S t r e a m C p o d a c i 2 . t x t " ) .getChannel ( ) ;
b a f = B y t e B u f f e r . a l l o c a t e ( 2 4 ) ; / / Više nego š t o j e potrebno
b a f . asCharB uff e r ( ) . p u t ( " Neki t e k s t " ) ;
f c . w r ite (b a f);
758 Misliti na Javi
fc .c lo s e ();
/ / č ita n je i p rik a z iv a n je :
f c = new F i l e I n p u t S t r e a m ( " p o d a c i 2 . t x t “ ) . g e t C h a n n e l( ) ;
b a f.c le a rO ;
fc .re a d (b a f);
b a f.fl ip ( ) ;
S y s te m .o u t.p rin tln (b a f .a s C h a rB u ffe rO );
)
} /* Is p is :
????
Dekodirano po šemi Cpl252: Neki t e k s t
Neki t e k s t
Neki t e k s t
* ///:-
Bafer sadrži sirove bajtove, i da bismo njih pretvorili u znakove, moramo ih kodirati
prilikom stavljanja unutra (da bi nešto značili kada ih izvadimo iz bafera) ili dekodirati
prilikom vađenja iz bafera. To se može postići klasom java.nio.charset.Charset koja
obuhvata alatke za kodiranje po raznim šemama:
/ / : u i/D o s tu pn e S em e K o d ir a nja.ja v a
/ / P r ik a z u je šeme k o d i r a n j a i n ji h o v e a l i j a s e
im p o r t j a v a . n i o . c h a r s e t . * ;
im p o r t j a v a . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s DostupneSemeKodir a n ja {
p u b l i c s t a t i c v o id m a in ( S t r i n g [] a r g s ) {
S o r te dM a p < S tring ,C h a rs e t> semeKodiranja =
Ch arset . a v a i l a b l e C h a r s e t s O ;
I t e r a t o r < S t r i n g > i t = semeKodir a n j a . ke y S e t( ) . i t e r a t o r ( ) ;
w h i1e ( i t ,h a s N e x t( ) ) {
S t r i n g imeSemeKodiranja = i t . n e x t ( ) ;
p r i ntnb(imeSemeKodi r a n j a ) ;
Ite ra to r a lija s i =
semeKodi r a n j a . g e t (imeSemeKodi r a n j a ) . a l i a s e s ( ) . i t e r a t o r ( ) ;
i f ( a l i j a s i .h a s N e x t( ) )
p rin tn b (": " );
w h i1e ( a l i j a s i . hasN ext( ) ) {
p rin tn b (a lija s i,n e x t());
i f ( a l i j a s i .h a s N e x t( ) )
p rin tn b C ', ");
}
pri nt ();
}
}
} / * Is p is :
B ig 5 : csBig5
Big5-HKSCS: b i g 5 - h k s c s , b ig 5 h k , b i g 5 - h k s c s : u n ic o d e 3 .0 , big 5 h ks cs , Big5_HKSCS
Poglavfje 18: Javin ulazno-izlazni sistem 759
* ///:-
Vratimo se na BaferUTekst.java. Ako bafer ,,premotate“ metodom rew in d () (da biste
se vratili na početak podataka) i zatim za dekodiranje podataka metodom decode( )
upotrebite podrazumevanu šemu kodiranja te platforme, dobićete CharBuffer koji se
lepo ispisuje na konzoli. Za pronalaženje podrazumevane šeme kodiranja pozovite me-
todu System.getProperty("file.encoding") koja proizvodi znakovni niz s imenom te
šeme kodiranja. Prosledite to metodi Charset.forN am e() i dobićete objekat tipa Charset
kojim se taj znakovni niz može dekodirati.
Druga mogućnost je da (metodom encode()) kodirate po šemi kodiranja koja će dati
rezultat koji se može štampati nakon čitanja datoteke, kao što vidite u trećem delu pro-
grama BaferUTekst.java. Tu je za pisanje teksta u datoteku upotrebljena šema kodirania
UTF-16BE, pa nakon čitanja samo morate da je konvertujete u CharBuffer koji će dati
očekivani tekst.
Najzad, vidite šta se dešava ako pišete u ByteBuffer preko CharBuffer bafera (o tome
će još biti reči). Baferu ByteBuffer dodelili smo 24 bajta. Pošto svaki znak (char) zahteva
dva bajta, to je dovoljno za 12 znakova, ali „Neki tekst“ ih ima samo 10. Preostali bajtovi
s nulama ipak se vide u prikazu CharBuffer bafera koji proizvodi njegova metoda
to S trin g (), kao što vidite iz ispisa rezultata.
Vežba 23: (6) Napravite i ispitajte uslužnu metodu koja štampa sadržaj CharBufferbafera
sve do mesta od kojega se njegovi znaci više ne mogu odštampati.
/ / : u i / D a jP o d a t k e . ja v a
/ / Tumačenje b a jt o v a i z B y te B u ffe ra na r a z l i č i t e načine
im p o r t j a v a . n i o . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s DajPodatke {
p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) {
B y t e B u f f e r bb = B y t e B u f f e r . a l 1ocate(VELBAF);
// B y t e B u f f e r se automatski puni nulama čim mu se d o d e li memorija:
i n t i = 0;
w h i 1e ( i + + < b b . 1 i m i t ())
760 Misliti na Javi
i f ( b b . g e t ( ) != 0)
p r in t( " n ije n u la ");
p rin t("i = " + i) ;
b b .re w in d ();
/ / U p iš i i p r o č i t a j n i z t i p a ch a r:
b b .a s C h a rB u ffe r().p u t("Z d ra v o !" ) ;
c h a r c;
w h i ! e ( ( c = b b . g e t C h a r ( ) ) != 0)
p rin tn b (c + " " );
p rin t();
b b .re w in d ();
/ / U p iš i i p r o č i t a j b r o j t i p a s h o r t :
b b .asS h o rtB u ffe r(),p ut((sho rt)4 7 1 1 4 2 );
p rin t(b b .g e tS h o rt());
b b .re w in d ();
/ / U p iš i i p r o č i t a j b r o j t i p a i n t :
b b .asIn tB u ffe r().p u t(9 9 4 7 1 1 4 2 );
p rin t(b b .g e tln t());
b b .re w in d ();
/ / U p iš i i p r o č i t a j b r o j t i p a lo n g :
bb.asLo n g B u ffe r().pu t(9 9 47 1 142);
p rin t(b b .g e tL o n g ());
b b .re w in d ();
/ / U p iš i i p r o č i t a j b r o j t i p a f l o a t :
b b .a s F lo a tB u ffe r().p u t(9 9 4 7 1 1 4 2 );
p rin t(b b .g e tF lo a t());
b b .re w in d ();
/ / U p iš i i p r o č i t a j b r o j t i p a do u ble:
b b . a s D o u b le B u f f e r ( ) . p u t (99471142) ;
p r i n t ( b b . g e t D o u b le ( ) ) ;
b b . rewi n d ( ) ;
}
} / * Is p is :
i = 1025
Z d r a v o !
12390
99471142
99471142
9 . 9471144E7
9 . 9471142 E7
* ///.-
Nakon dođele memorije objektu tipa ByteBuffer, proverili smo da li su njegove vred-
nosti automatski postale nula - i jesu. Pročitane su sve 1024 vrednosti (do kraja bafera
koji daje metoda lim it()), i sve su bile nula.
Vrednosti prostih tipova najlakše je umetnuti u ByteBuffer pomoču odgovarajučih
,,prikaza“ tog bafera koje daju metode asC harB uffer(), asShorlB uffer() itd., i zatim tre-
ba upotrebiti metodu p u t( ) tog prikaza. Upravo taj postupak smo primenili za sve proste
tipove podataka. Među svim tim metodama, jedino je čudna metoda p u t( ) za ShortBuf-
fer koja zahteva eksplicitnu konverziju tipa (a ona odseca i menja rezultujuću vrednost).
Nijedan od ostalih prikaza bafera u svojoj metodi p u t ( ) ne zahteva konverziju tipa.
Poglavlje 18: Javin ulazno-izlazni sistem 761
Baferi prikaza
„Bafer prikaza“ omogućuje da ByteBuffer u pozadini posmatrate kroz prozor određenog
prostog tipa. ByteBuffer je i dalje jedino skladište koje ,,podupire“ taj prikaz, pa se sve iz-
mene koje napravite u prikazu odražavaju u izmenama podataka u ByteBuffer baferu.
Kao što ste videli u prethodnom primeru, na raspolaganju su vam pomoćne metode za
umetanje prostih tipova u ByteBuffer. Prikaz omogućuje i čitanje vrednosti prostih tipo-
va iz ByteBuffer bafera, bilo jednu po jednu (što ByteBuffer dozvoljava) bilo u grupama
(kao nizove). Evo primera kako se pomoću klase IntBuffer radi s celim brojevima (int) u
ByteBuffer baferu:
p u b l i c c la s s I n t B u f f e r P r i m e r {
p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
B y t e B u f f e r bb = B y t e B u f f e r . a l 1ocate(VELBAF);
In tB u ffe r ib = b b .a s In tB u ffe r ( ) ;
/ / U s k l a d i š t i n iz c e l i h b r o je v a :
i b . p u t ( n e w i n t [ ] { 11, 42, 47, 99, 143, 811, 1016 } ) ;
/ / Č i t a n j e i p is a n je sa /u a p s o lu tn e l o k a c i j e :
S y s te m .o u t.p rin tln (ib .g e t(3 ));
i b . p u t (3, 1811);
/ / Zadavanje novog k r a j a b a fe ra pre " p r e m o t a v a n ja " .
ib .fl ip ( ) ;
w h i1e (ib . h a s R e m a in in g ( ) ) {
in t i = ib .g e t();
S y s te m .o u t. p r i n t l n ( i ) ;
}
}
} /* Is p is :
99
11
42
47
1811
143
811
1016
* ///:-
određeni tip proste vrednosti. U narednom prim eru se ista sekvenca bajtova redom tu-
mači kao broj tipa short, int, float, long i double, tako što se proizvode odgovarajući
baferi prikaza za isti ByteBuffer:
p u b l i c c la s s B a f e r i P r i k a z a {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
B y t e B u f f e r bb = B y t e B u f f e r . w r a p (
new b y t e [ ] { 0, 0, 0, 0, 0, 0, 0, ' a ' } ) ;
b b .re w in d ();
p rintn b ("B yte B u ffer " );
w h ile ( b b . h a s R e m a in in g ( ))
p r i n t n b ( b b . p o s i t i o n ( ) + " -> " + b b . g e t ( ) + " , " ) ;
p rin t();
C h a rB u ffe r cb =
( (B y te B u ffe r)b b .re w in d ()). a s C h a rB u ffe r();
p rintn b ("C h a rB u ffe r " ) ;
w h i1e(cb.hasRemai ni n g ( ) )
p r i n t n b ( c b . p o s i t i o n ( ) + " -> " + c b . g e t ( ) + " , " ) ;
p rin t();
F lo a tB u ffe r fb =
( ( B v t e B u f f e r ) b b . rewi n d ( ) ) . a s F l o a t B u f f e r ( ) ;
p rin tn b ("F lo a tB u ffe r " ) ;
w h i1e(fb .h a sR e m a in i n g ( ) )
p r i n t n b ( f b . p o s i t i o n ( ) + " -> " + f b . g e t ( ) + " , " ) ;
p rin t();
In tB u ffe r ib =
( (B y te B u ffe r)b b .re w in d ()) , a s I n tB u f fe r ( ) ;
p rin tn b ( " In tB u ffe r ");
w h i l e ( ib . h a s R e m a in in g ( ) )
p r i n t n b ( i b . p o s i t i o n ( ) + " -> " + i b . g e t ( ) + " , " ) ;
p rin t();
L o n g B u ffe r l b =
( ( B y t e B u f f e r ) b b . r e w i nd( ) ) . asLongBuff e r ( ) ;
p rin tn b ("L o n g B u ffe r " ) ;
w h i1e ( l b . hasRemaining ( ) )
p r i n t n b (1b . p o s i t i o n ( ) + " -> " + l b . g e t ( ) + " , “ ) ;
p rin t();
S h o r t B u f f e r sb =
((B y te B u ffe r)b b .re w in d ()).a s S h o rtB u ffe r();
p rintn b ("S h ortB u ffe r " ) ;
w h i1e(sb.hasRemai ni n g ( ) )
p r i n t n b ( s b . p o s i t i o n () + " -> " + s b . g e t ( ) + " , ” ) ;
p rin t();
D o u b le B u ffe r db =
((B y te B u ffe r)b b .re w in d ()) .a s D o u b le B u ffe r();
p rin tn b ("D o u b le B u ffe r " ) ;
Poglavlje 18: Javin ulazno-izlazni sistem 763
w h ile ( d b .h a s R e m a in in g ( ))
p r i n t n b ( d b . p o s i t i o n ( ) + 11 -> " + d b . g e t ( ) + " , ");
}
} /* Is p is :
B y t e B u f f e r 0 -> 0, 1 -> 0, 2 -> 0, 3 -> 0, 4 -> 0, 5 -> 0, 6 -> 0 , 7 -> 97,
C h a rB u ffe r 0 -> , 1 -> , 2 -> , 3 -> a,
F l o a t B u f f e r 0 -> 0 . 0 , 1 -> 1.36E-43,
I n t B u f f e r 0 -> 0, 1 -> 97,
L o n g B u ffe r 0 -> 97,
S h o r t B u f f e r 0 -> 0, 1 -> 0, 2 -> 0 , 3 - > 97,
D o u b le B u ffe r 0 -> 4.8E-322,
* ///:-
ByteBuffer je proizveđen omotavanjem osmobajtnog niza tipa byte koji se zatim pri-
kazuje putem bafera prikaza svih prostih tipova. U narednom dijagramu prikazana su
različita tumačenja istih bitova kada se čitaju iz različitih vrsta bafera:
Ovo odgovara ispisu rezultata programa.
0 0 0 0 0 0 0 97 bytes
a chars
0 0 0 97 shorts
0 97 ints
97 longs
4.8E-3 22 d oubles
Vežba 24: (1) Izmenite program IntBufferPrimer.java tako da se koriste brojevi tipa
double.
Endiani
Podaci se na raznim računarima skladište na različite načine. Na „big endian“ plat-
formama, najznačajniji bajt se smešta na najnižu memorijsku adresu, dok se na „little en-
dian“ platformama najznačajniji bajt smešta na najvišu memorijsku adresu. Kada
skladištite vrednost veću od jednog bajta, kao što su int, float itd., morate uzeti u obzir re-
dosled bajtova. ByteBuffer skladišti podatke u „big endian“ obliku, a tako se podaci uvek
šalju i preko mreže. Redosled smeštanja bajtova podataka u ByteBufferu možete prome-
niti metodom order( ), uz argument ByteOrder.BIG_ENDIAN ili ByteOr-
der.LITTLE_ENDIAN.
764 Misliti na Javi
bl b2
/ / : u i/E n d ia n i.ja v a
/ / R a z lik e između endiana i s k l a d i š t e n j e podataka.
im p o r t j a v a . n i o . * ;
im p o r t j a v a . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s Endiani (
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) (
B y t e B u f f e r bb = B y t e B u ffe r .w ra p ( n e w b y te [ 1 2 ] ) ;
bb .asC ha rB u ffe r().pu t("ab cd e f");
p rin t(A rra y s .to S trin g (b b .a rra y ()) ) ;
b b .re w i n d ( ) ;
bb.ord e r(B y te O rd er.B IG _ E N D IA N );
b b .a sC ha rB u ffe r().pu t("ab cd e f" ) ;
p rin t(A rra y s . to S trin g (b b .a rra y ( ) ) ) ;
b b .re w in d ();
bb.ord e r(B y te O rd er.L ITT L E _ E N D IA N );
b b .a s C h a rB u ffe r().p u t("a b c d e f" ) ;
p rin t(A rra y s .to S trin g (b b .a rra y ()));
}
} / * Is p is :
[ 0 , 97, 0, 98, 0, 99, 0, 100, 0 , 101, 0 , 102]
[ 0 , 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102]
[9 7, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0]
* ///:-
ByteBuffer je dobio dovoljno prostora da sve bajtove čuva u ni/.u znakova (charAr-
ray) kao spoljnom baferu, tako da se može pozvati metoda array( ) da bi prikazala sve
bajtove. Metoda a rra y () je neobavezna i možete je pozvati za bafere poduprte nizom; u
protivnom, dobićete U nsupportedO perationException.
Pomoću prikaza CharBuffer umeće se charA rray u ByteBuffer. Nakon prikazivanja
svih bajtova uverićete se da je podrazumevani redosled jednak kasnijem eksplicitno po-
zvanom poretku big endian, dok redosled little endian zamenjuje bajtove.
Poglavlje 18: Javin ulazno-izlazni sistem 765
g e tC h a n n e l()
,w rite (B yte B u ffe r)
F ile C h a n n e l <- B yteB u ffer
rea d (By te B u ffe r)
~K\
m ap(F ileC hannel. M a p M o d e , position, size)
M a p p e d B y te B u ffe r
Detaljno o baferima
Bafer se sastoji od podataka i četiri indeksa za pristupanje tim podacima i delotvoran rad
s njima: marker, položaj, kraj i kapacitet. Postoje metode za postavljanje vrednosti tih in-
deksa, za njihovo resetovanje i za ispitivanje njihovih vrednosti.
Ove indekse ažuriraju metode koje umeću i vade podatke iz batera. Tako indeksi
odražavaju izmene sprovedene u baferu.
U narednom primeru upotrebljen je veoma jednostavan algoritam (zamena susednih
znakova) da bi se ispreturali i ponovo vratili na mesta znakovi u CharBufferu:
//: u i/ U p o t r e b a B a f e r a . ja v a
im p ort j a v a . n i o . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s UpotrebaBafera {
p r i v a t e s t a t i c v o id s i m e t r i c n o P r e t u r a n j e ( C h a r B u f f e r b a f e r ) {
w hile (b afe r.h asR e m a in in g ()) {
b a fe r.m a rk();
ch ar c l = b a f e r . g e t ( ) ;
ch a r c2 = b a f e r . g e t ( ) ;
b a fe r.re s e tO ;
b a fe r.p u t(c 2 ).p u t(c l);
Poglavlje 18: Javin ulazno-izlazni sistem 767
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
c h a r [ ] podaci = " U p o t r e b a B a f e r a " . t o C h a r A r r a y ( ) ;
B y t e B u f f e r bb = B y t e B u f f e r . a l 1o c a t e ( p o d a c i .1 ength * 2 ) ;
C h a rB u ffe r cb = b b . a s C h a r B u f f e r ( ) ;
cb .p u t(p o d a c i);
p rin t(c b .re w in d ());
s im e tric n o P re tu ra n je (c b );
p rin t(c b .re w in d ());
s im e tri cn o P re tu ra n je (c b );
p rin t(c b .re w in d ( ) ) ;
}
} / * Is p is :
UpotrebaBafera
pUtoera baBefa r
UpotrebaBafera
* ///:-
Iako se CharBuffer može napraviti neposredno metodom w ra p () od niza tipa char,
umesto toga smo memoriju dodelili ByteBufferu, a CharBuffer je napravljen kao prikaz
toga ByteBuffera. Time se ističe da je cilj uvek koristiti ByteBuffer, pošto on radi nepo-
sredno s kanalom.
Evo kako izgleda bafer na ulazu u metodu sim etricnoP returanje():
rwi
J
u p o t r e b a B a f e r a
i -’i i
1ndeks položaja pokazuje na prvi element bafera, a indeksi kapacitet i kraj na posiednji.
U metodi simetricnoPreturanjef ), petlja while iterira dok indeks položaja ne dođe do
vrednosti kraj. Indeks (tekućeg) položaja bafera se menja kada se pozovu relativne funk-
cije g e t( ) ili p u t( ) (pozivi bez argumenata). Možete pozivati i apsolutne metode g e t() i
p u t( ), sa indeksom položaja elementa kao argumentom koji odreduje mesto na kojem se
odigrava vadenje (metodom g e t()) odnosno umetanje (metodom p u t()). Te (apsolutne)
metode ne menjaju vređnost indeksa položaja bafera.
Kada kontrola ude u petlju while, vrednost indeksa m arker biva zadata pozivom me-
tode m a rk (). Tada je stanje bafera:
I m ,!r | 1 M -) 1
/ s/
U p O t r e b a B a f e r a
I ferai I
768 Misliti na Javi
Dva poziva relativne metode g e t() smeštaju vrednost prva dva znaka u promenljive cl i
c2. Nakon ta dva poziva, bafer izgleda ovako:
I mar I I kap |
\/ \/
u p O t r e b a B a f e r a
/
1gg I I krai I
I mar I j kap |
N,/ s✓
u p o t r e b a B a f e r a
I Pol I I k ra j |
\/ \
p u o t r e b a B a f e r a
\
T
Tokom sledeće iteracije petlje, indeksu marker se zadaje tekuća vrednost indeksa po-
ložaja:
\/ \/
P U o t r e b a B a f e r a
\
Postupak se nastavlja do prolaska kroz ceo bafer. Na kraju petlje vvhile, indeks položaja
pokazuje na kraj bafera. Kada ispišete sadržaj bafera, ispisuju se samo znakovi između po-
ložaja i kraja. Dakle, da biste ispisali ceo sadržaj bafera, indeksu položaja morate me-
todom rew in d () zadati vrednost poćetka bafera. Evo stanja bafera nakon poziva metode
rew in d () (vrednost indeksa marker postaje nedefinisana):
Poglavlje 18: Javin ulazno-izlazni sistem 769
I tap I
\/
p u t o e r a b a B e f a r
/ \
I pol I I kraj |
Kada se ponovo pozove metoda sim etricn o P retu ranje(), CharBuffer se podvrgava
istom postupku i vraća u svoje prvobitno stanje.
/ / : u i / V e l i k e P r e s lik a v a n e D a to te k e .ja v a
/ / P r a v l j e n j e veoma v e l i k e d a tote ke pomoću p r e s l i k a v a n j a .
/ / {RunByHand}
im p o rt j a v a . n i o . * ;
im p o r t j a v a . n i o . c h a n n e l s . * ;
im p o r t j a v a . i o . * ;
im p o rt s t a t i c n e t . m i n d v i ew.u t i 1 . P r i n t . * ;
p u b l i c c la s s V e lik e P re s lik a v a n e D a t o te k e {
s t a t i c i n t duzina = 0x8FFFFFF; / / 128 MB
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n {
MappedByteBuffer i z l a z =
new R a n d o m A c c e s s F ile C t e s t . d a t " , " r w " ) .getChannel ()
,map(FileChannel.MapMode.READ_WRITE, 0, d u z in a ) ;
f o r ( i n t i = 0; i < d u z in a ; i++)
iz la z .p u t((b y te )' x ' );
p r i n t ( " P is a n je završeno");
f o r ( i n t i = d u z in a /2 ; i < d u z in a /2 + 6; i+ + )
p rin tn b ((c h a r)iz la z .g e t(i) );
}
} III--
Da bismo obrađili i čitanje i pisanjc, počinjemo tako što pravimo (objekat tipa) Ran-
domAccessFile, pribavimo kanal za tu datoteku, i zatim pozovemo metodu n ia p () koja
proizvodi objekat tipa MappedByteBuffer, što je posebna vrsta direktnog bafera. Obra-
tite pažnju na to da možete zađati početnu tačku i dužinu oblasti u datoteci koju želite da
preslikate; to znači da imate mogućnost da preslikavate manje oblasti velikih datoteka.
MappedByteBuffer je izvedena iz klase ByteBuffer, pa ima sve njene metode. Ovde su
prikazane samo veoma jednostavne primene metoda p u t( ) i g e t(), ali na raspolaganju su
vam i metode kao asC harB uffer() itd.
770 Misliti na Javi
Performanse
Iako su performanse ,,starog“ U/I sistema zasnovanog na tokovima poboljšane realizaci-
jom pomoču nio klasa, obično se još mnogo brže pristupa datotekama preslikanim u me-
moriju. U narednom programu upoređene su njihove performanse (na veoma
jednostavan način):
I n t B u f f e r i b = fc .m a p (
FileChannel ,MapMode.READ_WRITE, 0, f c . s i z e O )
.a s In tB u ffe r();
ib .p u t(O );
f o r ( i n t 1 = 1; i < b r o j l lp is a U B a f e r ; i+ +)
ib .p u t( ib .g e t(i - 1 ));
fc .c lo s e ();
}
}
};
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) {
fo r(T e ste r te s t : is p itiv a n ja )
te s t.ru n T e s t();
}
} / * I s p i s : (90% podudaranja)
U p is iv a n je u t o k : 0.56
U p is iv a n je u p r e s l i k a n u : 0.12
Č i t a n j e t o k a : 0.80
Č i t a n j e p r e s l i k a n e : 0.07
Č i t a n j e i z t o k a / p i s a n j e u t o k : 5.32
Č i t a n j e i z p r e s l i k a n e / p i s a n j e u p r e s l i k a n u : 0.0 2
* ///:-
Kao što ste videli u prethodnim primerim a iz ove knjige, metoda runTest( ) je upo-
trebljena u projektnom obrascu Template M ethod (Šablonska metoda) za pravljenje
strukture za ispitivanje raznih realizacija metode te s t( ) defmisanih u anonimnim unu-
trašnjim potklasama. Svaka od tih potklasa obavlja jednu vrstu ispitivanja, pa te metode
te s t() predstavljaju i prototip za obavljanje raznih U/I operacija.
Premda izgleda kao da se za pisanje u preslikanu datoteku upotrebljava tok FileOut-
putStream, sav izlaz preslikanih datoteka mora upotrebljavati klasu RandomAccessFile,
baš kao čitanje/pisanje u prethodnom kodu.
Obratite pažnju na to da te s t() metode obuhvataju i vreme za inicijalizaciju raznih U/I
objekata. Priprema preslikanih datoteka ume da bude skupa, ali je sveukupni dobitak
značajan kada se uporedi sa stanjem pri koriščenju U/I sistema zasnovanih na toku.
Vežba 25: (6) Napravite eksperiment tako što čete u primerima iz ovog poglavlja naredbu
ByteBuffer.aIlocate( ) promeniti u ByteBuffer.aIlocateDirect( ). Pokažite razlike u per-
formansama, i proverite da li se prim etno menja trajanje pokretanja programa.
Vežba 26: (3) Izmenite program strings/JGrep.java tako da koristi Java nio datoteke
preslikane u memoriju po đelovima.
Zaključavanje datoteka
Zaključavanje datoteka omogućuje sinhronizovanje pristupa datoteci kao deljenom re-
sursu. Medutim, dve niti koje se takmiče za istu datoteku mogu biti u različitim JVM-ovi-
ma ili jedna može biti Java nit, a druga nit operativnog sistema. Zaključavanje datoteka
vide ostali procesi operativnog sistema zato što se zaključavanje Java datoteka direktno
preslikava u uslužnu metodu operativnog sistema za zaključavanje.
Poglavlje 18: Javin ulazno-izlazni sistem 773
/ / : u i/ Z a k l j u c a v a n j e D a t o t e k a . j a v a
im p o r t j a v a . n i o . c h a n n e l s . * ;
im p o r t j a v a . u t i l . c o n c u r r e n t . * ;
im p o r t j a v a . i o . * ;
p u b l i c c la s s Z a k lju c a v a n je D a t o t e k a {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exceptio n {
F ile O u tp u tS tre a m fos= new F i l e O u t p u t S t r e a m ( " d a t o t e k a . t x t " ) ;
F ile L o c k f l = f o s . g e t C h a n n e l( ) . t r y L o c k ( ) ;
i f ( f l != n u l 1) {
S yste m .o u t.p rin tln ("Z a k lju č a n a d a to te k a ");
Timellnit.MILLISECONDS.sl e e p (lO O );
f l ,re le a s e ();
S y s t e m . o u t . p r i n t l n ( " O t k l ju č a n a d a t o t e k a " ) ;
}
fo s .c lo s e O ;
}
} / * Is p is :
Z a k lju č a n a d a tote k a
O t k lju č a n a d a tote k a
* ///:-
Bravu (objekat tipa FileLock) cele datoteke dobijate pozivom metoda tryLock( ) ili
lock( ) za objekat tipa FileChannel. (SocketChannel, D atagram Channel i ServerSoc-
ketC hannel ne treba zaključavati, pošto su to po svojoj prirodi entiteti jednog procesa; po
pravilu se mrežna utičnica ne deli između dva procesa.) tryL ock() je neblokirajuća me-
toda. Ona će pokušati da prigrabi bravu za sebe, ali ako u tome ne uspe (kada neki drugi
proces već ima tu bravu, a ona nije deljena), vratiće se bez rezultata. Metoda lo c k () blo-
kira sve dok ne pribavi bravu (engl. lock), ili dok nit koja je pozvala lo c k () ne bude pre-
kinuta, ili dok se ne zatvori kanal za koji je metoda lock( ) pozvana. Brava se oslobađa
(prepušta drugima) metodom FileLock.release().
Može se zaključati i deo (oblast) datoteke, i to pozivom:
ili
Podršku za isključive (engl. exclusive lock) ili deljene brave mora dati operativni sistem.
Ukoliko operativni sistem ne podržava deljene brave, a primi zahtev da napravi jednu
takvu, biče upotrebljena isključiva brava umesto deljene. Vrstu brave (deljena ili isključi-
va) možete saznati kad pozovete metodu FileLock.isShared().
p u b l i c c la s s Z a k lj u c a v a n je P r e s lik a n ih D a t o t e k a {
s t a t i c f i n a l i n t DUZINA = 0x8FFFFFF; / / 128 MB
s t a t i c FileChannel f c ;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws E x ce p tio n {
fc =
new R a n d o m A c c e s s F ile ( " t e s t . d a t “ , " r w " ) .g e t C h a n n e l( ) ;
MappedByteBuffer i z l a z =
fc.map(FileChannel.MapMode.READ_WRITE, 0 , DUZINA);
f o r ( i n t i = 0; i < DUZINA; i+ + )
i z l a z . p u t ( ( b y t e ) ' x 1) ;
new Z a k l j u c a j l P r o m e n i ( i z l a z , 0, 0 + DUZINA/3);
new Z a k l j u c a j l P r o m e n i ( i z l a z , DUZINA/2, DUZINA/2 + DUZINA/4);
}
p r i v a t e s t a t i c c la s s Z a k lj u c a jl P r o m e n i extends Thread {
p r iv a te B yteB uffer baf;
p r i v a t e i n t po ce ta k, k r a j ;
Z a k l j u c a j l P r o m e n i ( B y t e B u f f e r mbb, i n t p o c e ta k , i n t k r a j ) {
t h i s . p o c e t a k = poceta k;
th is .k ra j = k ra j;
mbb.1i m i t ( k r a j ) ;
m b b .p o s itio n (p o c e ta k );
b a f = m b b . s li c e ( ) ;
p o c e ta k();
}
publ i c v o id r u n ( ) {
try {
/ / I s k l j u č i v a brava bez p r e k la p a n ja :
F ile L o c k f l = f c . l o c k ( p o c e t a k , k r a j , f a l s e ) ;
S y s t e m . o u t . p r i n t l n ( " Z a k l ju c a n o : "+ pocetak +" do "+ k r a j ) ;
/ / Obavi izmene:
w h i l e ( b a f . p o s i t i o n ( ) < b a f . l i m i t ( ) - 1)
Poglav[je 18: Javin ulazno-izlazni sistem 775
b a f . p u t ( (by t e ) ( b a f . g e t ( ) + i ) ) ;
f l .re le a s e ();
S y s te m .o u t.p rin tln ("O tk lju c a n o : "+ pocetak +" do “ + k r a j ) ;
} c a t c h ( I 0 E x c e p t io n e) (
th ro w new R u n tim e E x c e p tio n ( e ) ;
}
}
}
} lll--~
Klasa niti ZakljucajlProm eni priprema oblast bafera i metodom slice() pravi odsečak
(engl. slice) koji če biti izmenjen, a u metodi r u n ( ) pribavlja se brava za kanal datoteke (ne
možete pribaviti bravu bafera, nego samo kanala). Metoda lo ck () poziva se veoma slično
pribavljanju zaključane niti objekta - sada imate „kritičnu sekciju“ sa isključivim pristu-
pom tom delu datoteke.5
Brave se otključavaju automatski kada JVM izađe iii kada se zatvori kanai za koji je
brava pribavljena, ali za otključavanje objekta tipa FileLock možete i eksplicitno da po-
zovete metodu release() kao što je urađeno u prethodnom programu.
Komprimovanje
Javina U/I biblioteka sadrži klase koje podržavaju čitanje i upisivanje tokova u kompri-
movanom formatu. Te klase treba omotati oko postojećih U/I klasa.
Ove klase nisu izvedene iz klasa Reader i Writer, već su deo hijerarhija klasa Input-
Streain i O utputStream . Razlog je to što biblioteka za komprimovanje radi s bajtovima,
a ne sa znacima. Ipak, ponekad ćete biti prim orani da kombinujete ove dve vrste tokova.
(Zapamtite da za jednostavnu konverziju između ova dva tipa možete koristiti klase In-
putStream Reader i OutputStreamReader.)
Iako postoje mnogi algoritmi za komprimovanje, Zip i GZIP se verovatno najčešće ko-
riste. Zbog toga s komprimovanim podacima možete lako da radite pom oću brojnih alat-
ki za čitanje i upisivanje u pomenutim formatima.
p u b l i c c la s s GZIPKomprimovanje {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
throvvs IO Exception {
i f ( a r g s . l e n g t h == 0) {
S y s t e m . o u t . p r i n t l n(
"U potreba: \nGZIPKomprimovanje d a t o t e k a \ n " +
" \ t U p o t r e b l ja v a GZIP komprimcvanje da bi komprimovao " +
" d a to te k u u t e s t . g z " ) ;
S y s te m .e x it(l);
}
Buffe redReader i n = new BufferedReaderf
new F i l e R e a d e r ( a r g s [ 0 ] ) ) ;
Bu ffe red O u tp u tS tre a m o u t = new B u fferedO utputStre am (
new GZIPOutputStream(
new F ile O u t p u t S t r e a m ( “ t e s t . g z " ) ) ) ;
S y s te m .o u t.p rin tln ("U p is u d a to te k u ");
i n t c;
w h i l e ( ( c = i n . r e a d ( ) ) != -1)
o u t.w ri-te (c );
in .c lo s e O ;
o u t.c lo s e O ;
S y s te m .o u t.p rin tln ("C ita n je d a to te k e ");
B u fferedReader in 2 = new Buffe red Reader(
new InputStreamReader(new GZIPInputStream(
new F i l e I n p u t S t r e a m ( " t e s t . g z " ) ) ) ) ;
S t r i n g s;
w h i l e ( ( s = i n2 . r e a d L i n e ( ) ) != n u l l )
S y s te m .o u t.p rin tln (s );
}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta te ) *///:-
/ / : u i/ Z ip K o m p r im o v a n je . ja v a
/ / K o r i s t i fo rm a t Z ip za komprimovanje p r o i z v o l j n o g
/ / b r o j a d a to te k a k o j i se z adaje na komandnoj l i n i j i .
/ / ( A r g s : Zip K o m p rim ova n je .ja va }
im p o r t j a v a . i o . * ;
im p o r t j a v a . u t i l
im p o r t j a v a . u t i l . z i p . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s ZipKomprimovanje {
p u b l i c s t a t i c vo id m a i n ( S t r i n g [ ] args)
thro ws IOExce ption {
Fi 1eOutputStream f = new F i l e O u t p u t S t r e a m C t e s t . z i p " ) ;
CheckedOutputStream csum =
new Ch eckedOutp utStream (f, new A d l e r 3 2 ( ) ) ;
Z i pOutputStream zos = new Z ipO utputStream (csum );
Buffe red O u tp u tStre a m o u t = new B u ff e r e d O u t p u t S t re a m (z o s ) ;
zos.setComment("Proba pakovanja u fo rm a tu Z i p " ) ;
/ / Ip a k , nema odgovaraju će metode getComment().
f o r ( S t r i n g arg : args) {
p r i n t ( " U p i s u d a to te k u " + a r g ) ;
BufferedReader in =
new Buffe red Reader(
new F i 1e R e a d e r(a rg )) ;
z o s . p u t N e x t E n t r y (new Zi p E n t r y ( a r g ) ) ;
in t c;
w h i l e ( ( c = i n . r e a d ( ) ) != -1)
out.w ri t e ( c ) ;
i n .c lo se ();
o u t . f l ush( ) ;
}
o u t.c lo s e O ;
/ / K o n t r o l n i z b i r j e važeći tek po z a t v a r a n ju d a to t e k e !
p rin t(" K o n tro ln i z b ir: " + cs u m .g e tC h e c k s u m ().g e tV a lu e ()) ;
774 Misliti na Javi
Podršku za isključive (engl. exclusive lock) ili deljene brave mora dati operativni sistem.
Ukoliko operativni sistem ne podržava deljene brave, a primi zahtev da napravi jednu
takvu, biće upotrebljena isključiva brava umesto deljene. Vrstu brave (deljena ili isključi-
va) možete saznati kad pozovete metodu FileLock.isShared().
p u b l i c c la s s Z a k lj u c a v a n je P r e s lik a n ih D a t o t e k a {
s t a t i c f i n a l i n t DUZINA = 0x8FFFFFF; / / 128 MB
s t a t i c FileChannel f c ;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws E x ce p tio n {
fc =
new R a n d o m A c c e s s F ile ( " t e s t . d a t" , " r w " ) . g e t C h a n n e l ( ) ;
MappedByteBuffer i z l a z =
f c . m a p ( F i 1eChannel.MapMode.READ_WRITE, 0, DUZINA);
f o r ( i n t i = 0; i < DUZINA; i+ + )
i z l a z.p u t((b yte )‘ x ' );
new Z a k lju c a j I P r o m e n i ( i z l a z , 0, 0 + DUZINA/3);
new Z a k l j u c a j l P r o m e n i ( i z l a z , DUZINA/2, DUZINA/2 + DUZINA/4);
}
p r i v a t e s t a t i c c la s s Z a k lj u c a jl P r o m e n i extends Thread {
p r iv a te B yteB uffer baf;
p r i v a t e i n t po ce ta k, k r a j ;
Z a k l j u c a j I P r o m e n i ( B y t e B u f f e r mbb, i n t p o ce ta k , i n t k r a j ) {
t h i s . p o c e t a k = po ce ta k;
th is .k ra j = k ra j;
mbb.1imi t ( k r a j ) ;
m b b .p o sitio n (p o ce ta k);
b a f = m b b . s li c e ( ) ;
p o c e ta k ();
}
p u b l i c vo id r u n ( ) {
try {
/ / I s k l j u č i v a brava bez p re k la p a n ja :
F ile L o c k f l = f c . l o c k ( p o c e t a k , k r a j , f a l s e ) ;
S y s t e m . o u t . p r i n t l n ( " Z a k l ju c a n o : “ + poce tak +" do "+ k r a j ) ;
/ / Obavi izmene:
w h i l e ( b a f . p o s i t i o n ( ) < b a f . l i m i t ( ) - 1)
Poglavlje 18: Javin ulazno-izlazni sistem 775
Komprimovanje
Javina U/I biblioteka sadrži klase koje podržavaju čitanje i upisivanje tokova u kompri-
movanom formatu. Te klase treba omotati oko postojećih U/I klasa.
Ove klase nisu izvedene iz klasa Reader i Writer, već su deo hijerarhija klasa Input-
Streain i O utputStream . Razlog je to što biblioteka za komprimovanje radi s bajtovima,
a ne sa znacima. Ipak, ponekad ćete biti prim orani da kombinujete ove dve vrste tokova.
(Zapamtite da za jednostavnu konverziju između ova dva tipa možete koristiti klase In-
putStream Reader i OutputStreamReader.)
Iako postoje mnogi algoritmi za komprimovanje, Zip i GZIP se verovatno najčešće ko-
riste. Zbog toga s komprimovanim podađm a možete lako da radite pomoću brojnih alat-
ki za čitanje i upisivanje u pom enutim formatima.
p u b l i c c la s s GZIPKomprimovanje {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s)
throws IOException {
i f ( a r g s . l e n g t h == 0) {
System. out. . p r i n t l n (
"Up otreba: \nGZIPKomprimovanje d a t o t e k a \ n " +
" \ t U p o t r e b l ja v a GZIP komprimovanje da bi komprimovao " +
" d a to te k u u t e s t . g z " ) ;
S y s te m .e x it(l);
}
Buffe redReader i n = new Buffe red Re a d e r(
new F ile R e a d e r ( a r g s [ 0 ] ) ) ;
B u ffe red O u tp u tS tre a m o u t = new B u fferedO utputStre am (
new GZIPOutputStream(
new F i l e O u t p u t S t r e a m ( " t e s t . g z " ) ) ) ;
S y s te m .o u t.p rin tln ("U p is u d a to te k u ");
i n t c;
w h i l e ( ( c = i n . r e a d ( ) ) != -1)
o u t.w ri-te (c );
in .c lo s e O ;
o u t.c lo s e ();
S y s t e m . o u t . p r i n t l n ( "C i t a n j e d a t o t e k e " ) ;
BufferedReader in 2 = new Buffe red R e a d e r(
new InputStreamReader(new GZIPInputStreamf
new F i 1e l n p u t s t r e a m ( " t e s t . g z " ) ) ) ) ;
S t r i n g s;
w h i l e ( ( s = i n 2 . r e a d L i n e ( ) ) != n u l l )
S y s te m .o u t.p rin tln (s );
}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta te ) *///:-
/ / : u i/Z ip K o m p r im o v a n je .ja v a
/ / K o r i s t i fo rm a t Z ip za komprimovanje p r o i z v o l j n o g
/ / b r o j a d a to te k a k o j i se zadaje na komandnoj l i n i j i .
/ / { A rg s : Z ip K om prim ovanje .ja va)
im p o r t j a v a . i o . * ;
im p o r t j a v a . u t i l . * ;
im p o r t j a v a . u t i l . z i p . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s ZipKomprimovanje {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args)
thro ws IO Exception {
Fi 1eOutputStream f = new F i l e O u t p u t S t r e a m C t e s t . z i p " ) ;
CheckedOutputStream csum =
new Check edOutputStream ff, new A d l e r 3 2 ( ) ) ;
Z ipOutputStream zos = new Z ipO utputStream (csum );
BufferedO utputStre am o u t = new B u ff e r e d O u t p u t S t re a m (z o s ) ;
zos.setComment("Proba pakovanja u form atu Z i p " ) ;
/ / Ip a k , nema odgovarajuće metode getComment().
f o r ( S t r i n g arg : args) {
p r i n t ( " U p i s u d a to te k u " + a r g ) ;
BufferedReader i n =
new Buffe redReader(
new F i l e R e a d e r ( a r g ) ) ;
z o s .p u tN e x tE n try (n e w Z i p E n t r y ( a r g ) ) ;
i n t c;
w h i l e ( ( c = i n . r e a d ( ) ) != -1)
out.w ri t e ( c ) ;
in .c lo s e ();
o u t . flu s h ( ) ;
}
o u t.c lo s e O ;
/ / K o n t r o l n i z b i r j e važeći te k po z a t v a r a n ju d a t o t e k e !
p r i n t ( " K o n t r o l n i z b i r : " + csu m .g e tC h e c k su m ().g e tV a lu e ()) ;
778 Misliti na Javi
Metodi putN extE ntry( ) m orate da prosledite objekat klase ZipEntry za svaku datote-
ku koja se dođaje u arhivu. Takav objekat sadrži obim an interfejs koji om ogučuje čitanje i
zadavanje svih podataka o datom elem entu Zip datoteke; ime, kom prim ovana i nekom -
prim ovana veličina, datum , kontrolni zbir, dodatna polja podataka, komentar, rnetoda
kompresije i da li se radi o stavki direktorijum a. M edutim , Javina biblioteka ne podržava
uvođenje lozinke iako je form at Zip om ogućuje. Pored toga, mada klase C lieckedlnput-
Stream i CheckedOutputStream podržavaju oba tipa kontrolnih zbirova (Adler32 i
CRC32), klasa ZipEntry podržava sam o interfejs za kontrolni zbir tipa CRC. To ograniče-
nje nam eće form at Zip, ali zbog toga se ne može koristiti brži algoritam Adler32.
Klasa Z ipInputStream ima m etodu getN extE ntry( ) za raspakivanje arhive. Ta meto-
da vraća sledeći objekat klase ZipEntry ako on postoji. Postoji i kraća varijanta - proči-
tajte datoteku pom oću klase ZipFile koja im a m etodu en trie s( ), a ona vraća objekat tipa
Enum eration u kojem su nabrojani objekti klase ZipEntries.
Da biste pročitali kontrolni zbir, m orate na neki način da obezbedite pristup odgova-
rajućem objektu klase Checksum. Pri tom se zadržava referenca na objekte tipa Checke-
dO utputStream i CheckedlnputStream , ali m ožete da se oslonite i na referencu na
objekat tipa Checksum.
Z bunjujuća m etoda u Zip tokovim a je setC om m ent( ). Kao što je pokazano u gornjem
prim eru, prilikom upisivanja u datoteku m ožete da stavite komentar, ali ne postoji način
Poglavlje 18: Javin ulazno-izlazni sistem 779
O pcije su sam o zbirka slova (nisu neophodne crtice ili neki drugi pom oćni znaci). Ko-
risnici Unixa/Linuxa prim etiće sličnost s alatkom tar. Opcije su:
Ako spisak datoteka koje treba arhivirati sadrži poddirektorijum , taj poddirektorijum
se autom atski arhivira sa svim svojim poddirektorijum im a itd. Čuvaju se i inform acije o
putanji.
780 Misliti na Javi
Evo nekih tipičnih načina za korišćenje program a jar. Sledeća kom anda pravi JAR ar-
hivu mojaJarDatoteka.jar koja sadrži sve datoteke s klasam a u tekućem direktorijum u,
kao i autom atski generisanu m anifest datoteku:
Sledeća kom anda radi isto što i p reth o d n i prim er, ali uključuje i korisnički napravljenu
manifest datoteku mojManifest.mf:
N aredna kom anda daje sadržaj arhive m ojaJarD atoteka.jar u obliku liste datoteka:
j a r t f m ojaJarD atoteka.jar
Ako napravite JAR datoteku korišćenjem opcije o (nula), moći ćete da je stavite u pu-
tanju klasa:
Nakon toga Java može da traži datoteke s klasama u bibliotekam a lib l.jar i lib2.jar.
Alatka jar nije toliko korisna kao uslužni program zip. Na prim er, ne m ožete da doda-
jete ili ažurirate datoteke u postojećoj JAR arhivi, tj. ovakve arhive m orate da pravite ,,od
nule“. Takode, datoteke ne možete da prem eštate u JAR arhivu i da ih nakon prem eštanja
brišete. M eđutim , JAR datoteku, koja je napravljena u jednoj platform i, moći će da pro-
čita alatka ja r na bilo kojoj drugoj platform i, što je problem koji ponekad muči uslužne
program e zip.
Kao što ćete videti u poglavlju Grafička korisnička okruženja , JAR arhive se koriste i za
pakovanje zrna Jave.
Serijalizovanje objekata
Kada napravite objekat, on postoji onoliko dugo koliko vam je potreban, ali ni pod kojim
okolnostim a ne može postojati kada program prestane da se izvršava. Koliko god da to
isprva im a smisla, lm a situacija u kojim a bi bilo izvanredno korisno kada bi objekat
m ogao da postoji i sačuva svoje inform acije čak i kada se program ne izvršava. Tada bi ob-
jekat postojao i kada se program pokrene sledeći put, i sadržao bi iste inform acije koje je
imao i kada se program izvršavao pretho dn i put. Naravno, to m ožete postići upisivanjem
inform acija u datoteku ili bazu podataka, ali u duhu politike da sve treba da bude objekat,
Poglavlje 18: Javin ulazno-izlazni sistem 781
bilo bi veom a pogodno deklarisati da je odredeni objekat ,,trajan“, a da se Java p ri tom au-
tom atski pobrine za sve pojedinosti.
Serijalizovanje objekata u Javi om ogućuje pretvaranje svakog objekta koji realizuje in-
terfejs Serializable u niz bajtova iz kojeg se taj objekat kasnije može p o tp u n o rekonstru-
isati. To važi čak i u m režam a, što znači da m ehanizam serijalizovanja autom atski
kom penzuje razlike izm eđu operativnih sistema. Dakle, m ožete da napravite neki objekat
na računaru koji radi pod W indow som , serijalizujete ga i pošaljete preko mreže na Unix
računar gde će on biti pravilno rekonstruisan. Ne m orate da brinete o predstavljanju
objekata na različitim računarim a, redosledu bajtova niti o drugim detaljima.
Samo po sebi, serijalizovanje objekata je zanimljivo zato što om ogučuje realizovanje
lake trajnosti (engl. lightw eight persistence). Setite se da trajnost znači kako životni vek
objekta nije određen izvršavanjem program a, već objekat živi i izm eđ u izvršavanja pro-
grama. Efekat trajnosti možete da postignete upisivanjem serijalizovanog objekta na disk
i njegovim rekonstruisanjem pri ponovnom pozivanju program a. Epitet laka potiče otud
što ne m ožete da definišete objekat p om oću neke rezervisane reči za trajnost i da prepu-
stite sistem u da brine o detaljim a (iako će i to m ožda jednog dana biti m oguće). Umesto
toga, m orate eksplicitno da serijalizujete i deserijalizujete objekat u program u. Ukoliko
vam treba ozbiljniji m ehanizam trajnosti, razm otrite alatku H ibernate ( h ttp ://h ib ern a -
te.sourceforgc.net). Više inform acija o njoj potražite u knjizi T hin king in Enterprise Java,
koja se m ože preuzeti sa adrese w w w .M in d V iew .n et.
Serijalizovanje objekata je đodato u jezik radi podrške dvema važnim funkcijama. Ja-
vino daljinsko pozivanje m etoda (engl. reniote m eth o d invocation, R M I) om ogućuje objek-
tim a na drugim računarim a da se ponašaju kao da se nalaze na vašem računaru. Pri slanju
poruka udaljenim objektim a, serijalizovanje je neophodno da bi se preneli argum enti i
povratne vrednosti. Daljinsko pozivanje m etoda objašnjeno je u knjizi T h in kin g in Enter-
prise Java.
Serijalizovanje objekata je neophodno i za zrna Jave, o čem u će biti reči u poglavlju
Grafička korisnička okruženja. Kada se koristi zrno, inform acije o njegovom stanju po pra-
vilu se podešavaju već tokom projektovanja. O ne se m oraju sačuvati i rekonstruisati ka-
snije, kada se program pokrene. Taj zadatak obavlja serijalizovanje objekata.
Serijalizovanje objekta je prilično jednostavno, pod uslovom da on realizuje interfejs
Serializable (ovaj interfejs je sam o indikator i nem a m etode). Kada je serijalizovanje do-
dato u jezik, m noge klase standardne biblioteke prom enjene su da bi ga podržale, uklju-
čujući i om otače za proste tipove, sve kontejnerske klase i m noge druge. Mogu se
serijalizovati čak i objekti klase Class.
Da bi se objekat serijalizovao, treba napraviti neku vrstu izlaznog toka i om otati ga unu-
tar objekta klase ObjectOutputStream. Nakon toga treba sam o pozvati m etodu write-
Object( ) i objekat će biti serijalizovan i poslat u izlazni tok. (Serijalizovanje objekata je
binarno orijentisano i zbog toga koristi hijerarhije klasa InputStream i O utputStream .)
Za ob rn u t postupak, objekat klase InputStream se om otava u n u tar objekta tipa Object-
InputStream i poziva se m etoda readO bject( ). Kao i obično, dobija se referenca na obje-
kat klase Object, pa je neophodna konverzija naniže u ispravan tip.
Posebno pam etno rešenje prim enjeno u serijalizovanju objekata jeste to što se pored
čuvanja slike objekta prate i sve reference koje on sadrži, a čuvaju se i ti objekti, prate se
782 Misliti na Javi
njihove reference itd. Ovaj princip se ponekad naziva „m reža objekata“ povezana s jed-
nim objektom , i ona sadrži niz referenci na objekte i njihove članove. Kada biste m orali da
održavate sopstvenu šem u za serijalizovanje, bilo bi preteško napisati kod koji bi pratio
sve veze. Izgleda da serijalizovanje objekata u Javi om ogućuje da se to postigne bez po
muke, verovatno korišćenjem optim izovanog algoritm a koji krstari po mreži objekata. U
sledećem prim eru testira se m ehanizam serijalizovanja pravljenjem ,,crva“ od povezanih
objekata, pri čem u svaki pokazuje na sledeći segm ent crva i sadrži niz referenci na objekte
drugačije klase, nazvane Podatak:
//: ui/Crv.java
// Prikazuje serijalizaciju objekata.
import java.io.*;
import j a v a . u t i l .*;
import static ne t. mi nd vi ew .u ti l.Print.*
rezultat.append(sledeci);
return rezultat.toString( );
}
public static void main(String[] args)
throws ClassNotFoundException, IOException {
Crv w = new Crv(6, 'a');
pri nt("w = " + w);
ObjectOutputStream out = new ObjectOutputStream(
new Fi le Ou tputStreamC'crv.out"));
ou t. wr iteObject("Pamcenje cr v a \ n " ) ;
o u t. wr it eO bj ec t( w);
out.close(); // Prelazi u sledeci red
ObjectlnputStream in = new ObjectInputStream(
new Fi leInputStream("crv.out"));
String s = (String)in.readObject();
Crv w2 = (Crv)in.readObject();
pri nt(s + "w2 = " + w 2 ) ;
By teArrayOutputStream bout =
new By te Ar ra yO ut pu tS tr eam ();
ObjectOutputStream out2 = new O b j e ct Ou tp ut St re am (bo ut );
out2.write0bject("Pamcenje c r va \n ");
out2 .w ri te 0b je ct (w );
o u t 2 . f l u s h ();
ObjectlnputStream in2 = new ObjectInputStream(
new ByteArrayI np ut St re am( bo ut.t o B y t e A r r a y ()));
s = (Strin g ) in 2 .re ad Ob je ct();
Crv w3 = (Crv)in 2. re ad Ob je ct();
print(s + "w3 = " + w 3 ) ;
}
} /* Ispis:
Konstruktor crva: 6
Konstruktor crva: 5
Konstruktor crva: 4
Konstruktor crva: 3
Konstruktor crva: 2
Konstruktor crva: 1
w = :a (8 53):b(119):c (802) :d (788):e(199):f(881)
Pamcenje crva
w2 = :a (853):b (119):c(802):d(788):e (199):f(881)
Pamcenje crva
w3 = : a ( 8 5 3 ) :b(1 1 9 ) : c ( 8 0 2 ) : d ( 7 8 8 ) : e ( 1 9 9 ) : f (8 8 1 )
* ///:-
Svrha svega bila je da se napravi nešto što je dovoljno složeno da se ne m ože lako seri-
jalizovati ručno. Postupak serijalizovanja, m eđutim , prilično je jednostavan. Kada se na-
pravi O bjectO utputStream od nekog drugog toka, serijalizuje ga m etoda w riteO bject().
O bratite pažnju i na poziv m etode w riteO b ject( ) za objekat tipa String. Pom oću istih
m etoda kao za D ataO utputStream m ožete da upisujete i sve proste tipove (oni koriste
isti interfejs).
Postoje dva zasebna odeljka koda koji izgledaju slično. Prvi upisuje i čita datoteku, a
drugi čita i upisuje u niz bajtova. Serijalizovanje om ogućuje čitanje ili upisivanje objekta
u bilo koji D atalnputStream ili D ataO utputStream , uključujući i mrežu, kao što je na-
vedeno u knjizi T hinking in Enterprisc Java.
Prim ećujete li da deserijalizovan objekat zaista sadrži sve veze koje su postojale u ori-
ginalnom objektu?
U postupku deserijalizovanja objekta interfejsa Serializable ne poziva se nijedan kon-
struktor, čak ni podrazum evani. Ceo objekat se rekonstruiše na osnovu podataka iz ula-
znog toka.
Vežba 27: (1) Napravite klasu koja realizuje interfejs Serializable i sadrži referencu na ob-
jekat neke druge klase koja se m ože serijalizovati. Napravite instancu svoje klase, serijali-
zujte je tako da bude snim ljena na disk, zatim je rekonstruišite i proverite da li je postupak
ispravno sproveden.
Pronalaženje klase
Možda se pitate šta je potrebno da bi se objekat rekonstruisao iz serijalizovanog stanja. Na
prim er, pretpostavim o da ste serijalizovali objekat i poslali ga nekoin drugom računaru
kao datoteku ili preko mreže. Da li bi program na drugom računaru mogao da rekonstru-
iše objekat sam o na osnovu sadržaja datoteke?
Najbolji način da se odgovori na ovo pitanje jeste, kao i obično, da se proba. Sledeća
datoteka treba da se smesti u poddirektorijum ovoga poglavlja:
//: ui/Zeleni.java
// Klasa koja se može serijalizovati.
import java.io.*;
public class Zeleni implements Serializable {} ///:-
Datoteka koja pravi i serijalizuje objekat klase Zeleni nalazi se u istom direktorijum u:
//: ui/ZamrzavanjeZelenog.java
// Pravi serijalizovanu izlaznu datoteku.
import java.io.*;
izlaz.writeObject(zorcon);
}
} ///= -
Um esto da hvata i obrađuje izuzetke, ovaj program ih iz m etode m a in ( ) prosleđuje na
konzolu.
Kada prevedete i pokrenete program , on će dobijenu datoteku dosije.X sm estiti u di-
rektorijum ui. U njegov p oddirektorijum dosijex stavite sledeći listing:
//: ui/dosijex/MaliZeleni.java
// Pokušaj da se serijalizovana datoteka rekonstruiše
// bez klase objekata uskladištenih u toj datoteci.
// {RunByHand}
import java.io.*;
Čak i otvaranje datoteke i učitavanje objekta m isterija zahteva objekat klase Class za
klasu Zeleni; Javina virtuelna m ašina (JVM) neće moći da nađe datoteku Zeleni.class,
osim ako se ona nekim slučajem ne nađe u putanji klase, što nije slučaj u ovom prim eru,
pa će generisati izuzetak ClassNotFoundException. (Kao i obično, svaki dokaz o posto-
janju vanzem aljaca nestaje pre nego što se može proveriti njegova verodostojnost!) Javina
virtuelna m ašina m ora da pronađe odgovarajuću datoteku .class.
Upravljanje serijalizovanjem
Kao što vidite, podrazum evani m ehanizam serijalizovanja lako se koristi. M eđutim , šta
ako im ate specijalne potrebe? Možda posebno brinete o bezbednosti i zato ne želite da se-
rijalizujete delove objekta, ili m ožda nem a smisla da se neki podobjekat serijalizuje ako će
m orati ponovo da se pravi kada se objekat bude rekonstruisao.
Postupak serijalizovanja možete da kontrolišete realizovanjem interfejsa Externaliza-
ble um esto interfejsa Serializable. Interfejs Externalizable proširuje interfejs Serializa-
ble i dodaje m u dve m etode, writeExternal( ) i readExternal( ), koje se autom atski
pozivaju tokom serijali/ovanja i deserijalizovanja objekta, da bi omogućile izvršavanje
specijalnih operacija.
Sledeći prim er prikazuje jednostavne realizacije m etoda interfejsa Externalizable.
Klase Blipl i Blip2 su skoro iste, a malu razliku izm eđu njih prim etićete ako proučite kod:
786 Misliti na Javi
//: ui/Blipovi.java
// Jednostavno koriščenje interfejsa Externa1izable i jedna zamka.
import java.io.*;
import static net.mindview.util.Print.*;
//: u i / B 1 i p 3 .java
// Rekonstruisanje objekta externalizable.
import java.io.*;
import static net. mi nd vi ew .u ti1 .P r i n t .*;
throws IOException {
print("Bl ip3.wri teExternal11) ;
// Ovo morate da uradite:
out.writeObject(s);
out.writeInt(i);
}
public void readExternal(Objectlnput in)
throws IOException, ClassNotFoundException {
print("Blip3.readExternal");
// Ovo morate da uradite:
s = (String)in.readObject();
i =in.readlnt();
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
print("Pravljenje objekata:");
Blip3 b3 = new B1 ip3(“ tekst 11 , 47);
print(b3);
ObjectOutputStream o = new ObjectOutputStream(
new FileOutputStream("Bli p3.out"));
print("Snimanje objekta:");
o.wri te0bject(b3);
o. c los e O;
// Sada ga vraćamo:
ObjectlnputStream in = new ObjectInputStream(
new FileInputStream("Blip3.out"));
print("Rekonstruisanje b3:");
b3 = (Blip3)in.readObject();
print(b3);
}
} /* Ispis:
Pravljenje objekata:
Blip3(String x, int a)
tekst 47
Snimanje objekta:
B1ip3.writeExternal
Rekonstruisanje b3:
Konstruktor za B1ip3
B1ip3.readExternal
tekst 47
* ///:-
Da bi sve radilo kako treba, znači da, pored upisivanja važnih podataka iz objekta to-
kom izvršavanja m etode w riteE xternal( ) (ne postoji podrazum evano ponašanje kojim
se upisuju bilo koji članovi objekta interfejsa Externalizable), m orate i da rekonstruišete
te podatke u m etodi readE xternal( ). To isprva m ože da zbuni zato što bi ponašanje pod-
razum evanog konstruktora objekta Externalizable m oglo stvoriti pogrešan utisak da se
neka vrsta pam ćenja i rekonstruisanja dešava autom atski. To se ne dešava.
Vežba 28: (2) U program u Blipovi.java kopirajte datoteku i preim enujte je u ProveraBli-
pova.java. Preimenujte i klasu Blip2 u ProveraBlipova (prepravite je u javnu, a ispred kla-
se Blipovi uklonite oznaku public) . U klonite oznake //! iz datoteke i izvršite novi program .
Potom postavite znakove za kom entar ispred podrazum evanog konstruktora za klasu Pro-
veraBlipova. Izvršite program i objasnite zašto radi. O bratite pažnju na to da nakon pre-
vođenja m orate da izvršite program sa „java Blipovi" zato što se m etoda m a in ( ) i dalje
nalazi u klasi Blipovi.
Vežba 29: (2) U program u Blip3.java, pretvorite u kom entar po dva reda iza rečenica
„Ovo m orate da uradite:“ i pokrenite program . O bjasnite rezultat i zašto se on razlikuje
od slučaja kada su ta dva reda u program u.
S tačke gledišta program iranja, ovo je prilično čudno. Prvo m ožete pom isliti da te me-
tode, pošto nisu deo osnovne klase ili interfejsa Serializable, treba da b u d u definisane u
sopstvenim interfejsima. O bratite pažnju na to da su one definisane kao privatne, što zna-
či da treba da ih pozivaju sam o drugi članovi iste klase. M eđutim , njih ne pozivaju drugi
članovi klase, već m etode writeObject( ) i readO bject( ) klasa ObjectO utputStream i
ObjectlnputStream . (Izuzetno se trudim da se ne upuštam u iscrpljujuću raspravu o
istim im enim a m etoda. Objašnjenje: sam o bih vas zbunio.) M ožda se pitate kako objekti
klasa ObjectO utputStream i O bjectlnputStream im aju pistup privatnim m etodam a
vaše klase. M ožemo sam o pretpostaviti da je to deo magičnog postupka serijalizovanja.6
U svakom slučaju, sve što se definiše u interfejsu podrazum evano je javno, pa ako meto-
de w riteO bject( ) i readO bject( ) m oraju da budu privatne, one ne m ogu biti deo interfejsa.
Pošto m orate tačno da se pridržavate potpisa, učinak je isti kao da realizujete interfejs.
Izgleda kao da se posle poziva m etode O bjectO utputStream .w riteObject( ) objekat
interfejsa Serializable koji joj prosleđujete ispituje (bez sum nje, korišćenjem refleksije)
kako bi se ustanoviio cia li realizuje sopstvenu m etodu w riteO b ject( ). Ako je tako, pre-
skače se uobičajeni postupak serijalizacije i poziva nam enska m etoda w riteO bject( ). Isto
važi i za m etodu readO bject( ).
Postoji još jedna začkoljica. U nutar m etode w riteO bject( ) koju pišete možete da izve-
dete podrazum evanu akciju upisivanja objekta pozivom m etode defaultW riteO bject( ).
Slično, unutar m etode readO bject( ) možete da pozovete m etodu defaultR eadO bject( ).
Evo jednostavnog prim era koji pokazuje kako m ožete da upravljate čuvanjem i rekon-
struisanjem objekta koji realizuje interfejs Serializable:
private String a;
private transient String b;
public Ko nt ro laSerijalizovanja(String aa, String bb) {
a = "Nije transient; " + aa;
b = "transient: " + bb;
}
public String t o S t r i n g O { return a + “\n" + b; }
private void w r it eO bj ec t( Ob je ct Out pu tS tr ea m tok)
throws IOException {
t o k . de fa ul tW ri te Ob jec t( );
t o k. wr it e O b j e c t ( b ) ;
}
private void re ad Ob je ct (ObjectInputStream tok)
throws IOException, Cl assNotFoundException {
t o k. de fa ul tR ea dO bj ect ();
b = (Str in g) to k. re ad Ob jec t( );
}
public static void main(String[] args)
throws IOException, C1 assNotFoundException {
KontrolaSerijalizovanja sc = new Ko ntrolaSerijal izovanja("Testl",
"T e s t 2 " ) ;
System.out.println("Pre:\n" + sc);
By te Ar ra yO ut pu tS tr eam buf = new B y te Ar ra yO ut pu tS tr eam ();
Obje ct Ou tp ut St re am o = new Ob j e c t O u t p u t S t r e a m ( b u f ) ;
o. wr i t e O b j e c t ( s c ) ;
// Sada ga vraćamo:
Object ln pu tS tr ea m in = new ObjectInputStream(
new By t e A r r a y In pu tS t r e a m ( b u f .t o B y t e A r r a y ()));
KontrolaSerijalizovanja sc2 = (KontrolaSerijalizovanja)
in.readObjectO;
Sy st em .o ut.println("Posle:\n" + sc2);
}
} /* Ispis:
Pre:
Nije transient: Testl
transient: Test2
Posle:
Nije transient: Testl
transient: Test2
* ///:-
U ovom p rim eru jedno polje tipa String je obično, a drugo je označeno kao transient
kako bi se dokazalo da m etoda defaultW riteO bject( ) snim a i rekonstruiše polje koje nije
transient, a da se transient polje snim a i rekonstruiše eksplicitno. Polja se iniđjalizuju n
konstruktoru, a ne prilikom definisanja, kako bi se dokazalo da se ne inicijalizuju nekim
autom atskim m ehanizm om tokom deserijalizovanja.
Ako nam eravate da koristite podrazum evani m ehanizam za upis delova objekta koji
nisu označeni sa transient, m orate da pozovete m etodu đefaultW riteObject( ) kao prvu
operaciju u m etodi w riteO bject( ), odnosno m etodu defaultReadObject( ) kao prvu ope-
raciju u m etodi readO bject( ). To su čudni pozivi metoda. Izgledalo bi, na primer, kao da
Poglavlje 18: Javin ulazno-izlazni sistem 793
o.writeObject(sc);
Zadavanje verzije
M ožda ćete hteti da prom enite verziju klase koja se m ože serijalizovati (na prim er, objekti
originalne klase m ogu biti čuvani u bazi podataka). To je podržano u favi, ali ćete to raditi
sam o u specijalnim slučajevima, a pri tom m orate bolje razum eti problem , što ovde neće-
mo razm atrati. HTM L dokum entacija razvojnog paketa za Javu koja se m ože preuzeti
s lokacije http:lljava.sun .co m , detaljno obrađuje ovu temu.
U ovoj dokum entaciji uočićete m noge kom entare koji počinju sa:
U pozorenje: Serijalizovani objekd ove klase neće biti ko m p a tib iln i s budućim verzijam a
Svvinga. T renutna podrška serijalizaciji pogodna je za kratkotrajno čuvanje m etoda koje se
pozivaju daljinski u raznim aplikacijam a...
Ovo upozorenje stoji zato što je m ehanizam dodeljivanja verzije previše jednostavan
da bi pouzdano radio u svim situacijam a, naročito sa zrnim a Jave. Radi se na ispravci pro-
jekta, o čem u upravo i govori ovo upozorenje.
Korišćenje trajnosti
Tehnika serijalizovanja je prilično privlačna za čuvanje stanja nekog program a da bi se on
kasnije m ogao lako vratiti nazad. M eđutim , pre toga, m ora se odgovoriti na neka pitanja.
Šta će se desiti ako se serijalizuju dva objekta, a svaki od njih sadrži referencu na treći
objekat? Kada rekonstruišete ta dva objekta iz serijalizovanog stanja, da li će se treći obje-
kat pojaviti sam o jednom ? Šta će se desiti ako se dva objekta serijalizuju u zasebne dato-
teke, a potom deserijalizuju u različitim delovima koda?
Evo prim era koji prikazuje ovaj problem:
//: ui/MojSvet.java
import java.io.*;
import j a v a . u t i 1.*;
import static n e t .mindview.uti1 .P r i n t .*;
794 Misliti na Javi
//: ui/PamcenjeCADStanja.java
// Pamćenje stanja nazovi-CAD sistema.
import java.io.*;
import j a v a . u t i l .*;
796 Misliti na Javi
Klasa Oblik realizuje interfejs Serializable, pa se sve što se izvodi iz klase Oblik auto-
matski m ože serijalizovati. Svaki objekat klase Oblik sadrži podatke, a svaka klasa izvede-
na iz klase Oblik sadrži statičko polje koje određuje boju svih figura izvedenog tipa.
(Stavljanje statičkog polja u osnovnu klasu dalo bi samo jedno polje, pošto se statička
polja ne dupliraju u izvedenim klasama.) M etode osnovne klase se m ogu redefinisati da bi
se zadala boja različitih tipova oblika (za statičke m etode se ne prim enjuje dinam ičko po-
vezivanje, tj. radi se o običnim m etodam a.) M etoda slucajnoG enerisanje( ), kad god se
pozove, pravi različit objekat tipa Oblik tako što nasum ično bira tip oblika.
Klase Krug i Kvadrat jednostavna su proširenja klase Oblik; razlikuju se sam o po
tom e što se u klasi Krug boja inicijalizuje tokom definisanja, a u klasi Kvadrat - u kon-
struktoru. O dložićem o razm atranje klase Linija.
U funkciji m a in ( ) koristi se jedna lista tipa ArrayList za čuvanje objekata tipa Class i
još jedna za čuvanje oblika.
R ekonstrukđja objekata je prilično jednostavna:
//: ui /R ekonstruisanjeCADStanja.java
// Rekonstruisanje stanja nazovi-CAD sistema.
// {RunFirst: PamcenjeCADStanja}
import java.io.*;
import j a v a . u t i l .*;
Vidi se da se vrednosti xPos, yPos i dim uspešno snim aju i rekonstruišu, ali nešto nije
u redu sa rekonstruisanjem statičkih informacija. Sve tri ulaze, ali ne izlaze neprom enje-
ne. Krugovi im aju vrednost 1 (CRVENA, po definiciji), a kvadrati im aju vrednost 0 (se-
tite se da su inicijalizovani u konstruktoru). Izgleda kao da se statička polja uopšte nisu
serijalizovala! To je tačno: iako klasa Class može da se serijalizuje, ona ne radi ono što se
očekuje. Znači, ako želite da serijalizujete statičke podatke, to m orate da uradite sami.
U tu svrhu služe statičke m etode serijalizujStaticke( ) i deserijalizujStaticke( ) klase
Linija. V idite da se one eksplicitno pozivaju u postup ku snim anja i rekonstruisanja.
(O bratite pažnju na to da m ora biti održan redosled upisivanja i čitanja iz datoteke za se-
rijalizaciju.) Dakle, da bi ovi program i radili ispravno, m orate da:
1 . D odate m etode serijalizujStaticke() i deserijalizujStaticke() u sve klase oblika.
2. U klonite listu tipo viO b lika i kod koji je povezan s tom listom.
3. U oblike dodate pozive novih statičkih m etoda za serijalizovanje i deserijalizovanje.
Bezbednost je još jedna tem a o kojoj bi valjalo razmišljati, pošto se serijalizovanjem
čuvaju i privatni p o d ađ . Ako imate problem s bezbednošću, ta polja treba da označite kao
tra n sie n t. M edutim , tada m orate da smislite bezbedan način za čuvanje poverljivih in-
form acija tako da prilikom rekonstruisanja m ožete da vratite privatne prom enljive u pr-
vobitno stanje.
Vežba 30: (1) Popravite program CA D Stanje.java kako je opisano u tekstu.
XML
Važno ograničenje serijalizovanja objekata jeste njihova isključiva upućenost na Javu:
takve objekte m ogu da deserijalizuju samo Java program i. Konverzijom podataka u for-
m at XML dobilo bi se interoperabilnije rešenje koje bi mogle da koriste razne platform e
i jezici.
Zbog raširenosti XML-a postoji zbunjujuće velik broj opcija za program iranje koje ga
proizvode; m eđu njim a su i biblioteke javax.xm l.,<' koje se isporučuju uz razvojni paket
)ave. O dlučio sam da upotrebim biblioteku otvorenog izvornog koda XOM (preuzim anje
i dokum entacija na w w w .xo tn .m i ) - autor je Elliotte Rusty Harold. Izgleda mi da je to naj-
jednostavniji način da se XML proizvodi i m enja u Javi. Pored toga, XOM proizvodi veo-
ma ispravan XML kod.
Prim era radi, pretpostavim o da imate objekte tipa O soba i u njim a im ena i prezim ena
koja želite da serijalizujete u form atu XML. Naredna klasa O soba ima m etodu getX M L ()
koja pom oću XOM-a proizvodi podatke o klasi O soba konvertovane u XML Elem ent,
i konstruktor koji prim a Elem ent i vadi odgovarajuće podatke o klasi O soba (obratite
pažnju na to da su XML primeri u sopstvenom poddirektorijum u):
//: x m l / O s o b a .java
// Upotreba biblioteke X0M za pisanje i čitanje XML-a
// {Zahteva: nu.xom.Node; Morate instalirati
// biblioteku X0M sa http://www.xom.nu }
800 Misliti na Javi
import nu.xom.*;
import java.io.*;
import ja v a . u t i 1.*;
} /* Ispis:
[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry]
<?xml version="1.0" encoding="IS0-8859-l"?>
< 1judi>
<osoba>
<im>Dr. Bunsen</im>
<prez>Honeydew</prez>
</osoba>
<osoba>
<im>Gonzo</im>
<prez>The Great</prez>
</osoba>
<osoba>
<im>Phi11ip J.</im>
<prez>Fry</prez>
</osoba>
</ljudi>
* ///:-
XOM m etode su jasne same po sebi, a mogu se naći i u XOM dokum entaciji.
XOM sadrži i klasu S erializer koja je u m etodi f o r m a t( ) upotrebljena za pretvaranje
XML-a u čitljiviji oblik. Ukoliko sam o pozovete to X M L (), dobićete sve bačeno na gomi-
lu, pa je S erializer podesna alatka.
Jednostavno je i deserijalizovanje objekata tipa O soba iz XML datoteka:
//: x m l / L J u d i .java
// {Zahteva: nu.xom.Node; Morate instalirati
// biblioteku X0M sa http://www.xom.nu }
// {RunFirst: Osoba}
import nu.xom.*;
import j a v a . u t i l .*;
K onstruktor klase Ljudi otvara i čita datoteku XOM-ovom m etodom B u ild er.b u ild (),
a m etoda g etC h ild E lcm e n ts() proizvodi listu Elem ents (to nije standardna Javina lista,
nego objekat koji im a samo m etode s iz e () i g e t( ) - Harold nije želeo da natera korisnike
802 Misliti na Javi
da upotrcbljavaju Javn SE5, ali je ipak želeo da ima kontejner koji om ogućuje bezbedan
rad s tipovim a). Svaki Element u toj listi predstavlja objekat tipa Osoba, pa se predaje
drugom k onstrukto ru klase Osoba. Vodite računa o tom e da m orate unapred znati struk-
tu ru svoje XML datoteke, ali baš to je često slučaj u ovoj vrsti problem a. Ako ta struktura
ne odgovara onom e što očekujete, XOM će generisati izuzetak. Svakako da m ožete pisati
i složeniji kod koji će ispitati XML d o k um en t um esto da o njem u išta pretpostavlja, u
slučajevima kada im ate m anje konkretne inform acije o dolaznoj XML strukturi.
Da bi ovi prim eri mogli da se prevedu, m oraćete da stavite JAR datoteke iz XOM dis-
tribucije u svoju putan ju klasa.
Ovo je bio sam o kratak uvod u XML program iranje na Javi i s bibliotekom XOM; više
inform acija potražite na adresi w w w .xo m .n u .
Vežba 31: (2) Program im a Osoba.java i Ljudi.java dodajte odgovarajuće podatke o adresi.
Vežba 32: (4) Koristeći M ap<String,Integer> i uslužnu klasu net.mindview. util.TextFi-
le, napišite program koji prebrojava reči u datoteci (kao drugi argum ent u konstruktoru
klase TextFile upotrebite "\\W +"). Rezultate sačuvajte u obliku XML datoteke.
Preferences
Interfejs za program iranje aplikacija (API) Preferences m nogo je bliži konceptu trajnosti
nego serijalizovanju objekata, zato što inform acije autom atski skladišti i vadi iz skladišta.
M eđutim , m ože se prim enjivati sam o na ođređene male skupove podataka - na proste
tipove i znakovne nizove (objekte tipa S tring), a svaki uskladišten znakovni niz ne sme
biti duži od 8 K (nije baš da je sićušno, ali nije za ozbiljne poslove). Kao što mu ime go-
vori, API Preferences služi za skladištenje i vadenje param etara koje je izabrao korisnik i
postavki konfiguracije program a.
Poželjni param etri i postavke sm eštaju se u skupove ključ - vrednost (kao m ape), us-
kladištene u hijerarhiji čvorova. lako se u hijerarhiji čvorova mogu napraviti složene
strukture, obično se pravi sam o jedan čvor nazvan po klasi o kojoj se inform acije skladište
u njem u. Evo jednostavnog prim era:
//: ui/PrimerZaPreferences.java
import java.util.prefs.*;
import static net.mi nd vie w .u t i 1 .P r i n t .*;
U n orm aln om slučaju treba da zadate razum nu podrazum evanu vrednost. Zapravo,
ovo je jedan od tipičnih idioma:
Na taj način, U sageC ount će biti nula kada program pokrenete prvi put, ali će u kas-
nijim pokretanjim a biti različit od nule.
Kada pokrenete PrimerZaPreferences.java, videćete da se UsageCount zaista poveća-
va za jedan svaki put kada pokrenete program . Ali gde se taj broj čuva? Nema lokalne da-
toteke koja bi se pojavila nakon prvog pokretanja program a. API Preferences za
obavljanje tog svog posla koristi odgovarajuće sistemske resurse, a oni se m enjaju u zavis-
nosti od operativnog sistema. U Windovvsu se za to upotrebljava registar (pošto je on io-
nako hijerarhija čvorova s parovim a ključ - vrednost). Poenta je sledeća: inform acije se
autom atski skladište, a mi ne m oram o da brinem o o tom e kako se to obavlja u bilo kojem
pojedinačnom sistemu.
804 Misliti na Javi
API Preferences um e više nego što je ovde prikazano. Dalje pojedinosti o tom e po-
tražite u dokum entaciji razvojnog paketa JDK, koja je prilično razumljiva.
Vežba 33: (2) Napišite program koji prikazuje tekuću vrednost direktorijum a nazvanog
„osnovni direktorijum “ i traži od vas njegovu novu vrednost. Za skladištenje te vrednosti
upotrebite API Preferences.
Sažetak
Javina biblioteka U/I tokova zadovoljava osnovne zahteve: om ogućuje čitanje sa konzole i
upisivanje u nju, u datoteke, u m em orijske blokove, čak i preko Interneta. Pom oću nasle-
đivanja možete da napravite nove vrste ulaznih i izlaznih objekata. Možete čak i da izmc-
nite način na koji tokovi rade sa objektim a tako što ćete redefinisati m etodu to S trin g ()
koja se automatski poziva kada objekat prosledite m etodi čiji je argum ent tipa String (Ja-
vina ograničena autom atska konverzija tipa).
U dokum entaciji i projektu biblioteke U /I tokova, neka pitanja su ostala nedorečena.
Na prim er, bilo bi lepo kada biste mogli da generišete izuzetak pri pokušaju pisanja preko
postojeće datoteke tokom njenog otvaranja; neki program erski sistemi om ogućuju da za-
date otvaranje izlazne datoteke, ali sam o ako ona već ne postoji. Izgleda da u Javi treba ko-
ristiti objekat klase File kako bi se odredilo da li datoteka postoji, zato što će se ta datoteka
uvek menjati ako je otvarate kao FileOutputStream ili FileVVriter.
Biblioteka U/I tokova izaziva oprečna osećanja; ipak, radi dosta toga i prenosiva je.
Ukoliko ne razum ete projektni obrazac Decorator, njen projekat nije intuitivan pa je po-
treban dodatni napor da se ona objasni i razum e. Istovrem eno, biblioteka nije zaokruže-
na: na prim er, ne bi trebalo da ja m oram da pišem uslužne klase kao što je TextFile (nova
(SE5) Javina klasa PrintVVriter predstavlja korak u pravom sm eru, ali to je tek deliinično
rešenje). Java SE5 je donela veliko poboljšanje: najzad je dodato form atiranje izlaza kakvo
oduvek podržavaju gotovo svi ostali jezici.
Kada shvatite dekoratorski projektni obrazac i počnete da upotrebljavate ovu bibliote-
ku tam o gde je potrebna njena prilagodljivost, uvidećete da je korisna i tada vam nekoliko
dodatnih redova koda više neće predstavljati problem .
R ešenja o d a b ra n ih vežbi d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in Java A naotated Soln-
tion Guide, koji se m o ž e k u p iti na W eb lokaciji w ww.M iiuiView .net.
Nabrojani tipovi
R ezervisana re č e n u m služi zapravljenje novog tipa od ograničenogskupa itnenovanih vred-
nosti; zhog tije se te vrednosti tretiraju kao p u tiopravne kotnponenteprogratna. Ispostavlja se
d a je to veom a ko risn o .1
//: nabrojani/KlasaEnum.java
// Mogućnosti klase Enum
import static ne t, mindview.uti1 .Print.*;
}
} /* Ispis:
MORE rednibroj: 0
-1 false false
class Odmor
MORE
PLANINA rednibroj: 1
0 true true
class Odmor
PLANINA
BANJA rednibroj: 2
1 false false
class Odmor
BANJA
BANJA
PLANINA
MORE
* ///:-
M etoda o rd in al( ) proizvodi ceo broj koji pokazuje redosleđ deklarisanja svake enum
instance, počev od nule. enum instance uvek možete bezbedno porediti operatorom ==, i
metode equals( ) i hashC ode( ) bivaju autom atski napravljene. Klasa Enuni realizuje inter-
fejs Comparable, stoga ima svoju m etodu com pareTo( ), a realizuje i interfejs Serializable.
Pozivom m etode getD eclaringClass( ) za enum instancu, dobićete obuhvatajuću
enum klasu.
M etoda n am e( ) daje ime tačno onako kako je bilo deklarisano, a isto to dobijate i me-
todom to S trin g ( ). valueO f( ) je statični član klase Enum i proizvodi enum instancu koja
odgovara znakovnom nizu koji joj je prosleđen ili baca izuzetak ukoliko takav niz ne
može da pronađe.
//: nabrojani/LJutina.java
package nabrojani;
LJutina degree;
public P1jeskavica(LJutina stepen) { this.stepen = stepen;}
public String toString() { return "Pljeskavica je "+ stepen;}
public static void main(String[] args) {
System.out.println(new P1 je sk av ic a( NE ));
System.out.println(new Pljeskavica(SREDNJE)) ;
System.out.println(new P1 je sk av ic a( MN OG O) );
}
} /* Ispis:
Pljeskavica je NE
Pljeskavica je SREDNJE
Pljeskavica je MNOGO
* ///:-
static im port uvodi sve identifikatore enum instanci u lokalni prostor im ena, pa oni
ne m oraju biti po tp un i (kvalifikovani). Da li je to dobro ili je bolje biti izričit i navesti
potpu na im ena svih enum instanci? To verovatno zavisi od složenosti koda. Prevodilac si-
gurno neće dopustiti da se upotrebi pogrešan tip, pa sam o treba da se postarate da kod
bude razum ljiv čitaocu. U m nogim situacijam a će i n epotpuna im ena biti dovoljno razu-
mljiva, ali se to može prosuditi samo od slučaja do slučaja.
Vodite računa o tom e da ovu tehniku nije m oguće koristiti ako je enum definisan u
istoj datoteci ili podrazum evanom paketu. (Kao da je u kom paniji Sun bilo različitih
mišljenja o tom e treba li to dozvoliti.)
//: na br ojani/VesticalzOza.java
// Veštice iz Oza.
import static net.mind vi ew .u ti l.Print.*;
this.opis = opis;
}
public String getDescription() { return opis; }
public static void main(String[] args) {
for(VesticaIzOza vestica : V e s t i c a l z O z a . v a l u e s O )
print(vestica + 11 + vestic a. ge tD es cr ip tio n( ));
}
} /* Ispis:
WEST: Gospođica Galč, poznata i kao zla veštica sa zapada
NORTH: Glinda, dobra veštica sa severa
EAST: Zla veštica sa istoka, nosilac crvenih papučica, koju je smrvila Doro-
tina kuča
SOUTH: Verovatno dobra, ali je nema
* ///:-
Ukoliko nam eravate da definišete m etode, sekvencu enum instanci m orate završiti
znakom tačka i zarez. Takođe, Java nas prisiljava da u nabrojanom tipu (enum ) najpre
definišemo instance. Izazvaćete grešku u vrem e prevođenja ako pokušate da ih definišete
nakon neke od m etoda ili polja.
K onstruktor i m etode im aju isti oblik kao u uobičajenim klasama, zato što ovo i jeste
obična klasa uz nekoliko ograničenja. Dakle, s nabrajanjim a m ožete raditi gotovo sve što
poželite (iako ćete se verovatno tru diti da ostanu prilično jednostavna).
M ada je u prethodnom prim eru konstruktor privatan, ne bi bilo velike razlike i da ste
prim enili neki drugi pristup - ko n struktor se može upotrebiti samo za pravljenje enuin
instanci koje ste deklarisali u n u tar definicije nabrojanog tipa; prevodilac neće dozvoliti da
ga upotrebite za pravljenje novih instanci nakon završetka te definicije.
//: nabrojani/SvemirskiBrod.java
public enum SvemirskiBrod {
IZVIDJAC, T E R E T N I , TRANSPORTNI, KRSTARICA, BOJNIBROD, M A T I C N I ;
public String toString() {
String id = n a m e ( ) ;
String mala = i d .s u b s t r i n g ( 1 ).t o Lo we rC as e( );
return id.charAt(O) + mala;
}
public static void main(String[] args) {
fo r(SvemirskiBrod s : v a l u e s O ) {
System.out.pri ntln (s);
}
}
} /* Ispis:
Izvi djac
Teretni
Poglavlje 19: Nabrojani tipovi 809
Transportri
Krstarica
Bojnibrod
Maticni
* ///:-
M etoda to S trin g ( ) pribavlja ime klase SvemirskiBrod pozivanjem m etode n a m e ( ), a
njen rezultat m enja tako da je sam o prvo slovo veliko.
//: nabrojani/Semafor.java
// Nabrojani tipovi u naredbama switch.
import static n e t. mi nd vi ew .u ti l.Print.*;
} /* Ispis:
Na semaforu je CRVENO
Na semaforu je ZELENO
Na semaforu je ZUTO
Na semaforu je CRVENO
Na semaforu je ZELENO
Na semaforu je ZUTO
Na semaforu je CRVENO
* ///:-
Prevodilac se ne žali da unu tar naredbe switch nem a naredbe default, ali ne zato što je
uočio da za svaku instancu klase Signal im ate naredbe case. Neće se žaliti ni ako jednu od
naredaba case pretvorite u kom entar. Znači, sam i m orate paziti na to da definišete sve mo-
guće slučajeve. S druge strane, ukoliko iz naredbe case pozivate return, prevodilac će se ža-
liti ako nem ate default - čak i ako ste definisali sve moguće vrednosti tog nabrojanog tipa.
Vežba 1: (2) Izmenite Semafor.java pom oću uvoza statičnih članova tako da ne m orate
navoditi p otpuna imena enum instanci.
//: nabrojani/Refleksija.java
// Analiza nabrojanih tipova pomoću refleksije.
import java.lang.reflect.*;
import ja v a . u t i l .*;
import net.mindview.util.*;
import static ne t. mi nd vi ew .u ti l.Print.*;
Dakle, odgovor je da je v alu es( ) statična m etoda koju je đodao prevodilac. I m etoda
valu eO f( ) je dodata klasi Istrazi dok se pravio enuin. To pom alo zbunjuje, zato što
postoji i m etoda valueO f( ) koja je deo klase Enum, ali ona ima dva argum enta, a dodata
m etoda sam o jedan. M etoda interfejsa Set ovde je upotrebljena samo za pronalaženje
im ena m etoda, ne i njihovih potpisa, pa posle poziva Istrazi.removeAll(Enum) preostaje
sam o niz [values].
Iz rezultata vidite da je prevodilac klasu Istrazi označio sa final, pa od nje ne možete
izvoditi potklase. Tu je i statična ođređba za inicijalizaciju koja se može redefinisati
(pokazaču to kasnije).
812 Misliti na Javi
Zbog brisanja (opisanog u poglavlju Generički tipovi), dekom pajler nem a p u n u infor-
m aciju o nabrojanom tipu (Enurn), pa prikazuje da je natklasa od Istrazi sirovi Enum,
um esto stvarne Enum <Istrazi>.
Pošto je valu es( ) statična m etoda koju je u definiciju enum u m etn u o prevodilac, ako
neki od enum tipova svedete naviše na Enum, m etoda values( ) neće biti dostupna.
M edutim , im ajte u vidu da u klasi Class postoji m etoda getE num C onstants( ), pa čak i
ako m etoda v alu es( ) nije deo interfejsa od Enum, enum instance ipak m ožete dobiti po-
m oću Class objekta:
//: nabrojani/SvodjenjeEnumaNavise.java
/ / Kada se nabrojan tip svede naviše,
// metoda va1ues() postaje nedostupna
Pošto m etoda getE num C onstants( ) pripada klasi Class, možete je pozvati i za klase
koje nem aju nabrojane tipove:
//: nabrojani/NijeEnum.java
M etoda vraća null, pa ćete dobiti izuzetak kada pokušate da upotrebite njen rezultat.
Poglavlje 19: Nabrojani tipovi 813
Realizuje, ne nasleđuje
Rckao sam da su svi nabrojani tipovi izvedeni iz klase java.lang.Enum . Pošto Java ne
podržava višestruko nasleđivanje, to znači da nabrojan tip ne m ožete napraviti preko na-
sleđivanja:
M eđutim , moguće je napraviti nabrojan tip koji realizuje jedan ili više interfejsa:
//: nabrojani/crtaci/EnumRealizacija.java
// Nabrojani tip može da realizuje interfejs
package na br oj an i. cr ta ci;
import ja v a . u t i l .*;
import net.mindview.util.*;
enum LiklzCrtaca
implements Generator<LikIzCrtaca> {
FUCA, GRDA, TIBA, BLESA, SKOKA, LUDA, BOBA;
private Random slucajan = new Random(47);
public LiklzCrtaca next() {
return v a l u e s ()[slucajan.nextlnt(values().1e n g t h ) ] ;
}
Rezultat je malo neobičan, pošto m orate im ati instancu nabrojanog tipa da biste za nju
mogli pozvati m etodu. M edutim , L iklzC rtaca sada m ože da prihvati svaka m etoda koja
prim a G enerator, na prim er, m etoda p r in tN e x t().
Vežba 2: (2) Umesto da reali/.ujete interfejs, napravite n e x t( ) statičnom m etodom . Koje
su prednosti i m ane tog pristupa?
814 Misliti na Javi
Nasumičan izbor
U m nogim prim erim a u ovom poglavlju neophodan je nasum ičan izbor enum instanci,
kao što ste videli u m etodi LikIzCrtaca.next( ). To m ožem o uopštiti pom oću generičkih
tipova i rezultat sm estiti u zajedničku biblioteku:
/ / : n e t/ mi nd vi ew /u ti l/ Nab ro ja ni Ti po vi.java
package net.mindview.util;
import java.util
Prilično neobična sintaksa <T extends E n u m < T » opisuje T kao instancu nekog
nabrojanog tipa. O bjekat te klase činim o dostupnim tako što prosleđujem o Class<T>, i
zato se m ože napraviti niz enum instanci. Preklopljena m etoda ra n d o m ( ) treba da zna
sam o to da dobija T[], pošto ne m ora da obavlja Enum operacije; ona sam o nasum ično
bira neki elem ent niza. Povratni tip je tačno taj nabrojan tip.
Evo iednostavne provere m etode ra n d o m ( ):
//: na br ojani/ProveraMetodeRandom.java
import net.mindview.util.*;
//: nabrojani/jelovnik/Hrana.java
// Potkategorizacija nabrojanih tipova unutar interfejsa.
package na b r o j a n i .jelovnik;
/ / : nabrojani/jelovnik/VrstaHrane.java
package nabrojani.jelovnik;
import static n a b r oj an i.jelovnik.Hrana.*;
Svođenje naviše na klasu H rana funkcioniše za svaki enum tip koji realizuje interfejs
H rana, pa su svi oni tipovi klase H rana.
M eđutim , za rad sa skupom tipova interfejs nije toliko upotrebljiv kao enum. Ukoliko
želite da im ate „nabrojan tip drugih nabrojanih tipova“, m ožete napraviti obuhvatajudi
enum s po jedn om instancom za svaki enum u klasi Hrana:
//: na br oj ani/jelovnik/VrstaJela.java
package nabrojani.jelovnik;
import net.mindview.util.*;
Svaki od gorenavedenih nabrojanih tipova prim a odgovarajući objekat tipa Class kao
argum ent konstruktora od kojega m etodom g e tE n u m -C o n sta n ts() može da izvuče i
uskladišti sve en u m instance. Te instance kasnije koristi m etoda ra n d o m S e le c tio n (), pa
sada m ožem o da napravim o nasum ično generisan obrok tako što ćem o izabrati po jednu
stavku klase H ra n a iz svakog tipa V rstajela:
SUPA
Poglavlje 19: Nabrojani tipovi 817
SARMA
VOCE
CAJ
SALATA
PLJESKAVICA
VOCE
CAJ
SALATA
PLJESKAVICA
KARAMEL_KREM
KAFA_S_MLEKOM
SUPA
PLJESKAVICA
TIRAMISU
ESPRESSO
* ///:-
U ovom slučaju, svrha pravljenja nabrojanog tipa nabrojanih tipova jeste iteracija kroz
sve tipove V rstajela. Kasnije, u prim eru A utom atZ aM aloprodaju.java, videćete drugačiji
pristup kategorizaciji određen drugačijim ograničenjim a.
Drugačiji, kom paktniji pristup problem u kategorizacije jeste ugneždivanje nabrojanih
tipova u n u ta r drugih nabrojanih tipova, što se radi ovako:
//: nabrojani/KategorijeVrednosnihPapira.java
// Sažetija potkategorizacija nabrojanih tipova.
import net.mindview.util.*;
enum KategorijeVrednosnihPapira {
DEONICE(Vrednosni Papi ri.Deoni ca . c l a s s ) , OB VEZNICE(Vrednosni-
P a p i r i .O bv ez ni ca .c la ss );
V r e d n o s n i P a p i r i [] vrednosti;
KategorijeVrednosnihPapira(Class<? extends Vred no sn iPapiri> vrsta) {
vrednosti = vr st a. ge tE nu mC on st ant s( );
I
interface Vred no sn iP ap iri {
enum Deonica implements VrednosniPapiri { KRATKOROCNE, DUGOROCNE,
JEMSTVENE }
en u m Obveznica implements VrednosniPapiri { GRADSKE, RAZNE }
}
public VrednosniPapiri randomSelection() {
return Nabrojani Ti p o v i .ra nd om(vrednosti) ;
}
public static void m a i n ( S t r i n g [] args) {
for(int i = 0; i < 10; i++) {
KategorijeVrednosnihPapira kategorija =
Nabrojani Ti p o v i .random(KategorijeVrednosni hPapi ra.cla s s ) ;
Sy st em.out.println(kategorija + " +
818 Misliti na Javi
kategorija.randomSelectionO);
}
}
} /* Ispis:
OBVEZNICE: GRADSKE
OBVEZNICE: GRADSKE
DEONICE: JEMSTVENE
DEONICE: JEMSTVENE
OBVEZNICE: RAZNE
DEONICE: KRATKOROCNE
DEONICE: DUGOROCNE
OBVEZNICE: GRADSKEDEONICE: DUGOROCNE
OBVEZNICE: RAZNE
* ///:-
//: nabrojani/jelovnik/0brok2.java
package nabrojani.jelovnik;
import net.mindview.util.*;
Na kraju sm o dobili praktično isti kod, ali reorganizovan. U nekim slučajevima, tim e
se dobija jasnija struktura.
Vežba 3: (1) Dodajte nov tip Vrstajela u Vrstajela.java i pokažite da to radi u program u
Obrok.java.
Vežba4: (1) Ponovite p rethodnu vežbu za Obrok2.java.
Vežba 5: (4) Izmenite program kontroIa/SamoglasnicilSuglasnici.java (iz četvrtog pogla-
vlja) tako da koristi tri nabrojana tipa: SAMOGLASNIK, PONEKAD SAMOGLASNIK i
SUGLASNIK. K onstruktor nabrojanog tipa treba da prim a sva slova koja čine odgovara-
juću kategoriju. Uputstvo: upotrebite argum ente promenljive dužine i ne zaboravite da oni
autom atski prave niz.
Vežba 6: (3) Daje li ugnežđivanje tipova Predjelo, Glavnojelo, Desert i Kafa unutar in-
terfejsa H rana ikakve prednosti nad slučajem kada bism o ih deklarisali kao samostalne
nabrojane tipove koji slučajno svi realizuju interfejs Hrana?
Elementi skupa EnumSet m oraju poticati iz istog nabrojanog tipa. U n arednom pri-
m eru im am o enum svih m esta u zgradi na kojim a je ugrađen alarm ni senzor:
//: nabrojani/AlarmneTacke.java
package n a b r oj an i;
public enum AlarmneTacke {
STEPENISTEl, STEPENISTE2, HODNIK, K A N C EL AR IJ Al, KANCELARIJA2,
KANCELARIJA3, KANCELARIJA4, KUPATILO, SALA, KUHINJA
} ///= -
//: nabrojani/SkupEnumSet.java
// Operacije na skupu tipa EnumSet
package n a b r oj an i;
import j a va .u ti1
import static nabrojani.A1armneTacke.*;
import static net.mind vi ew .u ti l.Print.*;
već i jed n a m etoda o f ( ) sa argum entim a prom enljive dužine, ali bi to bilo nešto m anje
efikasno nego kada su argum enti zadati eksplicitno. Dakle, ako m eto d u o f ( ) pozovete sa
od dva o d pet argum enata, dobićete eksplicitne (nešto brže) m etode, ali ukoliko je pozo-
vete s jednim ili s više od pet argum enata, dobićete vararg verziju od o f ( ). O bratite pažnju
na sledeće: ukoliko je pozovete s jednim argum entom , prevodilac neće praviti vararg niz,
pa pozivanje te verzije s jednim argum entom neće povećati režijske troškove.
EnumSet se pravi povrh podupirućeg broja tipa long, long im a 64 bita, a svaka enum
instanca zahteva jedan bit za inform aciju uključeno/isključeno ili postojanje/neposto-
janje. To znači da EnumSet za jedan enum od najviše 64 elem enta troši sam o jedan broj
tipa long. Šta se dešava ako u nabrojanom tipu im ate više od 64 elementa?
//: nabrojani/VelikiEnumSet.java
import java.util
//: nabrojani/MapeEnumMap.java
// Osnove rada s mapama EnumMap.
package n a b r oj an i;
import java.util
import static nabrojani.AlarmneTacke.*;
import static net.mi nd vi ew .u ti l.Print.*;
Kao u skupu Enum Set, redosleđ elem enata u m api E num M ap određen je redosledom
njihovog definisanja u odgovarajućem nabrojanom tipu.
Poslednji deo m etode m a in ( ) pokazuje da u m api za svaki nabrojani tip uvek postoji
stavka ključ, ali je njena vrednost n u ll ukoliko za taj ključ niste pozvali p u t ( ).
Jedna od prednosti m ape E num M ap nad m etodnm a koje sc m enjaju u znvisnosti od
konstante (nabrojanog tipa) jeste to što je u E num M ap dozvoljeno m enjanje objekata
vrednost, dok m etode koje se m enjaju u zavisnosti od konstante bivaju fiksirane u vreme
prevođenja.
Poglavlje 19: Nabrojani tipovi 823
Kao što ćete videti u nastavku poglavlja, m apam a EnumMap m ožemo obavljati višekrat-
no otkrivanje tipa u situacijama gde više nabrojanih tipova ulazi u m eđusobnu interakciju.
//: nabrojani/MetodeKojeSeMenjajuUZavisnostiOdKonstante.java
import java.util
import java.text.*;
//: nabrojani/NisuKlase.java
// {Pokretanje: javap -c KaoKlase}
import static net.mindview.util .Print.*;
enum KaoKlase {
NAMIGNUTI { void ponasanje() { pr i n t ( " P o n a s a n j e l " ) ; } },
TREPNUTI { void ponasanje() { pr in t( " P o n a s a n j e 2 " ) ; } },
KLIMNUTI { void p o n a s a n j e O { pr i n t ( " P o n a s a n j e 3 " ) ; } };
abstract void ponasanje();
}
* ///:-
//: nabrojani/RedefinisanjeMetUZavOdK.java
import static net.mind vi ew .u ti l.Print.*;
napravim o više ne-DA instanci, pa će vam definicije nabrojanih tipova spočetka izgledati
malo čudno.
U klasi Posiljka videćete m etodu nasum icnaPosiljka( ) koja pravi nasum ične prim er-
ke prob nih pošiljki. M etoda g en erato r( ) proizvodi objekat koji realizuje interfejs Itera-
ble i m etodom nasum icnaPosiIjka( ) proizvodi više pošiljki, po jednu za svaki poziv
m etode n e x t( ) putem iteratora. Takva konstrukcija om ogućuje jednostavno pravljenje
foreach petlje pozivom m etode P osiljka.generator( ):
//: nabrojani/Posta.java
// Modelovanje pošte.
import j a va .u ti1
import net.mindview.util.*;
import static n e t . mi nd vi ew .u ti l.Print.*;
class Posiljka (
Tipovi NE su tu da bi se smanjila verovatnoća nasumićnog izbora:
enum OpstaDostava {DA,NE1,NE2,NE3,NE4,NE5}
enum MozeSeSkenirati {N E M O ZE SE SK EN IR AT I,D A 1,D A 2 ,D A 3 ,D A 4 }
enum Citljivo {NECITLJIV0,DA1,DA2,DA3,DA4}
en um Adresa {NETACNA,0K1,0K2,0K3,0K4,0K5,0K6}
enum PovratnaAdresa {NEDOSTAJE,OK1,OK2,OK3,OK4,OK5}
OpstaDostava opstaDostava;
Moze Se Sk en irati moze Se Sk en ir a t i ;
Citljivo citljivo;
Adresa adresa;
PovratnaAdresa povratnaAdresa;
static long brojac = 0;
long id = brojac++;
public String toString() { return "Pošiljka " + id; }
public String details() {
return t o S t r i n g O +
", Opšta dostava: " + opstaDostava +
", Adresa se moze skenirati: " + MozeSeSkenirati +
", Čitljivost adrese: " + Citljivo +
", Adresa Adresa: 11 + adresa +
", Povratna adresa: " + povratnaAdresa;
}
// Generisanje probne Pošiljke:
public static Posiljka nasumicnaPosiljka() {
Posiljka p = new Posiljka();
p.opstaDostava= Na br oj an iT ip ov i.rand om (O ps ta Do st av a.c la ss );
p.MozeSeSkeni rati = N a b r o j an iT ip ov i.ra nd om (M oz eS eS ke ni rat i. cl as s);
p.Citljivo = Na br oj an iT ip ov i.ra nd om(Citljiv o . c l a s s ) ;
p.adresa = Nabr oj an iT ip ov i. ra ndo m( Ad re sa .c la ss );
p.povratnaAdresa = NabrojaniTi p o v i .r a nd om (P ov ra tn aA dr esa .c la ss );
return p;
}
public static Iterable<Posiljka> generator(final int broj) {
return new Iterable<Posi1jka>() {
int n = b r o j ;
public Iterator<Posi1jka> iterator() {
828 Misliti na Javi
},
VIZUELNIPREGLED {
boolean ob ra da (Posiljka p) {
switch(p.Citljivo) {
case NECITLJIVO: return false;
default:
switch(p.adresa) {
case NETACNA: return false;
default:
print(p + " : isporuči obično");
return true;
}
}
Poglavlje 19: Nabrojani tipovi 829
}
},
VRATI_POSILJAOCU {
boolean obrada{Posiljka p) {
switch(p.povratnaAdresa) {
case NEDOSTAJE: return false;
default:
print(p + " : vrati p o š i 1j a o c u " ) ;
return true;
}
}
};
abstract boolean obrada(Posiljka p);
}
static void o b ra da (P os i1jka p) {
for(PrZaObraduPoste przaobradu : PrZaObraduPoste.values())
i f(przaobradu.obrada(p))
return;
print(p + " se ne može uručiti");
}
public static void main(String[] args) {
for(Posiljka posiljka : Po si1jka.gene ra to r( lO )) {
pr in t( po si lj ka .d et ails( ) );
obrada(posilj k a ) ;
p r i n t C 1* * * * * " ) ;
}
}
} /* Ispis:
Pošiljka 0, Opšta dostava: NE2, Adresa se moze skenirati: N E MO ZE SE SK EN IR AT I,
Adresa čitljiva: DA3, Adresa Adresa: OKl, Povratna adresa: OKl
Pošiljka 0 : isporuči obično
*****
Pošiljka I, Opšta dostava: NE5, Adresa se moze skenirati: DA3, Adresa
čitljiva: NECITLJIVO, Adresa Adresa: 0K5, Povratna adresa: OKl
Pošiljka 1 : isporuči automatski
*****
* ///:-
Chain o f Responsibility je izražen u nabrojanom tipu P rZ aO b rad u P o ste, a redosled
en u m definicija određuje redosled isprobavanja strategija na svakoj pošiljci. Isprobava se
svaka strategija redom dok jedna ne uspe ili dok sve ne propadnu - u tom slučaiu pošiljka
se ne m ože uručiti.
Vežba 8: (6) Izm enite program Posta.java tako da pošiljke može da prosleđuje (s jedne na
drugu adresu).
Vežba 9: (5) Izm enite klasu P osta tako da koristi Enum M ap.
P rojekat:2 Na specijalizovanim jezicima kao što je Prolog ovakvi problem i se rešavaju
ulančavanjem u n a za d (engl. backw ard chaining). Uz Posta.java kao nadahnuče, proučite
takve jezike i napišite program koji om ogućava lako đođavanje novih „pravila" sistemu.
Projekti su predlozi koji se m ogu koristiti (recim o) za sem inarske radove. Vodič s rešenjim a ne sađrži
rešenja p rojekata.
Poglavlje 19: Nabrojani tlpovi 831
//: nabrojani/Ulaz.java
package nabrojani;
import j a v a . u t i l .*;
//: nabrojani/AutomatZaMaloprodaju.java
// { A r g u m e n t i : UlazUAutomatZaMaloprodaju.txt}
package n a b r o j a n i ;
import j a v a . u t i l .*;
import net.mindview.util.*;
import static n a b r o j a n i .U1az.*;
import static ne t. mi nd vi ew .u ti1 .P r i n t .*;
enum Kategorija {
NOVAC(PETOPARAC, DESETOPARAC, FRTA LJ, DINAR),
IZBOR_ARTIKLA(ZUBNAPASTA, CIPS, SOK, S A P U N ) ,
PREKINI TRANSAKCIJU(ODUSTANI OD TRANSA KC IJ E),
832 Misliti na Javi
U6ASI(STOP);
private Ulaz[] vr e d n o s t i ;
Kategorija(Ulaz... tipovi) { vrednosti = tipovi; }
private static En umHap<Ulaz,Kategorija> kategorije =
new EnumMap<Ul a z ,Kategorija>(Ul a z .cl a s s ) ;
static {
for(Kategorija c : Kategorija.class.getEnumConstants())
for(Ulaz type : c.vrednosti)
kategorije.put(type, c);
}
public static Kategorija kategorizuj(Ulaz ulaz) {
return ka te go rije.get(ulaz);
}
}
case UGASI:
stanje = TERMINAL;
default:
}
}
},
O B R A CU N( Tr aj an je St anj a.PROLAZNO) {
void next() {
print("uzmite kupljeno: " + izbor);
iznos -= izbor. iz no s( );
stanje = VRACANJEJOJSURA;
}
}.
VRACAN JE _K US UR A( Tr aja nj eS ta nj a.PROLAZNO) {
void next() {
if(iznos > 0) {
print("Vaš kusur: " + iznos);
iznos = 0;
}
stanje = MIRUJE;
}
},
TERMINAL { void output() { p r in t( "Z au st av lj en "); } };
private boolean stanjeProlazno = false;
Stanjef) {}
Stanje(TrajanjeStanja trans) { stanjeProlazno = true; }
void next(Ulaz ulaz) {
throw new RuntimeException("Pozivajte " +
"next(Ulaz ulaz) samo za neprolazna stanja");
}
void next() {
throw new Ru ntimeException("Metodu next() pozivajte za " +
"stanja Trajan je St an ja .P RO LAZ NO ");
}
void output() { p r i n t( iz no s); }
}
static void run(Generator<Ulaz> gen) {
while(stanje != Stanje.TERMINAL) {
st an je .n ex t( ge n. ne xt( ));
while(stanje.stanjeProlazno)
st an je .n ex t( );
st a n j e .o ut pu t( );
25
35
Vaš kusur: 35
0
25
35
Nedovoljno novca za S0K
35
60
70
75
Nedovoljno novca za S0K
75
Vaš kusur: 75
0
Zaustavljen
* ///:-
Pošto se enu m instanca najčešće bira pom oću naredbe svvitch (obratite pažnju na do-
datni trud uložen u to da se naredba sw itch može lako upotrebljavati s nabrojanim tipovi-
ma), jedno od najčešćih pitanja u vezi sa upotrebom nabrojanih tipova jeste: ,,Po kojem
Poglav[je 19: Nabrojani tipovi 835
osnovu da biram?“ U navedenom prim eru je najlakše krenuti unazad od klase AutomatZa-
Maloprodaju. U svakom stanju treba izabrati jednu od osnovnih kategorija ulaznog dej-
stva: prim anje novca, izbor artikla, odustajanje od transakcije, i isključenje mašine.
M eđutim , unutar tih kategorija imate različite novčane apoene koje kupac može ubaciti u
m ašinu i različite artikle koje može izabrati. Nabrojani tip Kategorija grupiše različite tipo-
ve ulaza tako da m etoda kategorizuj( ) može u nutar naredbe switch da proizvede odgova-
rajuću kategoriju. Ta m etoda delotvorno i bezbedno obavlja pretraživanje pom oću m ape
EnumMap.
Ako proučite klasu AutomatZaM aloprodaju, videćete da se sva stanja razlikuju i da
drugačije reaguju na ulaze. O bratite pažnju i na dva prelazna stanja; u m etodi r u n ( )
m ašina čeka na neki Ulaz i ne prestaje da m enja stanja dok ne izađe iz prelaznog stanja.
Autom atZaM aloprodaju m ožem o ispitati na dva načina, pom oću dva različita
objekta tipa Generator. Random lnputG enerator stalno proizvodi nove ulaze, sve sem
ulaza UGASI. Ukoliko dovoljno dugo izvršavate takav program , donekle ćete proveriti
ispravnost, tj. videćete da li je mašina sklona da odluta u loše stanje. FilelnputG enerator
prim a datoteku koja u tekstualnom obliku opisuje ulaze, pretvara ih u instance nabroja-
nog tipa i pravi objekte tipa Ulaz. Evo tekstualne datoteke koja proizvodi p rethodno
prikazane rezultate:
//:! nabrojani/lllazUAutomatZaMaloprodaju.txt
FRTALJ; FRTALJ; FRTALJ; CIPS;
DINAR; DINAR; ZUBNAPASTA;
F R T A L J ; DESETOPARAC; ODUSTANI_OD_TRANSAKCIJE;
F R T A L J ; DESETOPARAC; SOK;
F R T A L J ; DESETOPARAC; PETOPARAC; SOK;
ODUSTANI_OD_TRANSAKCIJE;
STOP;
///:-
Jedno od ograničenja ovog dizajna jeste to što polja klase Autom atZaM aloprodaju
kojima pristupaju instance nabrojanog tipa Stanje m oraju biti statična, što znači da
možete imati sam o jednu instancu klase AutomatZaM aloprodaju. To vas neće m nogo
zabrinuti ako pom islite na stvarnu (ugrađenu Java) realizaciju, pošto ćete verovatno imati
samo jednu aplikaciju po autom atu.
Vežba 10: (7) Izmenite (samo) klasu AutomatZaM aloprodaju koristeći EnumMap, tako
da program može imati više instanci klase AutomatZaM aloprodaju.
Vežba 11: (7) U pravom autom atu za m aloprodaju trebalo bi da se m ogu lako dodavati i
menjati vrste artikala koji se prodaju, pa su ograničenja koja nabrojan tip nam eće na Ulaz
nepraktična (pošto enum definiše konačan i neprom enljiv skup tipova). Izmenite pro-
gram AutomatZaM aloprodaju.java tako da artikle koji se prodaju predstavlja klasa um e-
sto što su deo Ulaza, i pom oću tekstualne datoteke inicijalizujte ArrayList tih objekata.
(U potrebite net.mindview.utiI.TextFile.)
Projekat: Projektujte m eđunarodni autom at za m aloprodaju (koristeći „intern ađ o n ali-
zaciju“ ), tako da se ista mašina može lako prilagoditi za različite zemlje.
836 Misliti na Javi
//: nabrojani/Ishod.java
package n a b r o j a n i ;
public enum Ishod { POBEDA, PORAZ, NERESENO } ///:-
//: n a b r oj an i/ Ro Sh am Bo l.java
// Primer višekratnog otkrivanja tipa.
package na b r o j a n i ;
import java.util
import static n a b r o j a n i .Ishod.*;
interface Stavka {
Ishod takmicenje(Stavka st);
Ishod izrc(Papir p ) ;
Ishod izrc(Makaze m ) ;
Ishod izrc(Kamen k ) ;
}
//: nabrojani/RoShamBo2.java
// Izbor konstante jednog nabrojanog tipa
// korišćenjem drugog u naredbi svvitch.
package n a b r oj an i;
import static na br oj an i.Ishod.*;
Nakon što m etoda takm icenje( ) otkrije oba tipa, jedino dejstvo je vraćanje rezul-
tujućeg objekta tipa Ishod. M eđutim , mogli biste pozvati i neku drugu m etodu, čak (na
prim er) preko objekta tipa C o m m a n d dodeljenom u konstruktoru.
RoShamBo2.java je m nogo manji i jednostavniji od prvobitnog prim era, te ga je lakše
i pratiti. Vodite raćuna o tom e da i dalje koristim o dva otkrivanja tipa da bism o utvrdili
tip oba objekta. U program u RoShamBol.java oba otkrivanja su obavljena preko
virtuelnih poziva m etoda, ali ovde je virtuelni poziv m etode upotrebljen sam o u prvom
otkrivanju tipa. U drugom otkrivanju tipa upotrebljen je switch, ali sve je bezbedno zato
što enum ograničava broj izbora u naredbi switch.
840 Misliti na Javi
Kod koji upravlja nabrojanim tipom tako je raščlanjen da ga m ožem o koristiti u drugim
prim erim a. Prvo, interfejs Takmicar definiše tip koji se takmiči s drugim Takmicarom:
/ / : n a b r o ja n i/ T a k m ic a r . ja v a
/ / I z b o r k o n s tan te je dnog nabrojanog t i p a
/ / k o riš ć e n je m drugog u naredbi s w it c h .
package n a b r o j a n i ;
Z atim definišemo dve statične m etode (statične zato da ne bism o m orali eksplicitno
zadati param etarski tip). Prvo m a tc h ( ) poziva takm icenje( ) za jedan objekat tipa Tak-
m icar protiv drugog, pa vidite kako je u ovom slučaju dovoljno da param etarski tip bude
Takmicar<T>. Ali u m etodi ig ra j( ), param etarski tip m ora biti Enum<T> jer se koristi
u m etodi N abrojaniT ipovi.random ( ), i Takmicar<T>, zato što se prosleđuje m etodi
m a tc h ():
//: nabrojani/RoShamBo.java
// Zajedničke alatke za RoShamBo primere.
package n a b r oj an i;
import net.mindview.util.*;
one savršeno rešenje za višekratno otkrivanje tipa. Ali iako n a taj način m ogu dobiti
drugačija ponašanja, enum instance nisu tipovi, pa ih ne m ožete koristiti kao argum ente
tipova u potpisim a metoda. U ovom prim eru upotrebljavate naredbu svvitch, i to je naj-
više što se m ože uraditi:
/ / : nabrojani/Ro ShamBo3.java
/ / K o r iš ć e n je metoda k o je se menjaju u z a v i s n o s t i od k o n s ta n te
/ / (nabroja nog t i p a ) .
package n a b r o j a n i ;
im p o r t s t a t i c n a b r o j a n i . I s h o d . * ;
Iako ovo radi, a nije ni glupo, rešenje u program u RoSham Bo2.java kao da zahteva
m anje koda za dodavanje novog tipa, te izgleda jednostavnije.
M eđutim , RoSham Bo3.java m ožem o pojednostaviti i sažeti:
/ / : nabrojani/RoShamBo4.java
package n a b r o j a n i ;
Ovde se đrugo otkrivanje tipa obavlja verzijom m etode takm icenje( ) s dva argum en-
ta; m etoda obavlja niz poređenja i stoga radi slično naredbi switch. Program je manji, ali
i m anje jasan. U velikom sistem u ta nejasnost može imati strašne posledice.
//: na brojani/RoShamBo5.java
// Višekratno otkrivanje tipa pomoću mape EnumMap
// čiji su članovi drugi objekti tipa EnumMap.
package nabrojani;
import j a v a . u t i l .*;
Poglavlje 19: Nabrojani tipovi 843
//: nabrojani/RoShamBo6.java
// Nabrojani tipovi i "tabele" umesto
// višekratnog otkrivanja tipa.
package nabrojani;
import static nabrojani.Ishod.*;
Sažetak
Iako nabrojani tipovi sami po sebi nisu previše složeni, ovo poglavlje sam pom erio u dru-
gi deo knjige zbog onoga što se m ože uraditi kom binacijom nabrojanih tipova i polim or-
fizma, generičkih tipova i refleksije.
M ada znatno sofisticiraniji nego u jezicim a C i C + + , nabrojani tipovi i dalje pred-
stavljaju ,,malu“ m ogućnost, nešto bez čega je jezik opstajao (pom alo nezgrapno) mnogo
godina. Ovo poglavlje pokazuje kako m ože biti važan doprinos ,,male“ m ogućnosti - kat-
kada nam daje baš onu polugu kojom problem m ožem o rešiti elegantno i jasno, a u celoj
knjizi sam isticao koliko je elegancija važna. Jasnoća m ože biti činilac koji određuje da li
je rešenje uspešno ili nije dobro zato što ostali ne m ogu da ga razum eju.
Što se tiče jasnoće, Java 1.0 je proizvela zbrku zbog korišćenja term ina ,,enum eration“
um esto uobičajenog i prihvaćenog term ina „iterator" za objekat koji bira svaki element
neke sekvence. U nekim jezicim a čak se i nabrojani tipovi nazivaju ,,enum erators“! Ta
greška je ispravljena u Javi, ali interfejs E n u m e ra tio n nisu smeli sam o tako da izbace, pa
se još viđa u starim (a ponekad i u novim !) program im a, biblioteci i dokum entaciji.
R ešenja o d a b r a n ih vežbi d a ta su u e le k tro n sk o m d o k u m e n tu Thc Thinking in Java Annotatcd Sola-
tion Guide, koji se m o že k u p iti n a lok aciji www.MindView.com.
Anotacije
A notacije (ili m etapodaci) fo rm a lizo va n su način dodavanja inform acija kodu, što olakšava
kasniju upotrebu tih p o d a ta k a .'
prave jezičke konstrukcije, im aju stru k tu ru , a njihovi tipovi se proveravaju u vrem e pre-
vođenja. Kod je elegantniji i lakše se održava ukoliko su sve inform acije zadržane u
izvornom kodu, a ne u kom entarim a. K orišćenjem i nasleđivanjem API-ja i alatki za
anotacije, Oi pom oću spoljnih biblioteka za rad s bajtkodom koje ćete upoznati u ovom
poglavlju, možete obaviti dalekosežnu proveru i o brad u izvornog koda i bajtkoda.
Osnovna sintaksa
U donjem prim eru, m etoda p ro b n o Iz v rse n je () im a anotaciju @Test. Sama po sebi ona
ne radi ništa, ali će prevodilac proveriti im ate li deftniciju anotacije @Test u putanji au-
tom atskog prevođenja i pakovanja (builda). Kao što ćete videti u nastavku poglavlja,
pom oću refleksije m ožete napraviti alatku koja će tu m etodu izvršavati.
//: anotacije/MozeSeTestirati.java
package anotacije;
import net.mindview.atunit.*;
Definisanje anotacije
Sledi definicija gornje anotacije. Videćete da definicije anotacija mogo liče na definicije
interfejsa. Zapravo, prevodilac od njih pravi datoteke klasa kao od svakog drugog Java in-
terfejsa:
OTarget(ElementType.METHOD)
@Retenti on(RetentionPol icy.RUNTIME)
public Ointerface Test {} ///:-
Sem simbola definicija oznake @Test liči na prazan interfejs. Definicija anotacije
zahteva i m etaanotacijc @Target i @ Retention. @Target definiše gde se anotacija može
prim eniti (recimo, ispred m etode ili polja). @ Retention definiše da li će anotacija biti
dostupna u izvornom kodu (SOURCE), u datotekam a klasa (CLASS) ili u vreme izvr-
šavanja (RUNTIME).
Poglavlje 20: Anotacije 847
Anotacije najčešće sadrže elcm enie za zadavanje vrednosti u anotacijama korisnika. Pro-
gram ili alatka m ogu upotrebljavati te param etre prilikom obrade anotacije korisnika. Ele-
m enti liče na m etode interfejsa, sem što im možete definisati podrazum evane vrednosti.
A notaciju bez elem enata, kao što je @Test, nazivam o m arker anotacija.
Sledi jednostavna anotacija koja prati slučajeve upotrebe u projektu. Program eri ano-
tiraju svaku m etodu ili skup m etoda koje zadovoljavaju zahteve određenog slučaja
upotrebe. Kad prebroji realizovane slučajeve upotrebe, rukovodilac projekta može steći
uvid u napredovanje projekta, a program eri koji održavaju projekat lako pronalaze sluča-
jeve upotrebe kada treba da ažuriraju poslovna pravila u sistem u ili da otkriju i otklone
greške u njima.
/ / : a n o t a c i j e / S l u c a j l l p o t r e b e . ja v a
im p o r t j a v a . l a n g . a n n o t a t i o n . * ;
@Target(ElementType.METHOD)
@ R e te n tio n ( R e te n tio n P o li c y . RUNTIME)
p u b l i c @ in te rfa c e S lu c a jU p o tre b e (
publ i c i n t i d ( ) ;
p u b l i c S t r i n g o p i s ( ) d e f a u l t "nema o p i s a " ;
} ///:-
/ / : a n o ta c ij e /U s lu z n e M e to d e Z a L o z in k e .ja v a
im p o r t j a v a . u t i 1 . * ;
p u b l i c c la s s UsluzneMetodeZaLozinke {
@ SlucajU potrebe(id = 47, o p is =
"L o z in k a mora s a d r ž a t i barem jednu b r o j k u " )
p u b l i c boolean p r o v e r i L o z i n k u ( S t r i n g l o z i n k a ) {
retu rn (lo z in k a .m a tc h e s ( " \\w * \\d \\w * " )) ;
}
@S1ucajUpotrebe(id = 48)
p u b lic S trin g s if r ir a jL o z in k u ( S t r in g lo z in k a ) {
r e t u r n new S t r i n g B u i 1d e r ( l o z i n k a ) . r e v e r s e ( ) - t o S t r i n g ( ) ;
}
@S1ucajUpotrebe(id = 49, o p is =
"Nova lo z in k a ne može b i t i je dnaka p re th o d n im ")
p u b l i c boolean p ro v e r a N e p o n a v lja n ja L o z in k e (
L i s t < S t r i n g > p re th o d n e Lo z in k e , S t r i n g l o z i n k a ) {
r e tu rn !p re th o d n e L o z in k e .c o n ta in s (lo z in k a );
}
} ///:-
848 Misliti na Javi
V rednosti elem enata anotacije izražene su kao parovi im e-vrednost u zagradam a iza
deklaracije anotacije @SlucajUpotrebe. Anotaciji m etode sifrirajLozinku( ) ovde nije
bila prosleđena vrednost za elem ent opis, pa će se podrazum evana vrednost definisana u
@interface SlucajUpotrebe pojaviti kada ta klasa prode kroz procesor anotacija.
Ovakav sistem bi m ogao ,,skicirati“ stru k tu ru vašeg sistema, s tim što bi m u trebalo
dodati još neke funkcije.
Metaanotacije
U jeziku Java tren u tn o postoje sam o tri (prethodno opisane) standardne anotacije i četiri
definisane m etaanotacije. Ovo su m etaanotacije za anotiranje anotacija:
Uglavnom ćete sami definisati svoje anotacije i pisati sopstvene procesore koji će ih
obrađivati.
/ / : a n o t a c ij e / P r a c e n je S lu c a je v a U p o t r e b e . ja v a
iraport j a v a . l a n g . r e f l e c t . * ;
im p o r t j a v a . u t i l . * ;
Poglavlje 20: Anotacije 849
p u b l i c c la s s P ra ce n je S lu c a je v a llp o tre b e {
p u b l i c s t a t i c v o iđ
p r a t i S l u c a j e v e U p o t r e b e ( L i s t < I n t e g e r > s l u c a je v iU p o t r e b e , Class<?> c l ) {
f o r(M e th o d m : c l.g e t D e c la r e d M e t h o d s ( ) ) {
Slu c a jU p o tre b e su = m . g e t A n n o t a t i o n ( S l u c a j U p o t r e b e . c l a s s ) ;
i f ( s u != n u l l ) {
S y s t e m . o u t . p r in t ln ( ''P r o n a đ e n s l u č a j u p o tr e b e : " + s u . i d ( ) +
" " + s u .o p is O );
s lu c a je v iU p o tre b e .re m o v e (n e w I n t e g e r ( s u . i d ( ) ) ) ;
}
}
f o r ( i n t i : s l u c a je v iU p o t r e b e ) {
S y s t e m . o u t . p r i n t l n ( " U p o z o r e n j e : n e d o s ta je s l u č a j u p o tr e b e - " + i ) ;
}
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
L i s t < I n t e g e r > s lu c a je v iU p o t r e b e = new A r r a y L i s t < I n t e g e r > ( ) ;
C o l l e c t i o n s . a d d A l l ( s l u c a j e v i U p o t r e b e , 47, 48, 49, 5 0 );
p ra t iS 1 u c a je v e U p o t re b e ( s l u c a je v iU p o t re b e , Usl uzneMetodeZa
L o z in k e .c la s s );
}
} / * Is p is :
Pronađen s l u č a j upo tre b e :47 Lozinka mora s a d r ž a t i barem jednu b r o jk u
Pronaden s l u č a j u p o tr e b e :4 8 nema opisa
Pronađen s l u č a j u p o tr e b e :4 9 Nova l o z i n k a ne može b i t i je dnaka p re thodnim
Upozore nje : n e d o sta je s l u č a j u p otrebe-5 0
* ///:-
Elementi anotacija
O znaka @SlucajUpotrebe definisana u program u SlucajUpotrebe.java sadrži int ele-
inent id i String element opis. Ovo su dozvoljeni tipovi elem enata anotacija:
• Svi prosti tipovi (int, float, boolean itd.)
• String
• Class
• N abrojani tipovi
• Anotacije
• Nizovi svih prethodnih tipova
850 Misliti na Javi
Prevodilac će prijaviti grešku ako pokušate da upotrebite neki đrugi tip. Vodite računa
0 tom e da ne smete koristiti om otačke klase, ali zbog autom atskog pakovanja to zapravo
1nije ograničenje. Dozvoljeni su i elem enti koji su sami anotacije. Kao što ćete videti malo
kasnije, ugneždene anotacije m ogu veom a dobro da posluže.
/ / : a n o t a c i j e / S i m u l a c i j a N u l 1 . ja v a
im p o rt j a v a . 1a n g .a n n o t a t io n
@Target(E1ementType.METHOD)
@Retenti o n ( R e t e n t io n P o li c y . RUNTIME)
p u b l i c @ in te rfa c e S i m u l a c i j a N u l l {
p u b lic i n t id ( ) d e fa u lt -1;
p u b l i c S t r i n g o p i s () d e f a u l t
) / / / =-
anotacija, sve te inform acije zadržali biste u izvornoj datoteci zrna Jave. Za to bi vam bile
potrebne anotacije koje definišu im e tabele baze podataka pridružene tom zrnu, kolone i
SQL tipove za preslikavanje svojstava zrna.
Sledi anotacija zrna koja procesoru anotacija kazuje da napravi tabelu baze podataka:
/ / : a n o t a c ije / b a z a p o d a t a k a / T a b e la B P . ja v a
package a n o t a c ij e . b a z a p o d a t a k a ;
im p o r t j a v a . l a n g . a n n o t a t i o n . * ;
Svaki ElementType koji specificirate u anotaciji @Target jeste ograničenje koje prevo-
diocu kazuje da se ta anotacija može prim eniti sam o na taj određeni tip. Za nabrojani tip
ElementType m ožete specificirati jednu vrednost ili listu proizvoljnih kom binacija vred-
nosti razdvojenih zarezima. Ako anotaciju želite da prim enite na svaki ElementType,
m ožete p o tp u n o da izostavite anotaciju @Target, mada to nije uobičajeno.
O bratite pažnju na to da @TabelaBP im a elem ent im e ( ), tako da anotacija može dati
ime tabele baze podataka koju će procesor napraviti.
Evo anotacija za polja ovog zrna Jave:
/ / : a n o ta c ij e / b a z a p o d a t a k a / O g r a n ic e n ja . ja v a
package a n o ta c ije . b a z a p o d a t a k a ;
im p o r t j a v a . l a n g . a n n o t a t i o n . * ;
@Target(ElementType.FIELD)
@ R e te n tio n(R e te n ti onPoli c y . RUNTIME)
p u b l i c @ in te rfa c e O g ra n ic e n ja {
boolean p rim a r n iK L J u c () d e f a u l t f a l s e ;
boolean d o z v o li N u l1 () d e f a u l t t r u e ;
boolean j e d i n s t v e n o ( ) d e f a u l t f a l s e ;
} ///:-
/ / : a n o ta c ij e / b a z a p o d a t a k a / S Q L S t r in g . ja v a
package a n o t a c i j e . bazapodataka;
im p o r t j a v a . l a n g . a n n o t a t i o n . * ;
@Target(ElementType.FIELD)
@Retenti o n ( R e t e n t i o nPoli c y . RUNTIME)
p u b l i c @ in te rfa c e SQLString {
i n t v r e d n o s t O d e f a u l t 0;
S t r i n g ime() d e f a u l t
O g ra n ic e n ja o g r a n i c e n j a ( ) d e f a u l t @0granicenja;
) ///:-
852 Misliti na Javi
/ / : a n o t a c ij e / b a z a p o d a t a k a / S Q L I n t e g e r . ja v a
package a n o t a c ije . b a z a p o d a t a k a ;
im p o r t j a v a . l a n g . a n n o t a t i o n . * ;
/ / : a n o ta c ije / b a z a p o d a t a k a / J e d i n s t v e n o s t . ja v a
/ / Primer ugnežđenih a n o t a c i j a
package a n o ta c ije . b a z a p o d a t a k a ;
p u b l i c @ in te rfa c e J e d in s t v e n o s t {
O g ra n ic e n ja o g ra n ic e n ja O
d e f a u l t @ O gra nic enja (je di n s t v e n o = t r u e ) ;
} ///:-
/ / : a n o ta c ij e / b a z a p o d a t a k a / C la n . ja v a
package a n o ta c ije . b a z a p o d a t a k a ;
@TabelaBP(ime = "CLAN")
p u b l i c c la s s Clan {
@SQLString(30) S t r i n g li c n o l m e ;
@SQLString(50) S t r i n g prezlm e;
@SQLInteger I n t e g e r s t a r o s t ;
@SQLString(value = 30,
Poglav|je 20: Anotacije 853
o g r a n ic e n ja = @ O g r a n ic e n ja ( p r im a r n iK lju c = t r u e ) )
S trin g id e n t if ik a to r ;
s t a t i c i n t b ro jC la n o v a ;
p u b lic S trin g d a j I d e n t i f i k a t o r ( ) { re tu rn i d e n t i f i k a t o r ; )
p u b l i c S t r i n g d a jL ic n o Im e ( ) { r e t u r n l i c n o l m e ; )
p u b l i c S t r i n g d a jP re z Im e () { r e t u r n pre z lm e ; }
p u b lic S trin g to S trin g O { re tu rn id e n ti f i ka to r; }
p u b lic In te ge r d a jS ta ro s t() { re tu rn s ta ro s t; }
} ///= -
A notaciji @TabelaBP klase data je vrednost CLAN koja će biti upotrebljena kao im e
tabele. Svojstva zrna licnolm e i prezlm e an o tirana su pom oću @SQLStringova i imaju
vrednosti 30 odnosno 50. Te anotacije su zanimljive iz dva razloga: prvo, koriste
podrazum evanu vrednost ugnežđene anotacije @Ogranicenja, i drugo, koriste kraći ob-
lik pisanja. Ako anotacija im a sam o jedan elem ent i vi m u date im e value, ne m orate da
pišete parove im e-vrednost; dovoljno je da specificirate sam o vrednost (engl. value) u za-
gradam a. To važi za sve dozvoljene tipove elem enata. N aravno, tada elem entu m orate dati
im e „vrednost", ali u gornjem slućaju ipak dobijate sem antički sm islenu specifikaciju
anotacije koja se lako čita:
@SQLStri n g (30)
Drugačija rešenja
U ovom slučaju im a i drugih načina da se naprave anotacije. Na primer, m ožem o napraviti
samo jednu klasu anotacije @KolotiaTabele u čijem enum elem entu definišemo vrednosti
kao što su STRING, INTEGER, FLOAT itd. O nda ne bism o m orali imati interfejs (@inter-
face) za svaki SQL tip, ali bism o izgubili m ogućnost da kvalifikujemo tipove pom oću do-
datnih elemenata kao što su velicina ili preciznost , pa je šteta verovatno veća od koristi.
Stvarni SQL tip m ožem o opisati String elem entom , npr. VARCHAR(30) ili INTEGER.
Time ne bism o izgubili m ogućnost da kvalifikujem o tipove, ali bi preslikavanje }ava tipa
u SQL, tip bilo zaključano u kodu, a to nije dobro. Ne želimo da zbog prom ene baze
podataka m oram o ponovo da prevodim o klase; bilo bi elegantnije saopštiti procesoru
anotacija da koristim o drugu „varijantu" SQL-a i prepustiti njem u da tokom obrade
anotacija sam o tom e vodi brigu.
854 Misliti na Javi
Treće rešenje bilo bi korišćenje dva tipa anotacija, @ Ogranicenja i relevantnog SQL
tipa (recimo, @SQLInteger) za anotiranje željenog polja. To jeste malo zapetljano, ali
prevodilac dozvoljava proizvoljan broj različitih anotacija za svako odredište (engl. tar-
get). Kada koristite više anotacija, istu anotaciju ne sm ete u potrebiti dvaput.
Realizacija procesora
Sledi prim er procesora anotacija koji učitava datoteku klase, traži u njoj anotacije baze
podataka i generiše SQL kom andu za pravljenje takve baze podataka:
/ / : a n o ta c ije / b a z a p o d a t a k a / T v o r a c T a b e le . ja v a
/ / R e f l e k t i v n i p ro ce so r a n o t a c i j a .
/ / {Arg um e n ti: a n o ta c ije . b a z a p o d a t a k a . C la n }
package a n o t a c ije . b a z a p o d a t a k a ;
im p ort j a v a . l a n g . a n n o t a t i o n . * ;
im p o rt j a v a . 1a n g . r e f l e c t . * ;
im p o rt j a v a . u t i l . * ;
p u b l i c c la s s TvoracTabele {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throvvs E xc e p tio n {
i f ( a r g s . 1ength < 1) {
S y s te m .o u t.p r in tln ( " a rg u m e n ti: a n o tirane k la s e " );
System.exi t ( 0 ) ;
}
f o r ( S t r i n g imeKlase : a rg s) {
Class<?> cl = C la s s .fo rN a m e (im e K la s e );
TabelaBP tabelaBP = c l . g e t A n n o t a t i o n ( T a b e l a B P . c l a s s ) ;
i f (tabelaBP == n u l l ) {
S y s te m .o u t.p rin tln (
"U k l a s i " + imeKlase + " nema a n o t a c i j a T abela BP ");
c o n t i nue;
}
S t r i n g imeTabele = t a b e l a B P . i m e ( ) ;
/ / Ako j e ime prazno, u p o tr e b i ime Cla ss:
i f ( i m e T a b e l e . l e n g t h ( ) < 1)
imeTabele = c l .getName() .t o llp p e r C a s e ( ) ;
L i s t < S t r i n g > defKolona = new A r r a y L i s t < S t r i n g > ( ) ;
Poglavlje 20: Anotacije 855
f o r ( F ie l d p o lje : cl .g e tO e c la re d F ie ld s O ) {
S t r i n g imeKolone = n u l l ;
A n n o t a t i o n [ ] anot = p o l j e . g e t D e c l a r e d A n n o t a t i o n s ( ) ;
i f ( a n o t . l e n g t h < 1)
c o n t in u e ; / / N i j e kolona t a b e l e baze podataka
i f ( a n o t [ 0 ] i n s t a n c e o f SQLInteger) {
SQLInteger s l n t = (SQLInteger) a n o t [ 0 ] ;
/ / Ako ime n i j e s p e c i f i c i r a n o , u p o t r e b i ime p o l j a
i f ( s l n t . i m e ( ) . l e n g t h ( ) < 1)
imeKolone = p o l j e . g e t N a m e ( ) , t o U p p e r C a s e ( ) ;
e ls e
imeKolone = s l n t . i m e ( ) ;
d e fKolo na.a dd(im eK olone + " INT" +
d a jO g ra n ic e n ja (s In t.o g ra n ic e n ja () ) ) ;
}
i f ( a n o t [ 0 ] in s t a n c e o f SQLString) {
SQLString s S t r i n g = (SQLString) a n o t [ 0 ] ;
/ / Ako ime n i j e s p e c i f i c i r a n o , u p o tr e b i ime p o l j a .
i f ( s S t r i n g . i me( ) . l e n g t h ( ) < 1)
imeKolone = p o l j e . g e t N a m e ( ) . t o U p p e r C a s e ( ) ;
el se
imeKolone = s S t r i n g . i m e ( ) ;
d e fKolo na.a dd(im eK olone + " VARCHAR(" +
s S trin g .v re d n o s tO + " ) " +
daj Ograni cenj a ( s S t r i ng. o g ra n i cenj a ( ) ) ) ;
}
S t r i n g B u i l d e r komandaZaPravljenje = new S t r i n g B u i l d e r (
"NAPRAVI TABELU 11 + imeTabele + " ( " ) ;
f o r ( S t r i n g defKolone : defKolona)
k om an d a Za P ra v lje n je .a p p en d ("\n " + defKolo ne + " , " ) ;
/ / Uklo ni p r a t e ć i zarez
S t r i n g na p ra viT a be lu = k o m a n d a Z a P ra v lje n je . s u b s t rin g (
0, k o m a n d a Z a P ra v lje n je . le n g t h f ) - 1) + " ) ; " ;
S y s te m .o u t. p r i n t l n ( " S Q L za p r a v l j e n j e t a b e l e za kla su " +
imeKlase + " g l a s i : \ n " + n a p r a v i T a b e l u ) ;
p r i v a t e s t a t i c S t r i n g d a jO g r a n ic e n ja ( O g r a n ic e n ja og r) {
S t r i n g o g ra n ic e n ja =
i f ( lo g r.d o z v o liN u l1())
o g r a n ic e n ja += " NIJE NULL";
i f ( o g r . p r i m a r n i K L J u c ())
o g r a n ic e n ja += " PRIMARNI KLJUC";
i f ( o g r . je d in s tv e n o O )
o g r a n ic e n ja += " JEDINSTVENO";
r e t u r n o g r a n ic e n ja ;
856 Misliti na Javi
} /* Isp is:
SQL za p r a v l j e n j e t a b e l e za k la su a n o ta c ije . b a z a p o d a t a k a . C la n g la s i:
NAPRAVI TABELU CLAN(
LICNOIME VARCHAR(30)) ;
SQL za p r a v l j e n j e t a b e l e za k la s u a n o ta c ije . b a z a p o d a t a k a . C la n g la s i:
NAPRAVI TABELU CLAN(
LICNOIME VARCHAR(30),
PREZIME VARCHAR(50)) ;
SQL za p r a v l j e n j e t a b e l e za k la su a n o ta c ije . b a z a p o d a t a k a . C la n g la s i:
NAPRAVI TABELU CLAN(
LICNOIME VARCHAR(30),
PREZIME VARCHAR(50),
STAROST I N T ) ;
SQL za p r a v l j e n j e t a b e l e za kla su a n o ta c ije . b a z a p o d a t a k a . C la n g la s i:
NAPRAVI TABELU CLAN(
LICNOIME VARCHAR(30),
PREZIME VARCHAR(50),
STAROST INT,
IDENTIFIKATOR VARCHAR(30) PRIMARNI KLJUC);
* ///:-
M etoda m a in ( ) prolazi kroza sva im ena klasa na kom andnoj liniji. Svaku klasu redom
učitava m etoda forN am e( ) i naredbom getAnnotation(TabelaBP.class) proverava im a li
anotaciju @TabelaBP. Ukoliko ima, ime te tabele biva pronađeno i uskladišteno. Zatim se
sva polja te klase učitavaju i proveravaju m etodom getD eclaredA nnotations( ). O na
vraća niz svih anotacija definisanih za datu m etodu. O peratorom instanceof utvrduje se
da li su te an o tađ je tipova @SQLlnteger odnosno @SQLString, i u svakom od tih sluča-
jeva pravi se relevantni String odlom ak sa im enom kolone tabele. Pošto se interfejsi ano-
tacija ne m ogu nasleđivati, korišćenje m etode getD ecIaredA nnotations( ) jedini je način
da se postigne približno polim orfno ponašanje.
Ugnežđenu anotaciju @Ogranicenja prosleđujem o m etodi dajO granicenja( ) koja
gradi String što sadrži ta SQL ograničenja.
Vredi spom enuti da je goreopisana tehnika pom alo naivan način definisanja preslika-
vanja objekata na relacione baze podataka. Ako prom enite im e tabele, m oraćete iznova da
prevodite Java kod, pošto anotacija tipa @TabelaBP prim a ime tabele kao param etar. To
nije naročito poželjno ponašanje. Već je m nogo dostupnih struktura (engl. fram ew ork) za
preslikavanje objekata na relacione baze podataka, a u sve više njih koriste se anotacije.
Vežba 1: (2) Realizujte više SQL tipova u prim eru s bazom podataka.
Projekat:’ Izm enite prim er s bazom podataka tako da se pom oću JDBC-a povezuje i radi
s pravom bazom podataka.
Projekat: Izm enite prim er s bazom podataka tako da um esto pisanja SQL koda pravi od-
govarajuće XML datoteke.
@Target(E1ementType.TYPE)
@Retenti o n (R e t e n t io n P o li c y . SOURCE)
p u b l i c @ in t e rf a c e I z d v o j i I n t e r f e j s {
p u b lic S trin g v re d n o st();
) ///:-
M etaanotacija R etentionP olicy im a vređnost SOURCE zato što ovu anotaciju nem a
smisla čuvati u đatoteci klase nakon što iz nje izdvojimo interfejs. Sledeća klasa ima javnu
m etodu koja m ože postati deo korisnog interfejsa:
/ / : a n o ta c ij e / M n o z a c .j a v a
/ / Obrada a n o t a c i j a pomoću APT-a.
package a n o t a c i j e ;
Klasa M nozac (koja radi sam o s pozitivnim celim brojevim a) im a m etodu p o m n o z i()
koja više p u ta poziva privatnu m etodu a d d ( ) da bi obavila m noženje. M etoda add( ) nije
javna, te stoga nije ni deo interfejsa. A notaciji je data vrednost IfM nozaca, što je ime in-
terfejsa koji treba napraviti.
Sada nam treba procesor za izdvajanje:
p u b l i c c la s s P r o c e s o r Z a l z d v a j a n j e l n t e r f e j s a
implements A n n o ta tio n P ro c e s s o r {
p r i v a t e f i n a l An n o ta tio n P ro c e ss o rE n vir o n m e n t env;
p r i v a t e A r r a y L is t < M e th o d D e c la r a t io n > m e t o d e l n t e r f e j s a =
new A r r a y L i s t < M e t h o d D e c l a r a t i o n > ( ) ;
p u b lic P ro c e s o rZ a Iz d v a ja n je In te rfe js a (
A n n o ta tio nP ro ce ss o rE n vir on m e n t env) { t h i s . e n v = env; }
Poglavlje 20: Anotacije 859
p u b l i c v o id p ro c e s s ( ) {
f o r ( T y p e D e c l a r a t i o n d e k lT ip a :
e n v . g e t S p e c if ie d T y p e D e c la r a t io n s O ) {
I z d v o j i l n t e r f e j s anot =
d e k lT ip a .g e tA n n o ta tio n (Iz d v o jiIn te rfe js .c la s s );
i f ( a n o t == n u l l )
bre ak;
f o r ( M e t h o d D e c la r a t io n m : d e k lT ip a . g e t M e t h o d s O )
i f ( m . g e t M o d i f i e r s ( ) . c o n t a in s ( M o d ifie r . P U B L I C ) &&
! ( m . g e t M o d i f i e r s O . c o n t a in s ( M o d if ie r .S T A T I C ) ))
m eto d e ln te rfe jsa .a d d (m );
i f ( m e t o d e l n t e r f e j s a . s i z e O > 0) {
try {
P r i n t W r i t e r stampac =
e n v . g e t F i 1e r ( ) . c r e a t e S o u r c e F i l e ( a n o t . v r e d n o s t ( ) ) ;
s ta m p a c .p rin tln C 'p a k e t " +
d e k lT ip a .g e tP a c k a g e O .getQual ifie d N a m e () + " ; " ) ;
s ta m p a c .p rin tln ("ja v n i in t e r f e js " +
a n o t.v re d n o s t() + " { " ) ;
f o r ( M e t h o d D e c la r a t io n m : m e t o d e ln t e r f e js a ) {
s ta m p a c .p rin t(" ja v n i " ) ;
s ta m p a c . p rin t ( m .g e t R e t u rn T y p e () + " " ) ;
s ta m p a c.p ri nt(m .g e tSim p le N a m e() + 11 ( " ) ;
i n t i = 0;
f o r ( P a r a m e t e r D e c la r a t io n parm :
m .ge tP ara m e te rs ( ) ) {
s t a m p a c . p r in t ( p a r m . g e tT y p e ( ) + " " +
parm .getS im ple N am eO );
i f ( + + i < m .ge tP ara m e te rs( ) . s i z e ( ) )
stam pac.p r i n t ( " , " ) ;
}
s ta m p a c .p ri n t l n ( " ) ; " ) ;
}
stampac.p r i n t l n ( " } " ) ;
s ta m p a c .c lo s e O ;
} c a t c h ( IO Exception io e ) {
th ro w new R u n tim e E x c e p t io n ( io e ) ;
} ///:-
Sve se ra d i u m e to d i pro cess( ). P o m o ć u k lase M ethodDeclaration i n je n e m e to d e
getM odifiers( ) id e n tifik u je m o ja v n e m e to d e (i z a n e m a r u je m o o n e k o je su sta tič n e )
o b r a đ iv a n e klase. U k o lik o ih p r o n a đ e m o , s k la d iš tim o ih u o b je k tu tip a ArrayList i
u p o tr e b lja v a m o za p ra v lje n je m e to d a n o v o g in te rfe jsa u d a to te c i .java.
O b r a tite p a ž n ju n a to d a o b je k a t tip a A nnotationProcessorEnvironm ent p ro sle -
đ u je m o k o n s t r u k t o r u . T o m o b je k tu m o ž e te slati u p ite za sve tip o v e (d e fin ic ije k lasa) k o je
a la tk a apt o b r a đ u je i p o m o ć u n jeg a m o ž e te d o b iti o b je k a t tip a Messager i o b je k a t tip a
86 0 Misliti na Javi
Filer. Messager služi za slanje poruka korisniku, npr. grešaka koje su nastale tokom obra-
de i njihovih m esta u izvornom kodu. Filer je neka vrsta PrintW ritera za pravljenje novih
datoteka. Umesto običnog PrintW ritera najčešće se koristi objekat tipa Filer zato što
om ogućuje alatki apt da prati nove datoteke koje napravite i, ako treba, pronalazi u njim a
anotacije i prevodi ih.
Videćete i da m etoda createSourceFile( ) otvara običan izlazni tok s tačnim im enom
odgovarajuće Java klase ili interfejsa. N em a podrške za pravljenje Javinih jezičkih kon-
strukcija, pa Javin izvorni kod m orate da generišete pom alo prim itivnim m etodam a
p r in t( ) i p rin tln ( ). To znači da m orate paziti na to da zatvorite sve zagrade koje ste otvo-
rili i da vaš kod m ora biti sintaksički ispravan.
M etodu process( ) poziva alatka apt kojoj treba proizvodna m etoda da bi napravila
odgovarajući procesor:
/ / : a n o t a c ij e / P r o i z v o d j a c P r o c e s o r a Z a l z d v a j a n j e l n t e r f e j s a . j a v a
/ / Obrada a n o t a c i j a pomoću APT-a.
package a n o t a c i j e ;
im p o r t c o m . s u n . m i r r o r . a p t . * ;
im p o rt c o m . s u n . m i r r o r . d e c l a r a t i o n . * ;
im p o r t j a v a . u t i l . * ;
p u b l i c c la s s P r o iz v o d j a c P r o c e s o r a Z a l z d v a j a n j e l n t e r f e j s a
implements A n n o ta tio n P ro c e s s o r F a c to ry {
p u b l i c A n n o ta tio n P ro c e s s o r g e tP ro c e s s o rF o r(
Set<AnnotationTypeDec1 a r a t i o n > a t d s ,
A n n o ta tio n P ro c e s s o r E n v ironment env) {
r e t u r n new P r o c e s o r Z a l z d v a j a n j e l n t e r f e j s a ( e n v ) ;
}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o r te d A n n o ta tio n T y p e s () {
return
C o lle c tio n s .s in g le to n ("a n o ta c ije .Iz d v o jiIn te rfe js ");
}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o r te d O p tio n s ( ) {
r e t u r n C o l1e c t io n s . e m p t y S e t ( ) ;
}
} III'--
U interfejsu A nnotationProcessorFactory sam o su tri metode. Kao što vidite, procesor
pravi m etoda getProcessorFor( ) koja prim a Set (skup) deklaracija tipova (fava klasa koje
alatka apt obrađuje) i objekat tipa A nnotationProcessorEnvironm ent koji sm o već pro-
puštali kroz procesor. Ostale dve metode, supportedA nnotationTypes( ) i supportedO p-
tions( ), postoje da biste mogli proveriti imate li procesore za sve anotacije koje je apt
pronašao i da li podržavate sve opcije specificirane na kom andnoj liniji. Naročito je važna
metoda getProcessorFor( ) zato što će vas apt, ako u kolekciji String ne vratite puno ime
kJase (tipa) anotacije, upozoriti da nem a relevantnog procesora i izaći, a da ništa ne uradi.
Procesor i proizvodna m etoda su u paketu anotacije, pa je za gornju stru k tu ru direk-
torijum a kom andna Iinija ugrađena u kom entar Exec na početku program a Proce-
sorZalzdvajanjelnterfejsa.java. Time se alatki apt kazuje da upotrebi goredefinisanu
Poglav|je 20: Anotacije 861
proizvodnu m etodu i da obradi datoteku Mnozac.java. O pcija -s zadaje da se sve nove da-
toteke m oraju napraviti u direktorijum u anotacije. Ovako izgleda generisana datoteka If-
Mnozaca.java, što ste mogli pogoditi pročitavši naredbe p rin tln ( ) u gornjem procesoru:
package a n o t a c i j e ;
p u b l i c i n t e r f a c e IfMnozaca {
p u b l i c i n t pomnozi ( i n t x , i n t y ) ;
}
/ / : a n o t a c i j e / b a z a p o d a t a k a /P r o iz v o d ja c P r o c e s o r a Z a P r a v lj e n je T a b e la . ja v a
/ / Prim er s bazom podataka n a p r a v lje n po p ro je ktn o m obrascu V i s i t o r .
/ / {Exec: apt - f a c t o r y
/ / a n o ta c ije . b a z a p o d a t a k a . P r o iz v o d a č P ro c e s o r a Z a P r a v lje n je T a b e la
/ / b a z a p o d a ta k a /C la n .ja v a -s bazapodataka}
package a n o ta c ije . b a z a p o d a t a k a ;
im p o r t com.sun, m ir r o r . a p t . * ;
im p o r t c o m . s u n . m i r r o r . d e c l a r a t i o n . * ;
im p o r t c o m . s u n . m i r r o r . u t i 1 . * ;
im p o rt j a v a . u t i 1 . * ;
im p o r t s t a t i c c o m . s u n . m i r r o r . u t i 1. D e c l a r a t i o n V i s i t o r s . * ;
862 Misliti na Javi
p u b l i c c la s s P ro iz v o d ja c P r o c e s o r a Z a P r a v lje n je T a b e la
implements A n n o ta tio n P ro c e s s o r F a c to ry {
p u b l i c A n n o ta tio n P ro c e s s o r g e tP ro c e s s o rF o r(
Se t< A n n o ta ti onTypeDec1aration> a t d s ,
Ann o ta tio nP ro c e s s o rE n v ir on m e n t env) {
r e t u r n new P r o c e s o r Z a P r a v lje n je T a b e la ( e n v ) ;
}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o rte d A n n o ta tio n T y p e s () {
re tu rn A rra y s .a s L is t(
" a n o ta c ije .b a z a p o d a ta k a .T a b e la B P " ,
" a n o t a c ije . b a z a p o d a t a k a . O g r a n ic e n j a " ,
" a n o t a c ije . b a z a p o d a t a k a . SQLStri n g " ,
" a n o t a c ije . b a z a p o d a t a k a . S Q L I n t e g e r " ) ;
}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o r te d O p tio n s () {
r e t u r n C o l1e c t i o n s . emptySet( ) ;
}
p r i v a t e s t a t i c c la s s P ro cesorZ aPravlj e n je T a b e la
implements A n n o ta tio n P ro c e s s o r {
p r i v a t e f i n a l Annota tio nP ro ce s so rE n vir on m e n t env;
p r i v a t e S t r i n g sql =
p u b l i c P r o c e s o rZ a P r a v lje n je T a b e la (
Annota tio nP ro c e ss o rE n v ir on m e n t env) {
t h i s . e n v = env;
}
p u b l i c v o id p ro c e s s () {
f o r ( T y p e D e c la r a t io n d e k lT ip a ;
e n v .g e t S p e c if ie d T y p e D e c la r a t i o n s ( ) ) {
d e k l Ti p a . a c c e p t ( g e t D e c la r a t i onScanner(
new P o s e t i 1a c Z a P r a v l j e n j e T a b e l a ( ) , N0_0P)) ;
sql = s q l . s u b s t r i n g ( 0 , sq1 . 1 e n g th ( ) - 1) + " ) ; " ;
S y s t e m . o u t . p r in t ln ( " S Q L za p r a v l j e n j e g l a s i ; \ n " + s q l ) ;
sql = " " ;
}
}
p r i v a t e c la s s P o s e tila c Z a P r a v lj e n je T a b e la
extends S i m p l e D e c l a r a t i o n V i s i t o r {
p u b l i c v o id v i s i t C l a s s D e c l a r a t i o n (
C1 a s s D e c la r a t io n d) {
TabelaBP tabelaBP = d . g e t A n n o t a t io n ( T a b e la B P . c la s s ) ;
if ( t a b e l a B P != n u l l ) {
sql += "NAPRAVI TABELU " ;
sql += ( t a b e l a B P . i m e ( ) . 1e n g th () < 1)
? d.getS im ple Nam e().toUppe rCase()
: t a b e l a B P . im e ( ) ;
sql += " ( " ;
}
}
Poglavlje 20: Anotacije 863
p u b l i c v o id v i s i t F i e l d D e c l a r a t i o n (
F ie l d D e c l a r a t i o n d) {
S t r i n g imeKolone =
i f ( d . g e t A n n o t a t io n ( S Q L I n t e g e r . c la s s ) != n u l l ) {
SQLInteger s l n t = d . g e t A n n o t a t io n (
S Q L I n t e g e r . c la s s ) ;
/ / Ako ime n i j e s p e c i f i c i r a n o , u p o tr e b i ime p o l j a .
i f ( s l n t . i m e ( ) . l e n g t h ( ) < 1)
imeKolone = d .g etS im p le N a m e ().to U p pe rC a s e ();
el se
imeKolone = s l n t . i m e ( ) ;
sql += " \ n " + imeKolone + " INT" +
d a jO g ra n ic e n ja (s In t.o g ra n ic e n ja ()) +
}
i f ( d . g e t A n n o t a t i o n ( S Q L S t r i n g . c l a s s ) != n u l l ) {
SQLString s S t r in g = d . g e t A n n o t a t io n (
S Q L S tr in g . c la s s ) ;
/ / Ako ime n i j e s p e c i f i c i r a n o , u p o tr e b i ime p o l j a .
i f ( s S t r i n g . i m e ( ) . 1 e n g th () < 1)
imeKolone = d.g etS im p le Na m e (). to U p p e r C a s e () ;
el se
imeKolone = s S t r i n g . i m e ( ) ;
sql += " \ n " + imeKolone + " VARCHAR(" +
s S trin g .v re d n o s tO + " ) " +
d a jO g ra n ic e n ja (s S trin g .o g ra n ic e n ja ()) +
}
}
p r i v a t e S t r i n g d a jO g ra n ic e n ja (O g r a n ic e n ja og r) {
S t r i n g o g ra n ic e n ja =
i f ( l o g r . d o z v o l i Nu l1 ( ) )
o g r a n ic e n ja += " NIJE NULL";
i f ( o g r . p r i m a r n i K L J u c())
o g r a n ic e n ja += " PRIMARNI KLJUC";
i f (o g r.je d in s tv e n o O )
o g ra n ic e n ja += " JEDINSTVENO";
} r e t u r n o g ra n ic e n ja ;
}
}
} III--
Ispis rezultata je isti kao u prethodnom prim eru TabelaBP.
U ovom p rim eru su procesor i posetilac u nutrašnje klase. O bratite pažnju na to da me-
toda p ro cess( ) sam o dodaje klasu posetioca i inicijalizuje SQL znakovni niz.
O ba param etra m etode getD eclarationScanner( ) jesu posetioci; prvi biva upo-
trebljen pre posete svakoj deklaraciji, a drugi posle. O vom procesoru je potreban sam o
posetilac od pre posete, pa se NO_OP ne daje kao drugi param etar. To je statično polje in-
terfejsa DeclarationVisitor, što je objekat tipa DeclarationV isitor koji ne radi ništa.
PosetilacZaPravljenjeTabela nasleduje ldasu Sim pleDeclarationVisitor i redefiniše
m etode visitC lassD eclaration( ) i visitFieldD ecIaration( ). SimpleDeclarationVisitor
86 4 Misliti na Javi
p u b l i c c la s s A t U n i t P r i m e r l {
p u b l i c S t r i n g metodaJedan() {
r e t u r n "Ovo j e metodaJedan";
}
p u b l i c i n t metodaDva() {
S y s t e m . o u t . p r i n t l n ( " O v o j e metodaDva");
r e t u r n 2;
}
@Test boolean metodaJedanTest() {
r e t u r n m e t o d a Je d a n().eq u a ls ("O v o j e metodaJedan");
}
@Test boolean m2() { r e t u r n metodaDva() == 2 ; }
@Test p r i v a t e boolean m3() { r e t u r n t r u e ; }
/ / P r i k a z u j e r e z u l t a t u s lu č a j u g re ša ka :
@Test boolean neuspesanTest() { r e t u r n f a l s e ; }
@Test boolean jo sje danNeuspeh() { r e t u r n f a l s e ; }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) throws Exce p tio n {
OSIzvršenje.kom anda(
"ja va n e t.m in d v ie w .a tu n it.A tU n it A tU n itP r im e r l" ) ;
}
} / * Is p is :
a n o ta c ije .A tU n itP rim e rl
. metodaJedanTest
. m2 Ovo j e metodaDva
. m3
. neuspesanTest ( f a i l e d )
. josjedanNeuspeh ( f a i l e d )
(5 t e s t s )
p u b l i c c la s s A t U n i t S p o l j n i T e s t e xtends A t U n i t P r i m e r l {
@Test boolean _metodaJedan() {
r e t u r n m e to d a J e d a n ( ) .e q u a ls ( "0 v o j e m eto d a Je d a n");
}
@Test boolean _metodaDva() { r e t u r n metodaDva() == 2 ; }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) thro ws E xc e p tio n {
OSIzvršenje.komanda(
" ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t S p o l j n i T e s t " ) ;
}
} / * Is p is :
a n o ta c ije .A tU n itS p o l jn iT e s t
. _metodaJedan
. _metodaDva Ovo j e metodaDva
OK (2 t e s t s )
* ///:-
Iz ovog prim era vidi se i vrednost slobodnog davanja im ena (za razliku od obaveznog
test na početku imena svih testova u JU nitu). Ovde je <®Test m etodi koja neposredno te-
stira drugu m etodu dato ime te m etode, uz p o tcrtu (_) na početku. (Ne tvrdim da je to
idealno rešenje, nego sam o pokazujem jed n u od m ogučnosti.)
Za pravljenje neugrađenih testova m ožete upotrebiti i kompoziciju:
p u b l i c c la s s A t U n it K o m p o z ic ija {
A t U n i t P r i m e r l t e s t O b j e k a t = new A t U n i t P r i m e r l ( ) ;
@Test boolean _metodaJedan() {
return
te stO b je k a t.m e to d a J e d a n O . e q u a l s ( “ 0vo j e m eto d a Je d a n");
}
@Test boolean _metodaDva() {
r e t u r n t e s tO b je k a t.m e to d a D v a () == 2;
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) thro ws Exce p tio n {
O S I z v r š e n je . komanda(
Poglavlje 20: Anotacije 867
" ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t K o m p o z i c i j a " ) ;
)
} / * Is p is :
a n o t a c i j e . A t U n i t K o m p o z ic ija
. metodaJedan
. jn etodaDva Ovo j e metodaDva
OK (2 t e s t s )
* ///.-
Za svaki test pravi se po jedan nov član testObjekat, pošto se po jedan objekat tipa
AtUnitKompozicija pravi za svaki test.
N ema posebnih m etoda za proveru uspešnosti testiranja kao u JUnitu, ali drugi oblik
m etode @Test om ogućuje da vratite tip void (ili boolean, ako i u ovom slučaju želite da
vraćate tru e ili false). Za testiranje uspešnosti m ožete koristiti Javine naredbe assert koje
treba uključiti indikatorom -ea na java kom andnoj liniji, ali ih @Unit uključuje autom at-
ski. Kao pokazatelj neuspeha m ožete upotrebiti čak i izuzetak. Jedan od ciljeva prilikom
projektovanja alatke @Unit bio je da se količina dođatne sintakse svede na najm anju
m eru, pa su za prijavljivanje grešaka potrebni sam o Javine naredlie assert i izuzeci. Da je
test neuspešan, pokazuje assert koji vrati negativan rezultat ili izuzetak koji potiče iz
ispitne m etode, ali @Unit ni u tom slučaju ne prestaje da radi, nego nastavlja ispitivanje
dok ne obavi sve testove. Evo prim era:
p u b l i c c la s s A t U n it P r im e r 2 {
p u b l i c S t r i n g metodaJedan() {
r e t u r n "Ovo j e metodaJedan";
}
p u b l i c i n t metodaDvaO {
S y s te m .o u t. p r i n t l n ( “ Ovo j e metodaDva");
r e t u r n 2;
}
OTest vo id a s s e r t P r i m e r ( ) {
a s s e r t metodaJedan( ) . e q u a ls ( " 0 v o j e m eto d a Je d a n");
}
@Test v o id ass ertP rim erN euspehaO {
a s s e r t 1 == 2: "Kojeg l i iz n e n a đ e n ja ! " ;
}
@Test v o id p r i m e r l z u z e t k a ( ) throws I0 E x c e p tio n {
new F i l e I n p u t S t r e a m ( " n i j e d a t o t e k a . t x t " ) ; / / Baca
}
OTest boolean a s s e r t l R e t u r n () {
868 Mlsliti na Javi
(4 t e s t s )
/ / : a n o ta c ij e / H a s h S e t T e s t .j a v a
package a n o t a c i j e ;
im p o r t j a v a . u t i l . * ;
im p o r t n e t . m i n d v i e w . a t u n i t . * ;
im p o r t n e t . m i n d v i e w . u t i l . * ;
p u b l i c c la s s HashSetTest {
HashSet<String> t e s t O b j e k a t = new H a s h S e t < S t r in g > ( ) ;
OTest v o id i n i c i j a l i z a c i j a ( ) {
assert te stO bjeka t.isE m p ty( ) ;
}
@Test v o id _ c o n t a i n s ( ) {
te s tO b je k a t.a d d ("je d a n ");
asse rt te s tO b je k a t.c o n ta in s ( " j e d a n " ) ;
}
@Test v o id _remove() {
te s tO b je k a t.a d d ("je d a n ");
te s tO b je k a t.re m o v e ("je d a n ");
assert te s tO b je k a t.is E m p ty ();
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n {
OSIzvršenje.komanda(
" ja v a n e t .m in d v ie w . a t u n i t . A t U n i t H a s h S e tT e s t" ) ;
Poglavjje 20: Anotaclje 869
}
} / * Is p is :
a n o ta c ije .H a s h S e tT e s t
. in ic ija liz a c ija
. _remove
. _ c o n ta in s
OK (3 t e s t s )
* ///:-
p u b l i c c la s s A tU n itP rim e r3 {
p r i v a t e i n t n;
p u b l i c A t U n i t P r i m e r 3 ( i n t n) { t h i s . n = n; }
p u b l i c i n t getN() { r e t u r n n; }
p u b l i c S t r i n g metodaJedan() {
r e t u r n "Ovo j e metodaJedan";
}
p u b l i c i n t metodaDva() {
S y s t e m . o u t . p r i n t l n("Ovo j e metodaDva");
r e t u r n 2;
}
@TestObjectCreate s t a t i c A tU n it P rim e r3 n a p r a v i () {
r e t u r n new A t U n i t P r i m e r 3 ( 4 7 ) ;
}
@Test boolean i n i c i j a l i z a c i j a ( ) { r e t u r n n == 47; }
@Test boolean metodaJedanTest() {
r e t u r n m etodaJedan(). equals("Ovo j e m etodaJedan");
}
@Test boolean m2() { r e t u r n metodaDva() == 2; }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws E x ce p tio n {
870 Misliti na Javi
OK (3 t e s t s )
* ///:-
M etoda @TestObjectCreate m ora biti statična i vratiti objekat tipa koji testirate - pro-
gram @Unit će se postarati da to bude ispunjeno.
Katkada će vam zatrebati dodatna polja za podršku jediničnom testiranju. A notaciju
@TestProperty možete upotrebiti za označavanje polja koja se upotrebljavaju sam o za je-
dinično testiranje (da biste mogli da ih uklonite pre isporuke program a klijentu). U na-
rednom p rim eru čitam o vrednosti iz znakovnog niza raščlanjenog m etodom
S tring.split( ). Taj ulaz se upotrebljava za proizvodnju test objekata:
/ / : a n o t a c i j e / A t U n i t P r i m e r 4 . ja v a
package a n o t a c i j e ;
im p o r t j a v a . u t i l . * ;
im p o r t n e t . m i n d v i e w . a t u n i t . * ;
im p o r t n e t . m i n d v i e w . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s A tU n itP rim e r4 {
s t a t i c S t r i n g t e o r i j a = " A l l b ro n to sa u ru s e s " +
" a r e t h i n a t one end, much MUCH t h i c k e r i n th e " +
" m id d le , and then t h i n again a t the f a r e n d ." ;
p r i v a t e S trin g rec;
/ / I n i c i j a l i z a c i j a g e n e r a to ra s l u č a j n i h b ro je v a k o ja z a v i s i od vremena
p r i v a t e Random s lu c a ja n = new Random();
p u b l i c A t U n i t P r i m e r 4 ( S t r i n g re c ) { t h i s . r e c = r e c ; }
p u b l i c S t r i n g dajR ec() { r e t u r n r e c ; }
p u b l i c S t r i n g is p r e t u r a j R e c ( ) {
L i s t< C h ara c te r> znakovi = new A r r a y L i s t < C h a r a c t e r > ( ) ;
fo r(C h a ra cte r z : rec.toC harA rray( ) )
z n a k o v i.a d d (z );
C o l1e c t i o n s . s h u f f l e ( z n a k o v i , s l u c a j a n ) ;
S t r i n g B u i 1der r e z u l t a t = new S t r i n g B u i l d e r ( ) ;
f o r ( c h a r zn : zna k o vi)
re z u lta t.a p p e n d (z n );
retu rn re z u lt a t . t o S tr in g ( );
}
@TestProperty s t a t i c L i s t < S t r i n g > u la z =
A rra y s .a s L is t(te o rija .s p lit(" " ) ) ;
@TestProperty
s t a t ic Ite ra to r< S trin g > reci = u l az. i t e r a t o r ( ) ;
Poglavlje 20: Anotacije 871
@TestObjectCreate s t a t i c A tU n it P rim e r4 n a p r a v i( ) (
if(re c i,h a s N e x t())
r e t u r n new A t U n i t P r i m e r 4 ( r e c i . n e x t ( ) ) ;
el se
retu rn n u l l ;
}
@Test boolean r e c i ( ) (
p r i n t ( ..... + d a jR ec() + ..... ) ;
retu rn d a jR e c ().e q u a ls ("a re ");
}
@Test boolean i s p r e t u r a j l ( ) (
/ / Prelazimo na određeno seme (g e n e r a to r a s l u č a j n i h
/ / b r o je v a ) da bismo m ogli da proveravamo r e z u l t a t e :
s lu c a ja n = new Random(47);
p r i n t ( ..... + dajR ec() + ..... ) ;
S trin g is p returano = is p r e tu r a jR e c ( ) ;
p ri n t ( i spreturano);
retu rn is p re tu ra n o .e q u a ls ("lA l" ) ;
}
@Test boolean i s p r e t u r a j 2 ( ) {
s lu c a ja n = new Random(74);
p r i n t ( ..... + dajRecO + ..... ) ;
S t r i n g is p r e t u r a n o = i s p r e t u r a j R e c ( ) ;
p r i n t ( i spreturano);
r e tu rn is p re tu ra n o .e q u a ls ("ts a e b o ro rn u s s u ");
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] arg s) throws Exceptio n {
S y s te m .o u t. p r i n t l n ( “ k r e ć e " ) ;
OSIzvršenje.komanda(
" ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t P r i m e r 4 " ) ;
}
} / * Is p is :
kreće
a n o t a c i j e . A t U n i tP rim er4
. i s p r e t u r a j 1 ' A11 '
1A1
. is p re tu ra j2 ' b r o n t o s a u ru s e s '
ts a e bororn ussu
OK (3 t e s t s )
* ///:-
Ukoliko tokom inicijalizacije objekata za testiranje uradite nešto što kasnije treba
počistiti (engl. clean up ), m ožete dodati statičnu m etodu označenu sa @ TestObject-
C leanup koja obavlja čišćenje kada završite testiranje objekta. U sledećem prim eru,
@ TestO bjectCreate otvara datoteku rad i pravljenja svakog objekta za testiranje, pa se ta
datoteka m ora zatvoriti pre odbacivanja sam og objekta testiranja:
p u b l i c c la s s A tU n itP rim e r5 {
p riv a te S trin g te k s t;
p u b l i c A t U n it P r im e r 5 ( S t r in g t e k s t ) { t h i s . t e k s t = t e k s t ; }
p u b lic S trin g to S trin g () { re tu rn t e k s t; }
@TestProperty s t a t i c P r i n t W r i t e r o u t p u t ;
@TestProperty s t a t i c i n t b r o j a c ;
PT estO bje ctCreate s t a t i c A t U n it P r im e r 5 n a p r a v i () {
S t r i n g id = I n t e g e r . t o S t r i n g ( b r o j a c + + ) ;
try {
i z l a z = new P r i n t W r i t e r ( " T e s t " + i d + " . t x t " ) ;
} ca tc h (IO E x c e p tio n e) {
th ro w new R u n tim e E x c e p tio n ( e ) ;
}
r e t u r n new A t U n i t P r i m e r 5 ( i d ) ;
}
PTestObjectCleanup s t a t i c v o id
c le a n u p ( A tU n itP rim e r5 t o b j ) {
S y s t e m . o u t . p r in t ln ( " P o k r e ć e m metodu c l e a n u p " ) ;
iz la z .c lo s e O ;
}
@Test boolean t e s t l ( ) {
iz la z .p rin t(" te s tl" );
retu rn tru e ;
}
OTest boolean t e s t 2 ( ) {
iz la z .p rin t( " te s t2 " ) ;
retu rn tru e ;
}
@Test boolean t e s t 3 ( ) {
iz la z .p rin t("te s t3 ");
retu rn tru e ;
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) throws Exce p tio n {
OSIzvršenje.komanda(
" ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t P r i m e r 5 " ) ;
Poglavlje 20: Anotacije 873
} / * Is p is :
a n o ta c i j e . A t l l n i t P r i m e r 5
. te s tl
Pokrećem cleanup
. test2
Pokrećem cleanup
. te st3
Pokrećem cleanup
OK (3 t e s t s )
* ///•-
Iz ispisa rezultata vidite da se m etoda za čišćenje autom atski pokreće nakon svakog te-
stiranja.
/ / : a n o t a c i j e / S t e k L . ja v a
/ / Stek n a p r a v lje n od ulanćane l i s t e .
package a n o t a c i j e ;
im p o r t j a v a . u t i l . * ;
p u b l i c c la s s StekL<T> {
p r i v a t e L in k e d L is t< T > l i s t = new L i n k e d L i s t < T > ( ) ;
p u b l i c v o id push(T v) { l i s t . a d d F i r s t ( v ) ; }
p u b l i c T t o p ( ) { r e t u r n 1i s t . g g e t F i r s t ( ) ; }
p u b l i c T pop() { r e t u r n 1i s t . r e m o v e F ir s t ( ) ; }
} ///:-
PTarget(E1ementType.METHOD)
@Re tention(Re tentionPolicy.RUNT IME)
p u b l i c @ in te rfa c e T e s tO b je c tC re a te {} / / / : -
/ / : n e t / m in d v i e w / a t u n i t / T e s t O b j e c t C l e a n u p . j a v a
/ / PU n it oznaka PT estO bjectCleanup.
package n e t . m i n d v i e w . a t u n i t ;
im p o r t j a v a . l a n g . a n n o t a t i o n . * ;
O Ta rg e t(E1ementType.METHOD)
@ R e t e n tio n ( R e te n tio n P o li c y . RUNTIME)
p u b l i c (Pin te rfa ce TestO bje ctCle anup {} / / / : -
/ / : n e t/m i ndvi e w /a tu n i t / T e s t P r o p e r t y . j a v a
/ / @Unit oznaka @TestProperty.
package n e t . m i n d v i e w . a t u n i t ;
im p o r t j a v a . l a n g . a n n o t a t i o n . * ;
Svi testovi imaju RUNTIME kao vrednost m etaanotacije @ Retention, zato što sistem
@Unit testove m ora da pronalazi u prevedenom kodu.
l/.đvojičemo anotacije pom oču refleksije da bism o realizovali sistem koji izvršava te-
stiranje. Na osnovu tih informacija program odlučuje kako da napravi test objekte i kako da
ih testira. Zbog anotacija, to se postiže iznenađujuče malim i jednostavnim programom:
/ / : n e t / m i n d v i e w / a t u n i t / A t U n i t . ja v a
/ / S t r u k t u r a za j e d i n i č n o t e s t i r a n j e č i j u osnovu č in e a n o t a c i j e .
/ / {RunByHand}
package n e t . m i n d v i e w . a t u n i t ;
im p o r t j a v a . l a n g . r e f l e c t . * ;
im p o r t j a v a . i o . * ;
im p o r t j a v a . u t i 1. * ;
im p o r t n e t . m i n d v i e w . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
87 6 Misliti na Javi
p u b l i c c la s s A t U n i t implements P r o c e s s F ile s . S t r a t e g y {
s t a t i c Class<?> t e s t C l a s s ;
s t a t i c L i s t < S t r i n g > f a i l e d T e s t s = new A r r a y L i s t < S t r i n g > ( ) ;
s t a t i c long testsRun = 0;
s t a t i c long f a i l u r e s = 0;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n {
C la ssL o a d er.g e tSyste m C la ssL o a d e r()
. s e t D e f a u l t A s s e r t i o n S t a t u s ( t r u e ) ; / / D ozvoli naredbe a s s e r t
new P r o ce s sF ile s (n e w A t U n i t ( ) , " c l a s s " ) . s t a r t ( a r g s ) ;
i f ( f a i l u r e s == 0)
p r i n t ( " 0 K ( " + testsRun + " t e s t s ) " ) ;
e ls e {
p r i n t ( " ( " + te stsRun + " t e s t s ) " ) ;
p r i n t ( " \ n > » " + f a i l u r e s + " FAILURE" +
( f a i l u r e s > 1 ? "S" : " " ) + " < « " ) ;
fo r ( S t r in g f a ile d : fa ile d T e s ts )
p rin t(" " + fa ile d );
}
}
p u b l i c v o id p r o c e s s ( F i l e c F i l e ) {
try {
S t r i n g cName = C la s s N a m e F in d e r.th is C la s s (
B in a ry F ile .re a d (c F ile ));
r e t u r n ; / / Zanemari k la s e iz va n paketa
i f ( ! c N a m e . c o n t a in s ( " . " ) )
t e s t C la s s = Class.forName(cName);
} c a t c h ( E x c e p t io n e) {
th ro w new R u n t im e E x c e p t io n ( e ) ;
}
TestMethods te stM e th o d s = new T e s tM e th o d s ( ) ;
Method c r e a t o r = n u l l ;
Method cleanup = n u l l ;
fo r(M e th o d m : t e s t C la s s . g e t Đ e c la r e d M e t h o d s O ) {
t e s t M e t h o d s . a d d lf T e s t M e t h o d ( m ) ;
i f ( c r e a t o r == n u l l )
c r e a t o r = ch e ckForCre atorM ethod(m );
i f ( c l e a n u p == n u l l )
cleanup = checkForCleanupMethod(m);
}
i f ( t e s t M e t h o d s . s i z e ( ) > 0) {
i f ( c r e a t o r == n u l 1)
try {
if( !M o d ifie r .is P u b lic ( te s tC la s s
,g e tD e c la re d C o n s tru c to r(),g e tM o d ifie rs ())) {
p r i n t ( " E r r o r : " + t e s t C la s s +
" d e f a u l t c o n s t r u c t o r must be p u b l i c " ) ;
S y s te m .e x it(l);
}
} catch(NoSuchMethodException e) {
Poglavjje 20: Anotacije 877
/ / N a p ra v lje n podrazumevani k o n s t r u k t o r ; 0K
1
p rin t(te s tC la s s .g e tN a m e ());
}
f o r( M e th o d m : te stM e th o d s) {
p rin tn b (" . " + m.getName() + " " ) ;
try {
O b je ct t e s t O b j e k a t = c r e a t e T e s t O b j e c t ( c r e a t o r ) ;
boolean success = f a l s e ;
try {
if(m .g e tR e turn T y p e ().e q u a ls (bo o le a n .c la s s ))
success = ( B o o le a n ) m . in v o k e ( t e s t O b je k a t ) ;
el se {
m .i n v o k e ( t e s t O b j e k a t ) ;
success = t r u e ; / / Ako sve naredbe a s s e r t uspeju
}
} ca tc h (In v o c a tio n T a rg e tE x c e p t" io n e) {
/ / S t v a r n i iz u z e t a k j e u n u ta r e:
p rin t(e .g e tC a u s e O );
}
p rint(success ? "" : " ( f a i l e d ) " ) ;
testsRu n++;
if(is u c c e s s ) {
fa ilu re s + + ;
f a i le d T e s t s . a d d (t e s t C l ass.getNameO +
" : " + m.getName()) ;
}
i f ( c l e a n u p != n u l l )
c l e a n up .i n v o k e ( t e s t O b je k a t , t e s t O b j e k a t ) ;
} c a t c h (E x c e p t io n e) {
th ro w new R u n tiirte E x ce ptio n (e );
}
}
}
s t a t i c c la s s TestMethods extends A rray L is t< M e th o d > {
v o id addIfTestMethod(Method m) {
i f ( m . g e t A n n o t a t i o n ( T e s t . c l ass) == n u l l )
retu rn ;
i f ( ! ( m . g e t R e t u r n T y p e ( ) .e q u a ls ( b o o le a n . c la s s ) | |
m .g e t R e t u r n T y p e ( ) . e q u a ls ( v o i d . c l a s s ) ))
th ro w new RuntimeException("@Test method" +
" must r e t u r n boolean o r v o i d " ) ;
m .s e tA c c e s s ib le (tru e ); / / lik o lik o j e p riv a tn a i t d .
add(m );
}
}
p r i v a t e s t a t i c Method checkForCreatorMethod(Method m) {
if ( m . g e t A n n o t a t i o n ( T e s t O b j e c t C r e a t e . c l a s s ) == n u l l )
retu rn n u l1 ;
i f ( !m .g e t R e t u rn T y p e ( ) . e q u a l s ( t e s t C l a s s ) )
878 Misliti na Javi
Ako ne zadate argum ent s kom andne linije, program će pretražiti stablo tekućeg di-
rektorijum a. M ožete zadati i više argum enata, bilo class datoteka (s nastavkom .class ili
bez njega) bilo direktorijum a. Pošto @Unit autom atski pronalazi klase i m etode koji se
m ogu testirati, nije potreban m ehanizam ,,svita“.8
Jedan od problem a koje AtUnit.java m ora da reši kada pronađe class datoteku jeste taj
što se p u n o im e klase (koje obuhvata i ime njenog paketa) ne m ože saznati iz im ena same
class datoteke. Zato se class datoteka m ora analizirati, što nije lako, ali ni nem oguće.9
Dalde, nakon pronalaska .class datoteke ona se prvo otvara, njen binarni sadržaj učitava
i predaje m etodi ClassNam eFinder.thisClass( ). Potom prelazim o u dom en „inženjerin-
ga bajtkoda“, pošto zapravo analiziram o sadržaj class datoteke:
/ / : n e t/m i n d v i e w / a t u n i t / C l assNameFin d e r . ja v a
package n e t . m i n d v i e w . a t u n i t ;
im port j a v a . i o . * ;
im p o r t j a v a . u t i l . * ;
im p o r t n e t . m i n d v i e w . u t i l . * ;
im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s ClassNameFinder {
p u b l i c s t a t i c S t r i n g t h i s C la s s ( byt e [ ] c la s s B y te s ) {
M ap<Integer,Integer> o ffs e tT a b le =
new H a s h M a p < I n t e g e r , I n t e g e r > ( ) ;
M a p < I n t e g e r , S t r in g > classNameTable =
new H a s h M a p < I n t e g e r , S t r in g > ( ) ;
try {
Da ta ln p utS tre a m data = new Da ta InputStream(
new B y t e A r r a y I n p u t S t r e a m ( c la s s B y t e s ) ) ;
i n t magic = d a t a . r e a d l n t ( ) ; / / Oxcafebabe
i n t m in o rV e rsio n = d a t a . r e a d S h o r t ( ) ;
i n t m a jo rV e rsio n = d a t a . re a d S h o r t ( ) ;
i n t c o n s t a n t_ p o o l_ c o u n t = d a t a . r e a d S h o r t ( ) ;
i n t [ ] c o n s tan t_ p o o l = new i n t [ c o n s t a n t _ p o o l _ c o u n t ] ;
f o r ( i n t i = 1; i < c o n s ta n t_ p o o l_ c o u n t; i+ + ) {
i n t tag = d a t a . r e a d f ) ;
in t ta b le S iz e ;
s w itc h (ta g ) {
case 1: / / UTF
i n t le n g t h = d a t a . r e a d S h o r t ( ) ;
c h a r [ ] bytes = new c h a r [ l e n g t h ] ;
f o r ( i n t k = 0; k < b y t e s . l e n g t h ; k++)
b yte s[k] = (c h a r)d a ta .re a d ();
S t r i n g imeKlase = new S t r i n g ( b y t e s ) ;
classNameTable . p u t ( i , im e K la s e );
b re ak;
case 5: / / LONG
case 6: / / DOUBLE
d a t a . r e a d L o n g O ; / / o d b a c i t i 8 b a jt o v a
i+ + ; / / Potreban j e poseban skok
b re ak;
case 7: / / CLASS
in t o ffs e t = d a ta .re a d S h o rt();
o ffs e tT a b le .p u t(i, o ffs e t) ;
break;
case 8: / / STRING
d a ta .re a d S h o rt(); / / o d b a c iti 2 b a jta
bre ak;
case 3: / / INTEGER
case 4: / / FLOAT
case 9: / / FIELD_REF
case 10: / / METHOD_REF
case 11: / / INTERFACE_METHOD_REF
case 12: / / NAME_AND_TYPE
d a ta .re a d ln t( ) ; / / o d b a c iti 4 b a jta ;
break;
d e fa u lt:
throw new R untim eE xceptio n("B ad tag " + t a g ) ;
}
i
s h o r t a c c e s s _ fl ag s = d a t a . r e a d S h o r t ( ) ;
i n t th is _ c la s s = d a ta .r e a d S h o rt( ) ;
i n t s uper_ cla ss = d a t a . r e a d S h o r t ( ) ;
r e t u r n cla ssNam eT able.get(
o f f s e t T a b l e . g e t ( t h i s _ c l a s s ) ) . r e p l a c e ( 1/ 1, 1. 1) ;
} c a t c h (E x c e p t io n e) {
th ro w new R u n t im e E x c e p t io n ( e ) ;
}
}
/ / P r ik a z :
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exceptio n {
i f ( a r g s . l e n g t h > 0) {
f o r ( S t r i n g arg : args)
p r i n t ( t h i s C l a s s ( B i n a r y F i 1e .re ad(new Fi 1e ( a r g ) ) ) ) ;
} el se
/ / Prola zak kroz c e lo s t a b l o :
f o r ( F i l e kla ss : D i r e c t o r y . w a l k ( " . " , " . * \ \ . c l a s s " ) )
p rin t(th is C la s s (B in a ry F ile .re a d (k la s s )));
}
} lll--~
lako ovde ne m ožem o ulaziti u sve pojedinosti, svaka class datoteka im a odredeni for-
m at, a ja sam parčićim a podataka izvadenih iz toka B yteA rray In p u tS tream pokušao da
dam smislena imena. Veličinu svakog parčeta možete saznati na osnovu dužine učitanog
ulaznog toka. Prim era radi, prva 32 bita svake class datoteke uvek zauzim a „magični broj“
Poglavlje 20: Anotacije 881
Oxcafebabe,10 a sledeća dva shorta su inform acija o verziji. Skladište (engl. pool)
konstanti sadrži konstante za program i zato je prom enljive veličine; sledeći short kazuje
koliki je, da bi m u mogao biti dodeljen niz odgovarajuće veličine. Svaka stavka skladišta
konstanti može biti vrednost fiksne ili prom enljive veličine, pa m oram o da ispitam o
oznaku kojom stavka počinje da bism o shvatili šta s njom da radim o - to je naredba
switch. Ovde nism o pokušavaii da tačno analiziram o sve podatke u class datoteci, nego
sam o da prođem o kroz sve važne parčiće i da ih uskladištim o, pa se nem ojte čuditi što se
dobar deo podataka odbacuje. Inform acije o klasam a skladište se u tabeli className-
Table i tabeli offsetTable. Nakon učitavanja skladišta konstanti m ože se pronaći inform a-
cija this_dass, što je indeks tabele offsetTable koja proizvodi indeks za classNameTable
koja proizvodi ime klase.
V ratim o se program u AtUnit.java. M etoda pro cess( ) sada im a ime klase i m ože u njoj
p o tra ž iti., što znači da je deo nekog paketa. Klase izvan paketa se zanem aruju. Ukoliko je
klasa u paketu, standardni učitavač klasa učitava klasu m etodom Class.forN am e( ). Sada
se u klasi m ogu potražiti @Unit anotacije.
Tražim o samo tri stvari: @Test m etode koje su uskladištene u listi TestMethods i me-
tode označene sa @TestObjectCreate ili @TestObjectCIeanup. Njih pronalazim o tako
što pozivam o pridružene m etode koje traže anotacije.
Ako se pronađe neka @Test m etoda, ime odgovarajuće klase se štam pa da bi posm a-
trač znao šta se događa i zatim se izvršava svaki test. To znači da se štam pa ime metode,
zatim poziva createTestO bject( ) koja će pozvati m etodu @TestObjectCreate ako takva
postoji ili će u protivnom pozvati podrazum evani konstruktor. Nakon pravljcnja test
objekta poziva se m etoda koja ga testira. Ako test vrati vrednost tipa boolean, taj rezultat
se pam ti. U suprotnom , pretpostavljam o da je test uspešan ukoliko se nije pojavio
izuzetak (što bi se desilo u slučaju neuspešne naredbe assert ili bilo koje druge vrste izu-
zetka). Ukoliko izuzetak bude generisan, štam paju se pripadajuće inform acije da bi
korisnik video uzrok. U svim slučajevima neuspeha (engl. failure) povećava se sadržaj
brojača neuspeha, a ime neuspešne klase i m etode dodaju se listi failedTests da bi bile pri-
javljene na kraju testiranja.
Vežba 11: (5) Alatki @Unit dodajte anotaciju @TestNote; ona označava prateće napom e-
ne (engl. notes) koje se ispisuju tokom testiranja.
/ / : n e t / m in d v ie w / a t u n i t / A t U n it R e n io v e r . ja v a
/ / P r ik a z u je @Unit a n o t a c i j e u prevednim datotekama k la s a . Ako j e
/ / p r v i argument " - r " , @Unit a n o t a c i j e će b i t i u k lo n je n e .
/ / { A rg s: . . }
/ / {Neophodno za r a d : j a v a s s i s t . b y t e c o d e . C l a s s F i l e ;
/ / Morate i n s t a l i r a t i b i b l i o t e k u J a v a s s i s t sa
/ / h ttp ://s o u rc e fo rg e .n e t/p ro je c ts /jb o s s / }
package n e t . m i n d v i e w . a t u n i t ;
im p o r t j a v a s s i s t . * ;
im p o rt j a v a s s i s t . e x p r . * ;
im p o r t j a v a s s i s t . b y t e c o d e . * ;
im p o r t j a v a s s i s t . b y t e c o d e . a n n o t a t i o n . * ;
im p o r t j a v a . i o . * ;
im p o rt j a v a . u t i l . * ;
im p o r t n e t . m i n d v i e w . u t i l . * ;
im p o rt s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;
p u b l i c c la s s AtUnitRemover
implements P r o c e s s F ile s . S t r a t e g y {
p r i v a t e s t a t i c boolean u k lo n i = f a l s e ;
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws Exce p tio n {
i f ( a r g s . l e n g t h > 0 && a r g s , e q u a l s ( “ - r " ) ) {
u k lo n i = t r u e ;
S t r i n g [ ] b r o j a r g = new S t r i n g [ a r g s . l e n g t h - 1 ] ;
S y s te m .a rr a y c o p y ( a rg s , 1, b r o j a r g , 0, b r o j a r g . 1e n g t h ) ;
args = b r o j a r g ;
}
new P ro c e s s F i1e s (
new A tU n itR e m o v e r( ) , " c l a s s " ) . s t a r t ( a r g s ) ;
}
p u b l i c v o id p r o c e s s ( F i l e cDatka) {
boolean m o d if ik o v a n a = f a l s e ;
try {
S t r i n g clme = C la s sN a m e F in d e r.th is C la s s(
B i n a r y F i 1e . r e a d ( c D a t k a ) ) ;
if(!c Im e .c o n ta in s ("."))
r e t u r n ; / / Klase iz v a n paketa se zanemaruju
ClassPool cPool = C l a s s P o o l . g e t D e f a u l t ( ) ;
CtClass c t C la s s = c P o o l . g e t ( c l m e ) ;
fo r(C tM e th o d metoda : c t C l a s s . g e tD e c l a r e d M e t h o d s f ) ) {
M eth o d ln fo mi = m e to d a .g e tM e th o d ln fo O ;
A n n o ta tio n s A ttrib u te a t t r = (A n n o ta tio n s A ttrib u te )
m i.g e tA ttrib u te (A n n o ta tio n s A ttrib u te . vi s ib le T a g );
i f ( a t t r == n u l l ) c o n t in u e ;
f o r ( A n n o t a t i o n ann : a t t r . g e t A n n o t a t i o n s O ) {
if (ann.getTypeNam e()
Poglavjje 20: Anotacije 883
Sažetak
Anotacije su dobrodošao dodatak Javi. O ne su stru k tu riran način dodavanja m etapoda-
taka kodu, zajemčeno bezbedan u pogledu tipova, a pri tom kod ne čini nečitljivim i za-
petljanim . O ne mogu pom oći da se smanji dosadno pisanje deskriptora prim ene i drugih
Isto važi i za program iranje grafičkih korisničkih okruženja, kao što ćete videti u sledećem
poglavlju. Iako biblioteke Swing i SWT im aju m ehanizm e za bezbednost niti, teško je shva-
titi kako da ih ispravno upotrebljavate ako ne razum ete paralelno izvršavanje.
Java je višenitni jezik i problem i s paralelnim izvršavanjem postoje, znali vi to ili ne.
Zato postoji m nogo Java program a koji rade ili slučajnim sticajem okolnosti ili tek veći
deo vrem ena, i povrem eno m isteriozno zakazuju zbog neotkrivenih grešaka u para-
lelnom izvršavanju. Katkada su ta zakazivanja dobroćudna, ali um eju da prouzrokuju
gubljenje vrednih podataka, i ako barem niste čuli za problem e s paralelnim izvršava-
njem, mislićete da je problem u svemu pre nego u vašem softveru. Ta vrsta problem a
m ože izroniti ili se pojačati kada se program prem esti na višeprocesorski računar. Nakon
što proučite ovo poglavlje, trebalo bi da znate kako prividno ispravni program i mogu
zbog paralelnog izvršavanja da ispolje neispravno ponašanje.
Kada počnete da se bavite paralelnim program iranjem , kao da ste otišli u stranu zemlju
da učite njen jezik ili barem p o tp u n o nov skup jezičkih pojm ova. Naučiti paralelno pro-
gram iranje jednako je zahtevno kao naučiti objektno orijentisano program iranje. Ako se
potrudite, shvatićete osnovni m ehanizam , ali za pravo ovladavanje tim predm etom po
pravilu je potrebno dobro zagrejati stolicu. Iz ovog poglavlja steći ćete osnovno znanje o
paralelnom izvršavanju, pa ćete razum eti pojmove i moći ćete da pišete um ereno složene
višenitne program e. Budite svesni da je veoma lako steći neosnovano veliko poverenje u
svoje sposobnosti za paralelno program iranje. Moraćete da pročitate knjige posvećene
isključivo toj temi ukoliko se odlučite za pisanje iole složenijih višenitnih programa.
Brže izvršavanje
Spočetka problem zvuči jednostavno: ako želite da se program izvršava brže, podelite ga na
niti koje izvršavaju zasebni procesori. Paralelno izvršavanje je osnovna alatka višeproce-
sorskog program iranja. Pošto M urov zakon sve ređe deluje (barem za obična integrisana
kola), sredstvo za povećanje brzine postaju procesori s više jezgara, a ne brža integrisana
kola. Da biste naterali program e da rade brže, m oraćete naučiti da iskoristite te dodatne
procesore, i to je jedna od stvari koje će vam paralelno izvršavanje omogućiti.
Ako imate računar s više procesora, njim a se može dodeliti više zadataka (na isto-
vrem eno izvršavanje), što um e znatno da poboljša protok. Tako često rade snažni više-
procesorski Web serveri; oni procesorim a raspoređuju veliki broj korisničkih zahteva u
program u koji dodeljuje jednu nit po zahtevu.
Poglavlje 21: Paraielno izvršavanje 887
Evo jednostavnog prim era procesa u operativnom sistemu. D ok pišem knjigu, redov-
no pravim više redundantnih rezervnih kopija tekućeg stanja knjige. Jednu kopiju napra-
vim u lokalnom direktorijum u, dru gu na fleš uređaju, treću na Zip disku i četvrtu na
udaljenoj FTP lokaciji. Za autom atizaciju tog po stu p k a napisao sam m ali program (na
Pythonu, ali koncepti su isti) koji kom prim uje knjigu u datoteku čije im e sadrži broj ver-
zije i zatim kopira. Spočetka sam sve kopije pravio sekvencijalno (čekao da se prethodno
kopiranje završi pa da otpočnem sledeće). Potom sam shvatio da operacije kopiranja ne
traju jednako, jer se U/I brzine m edijum a razlikuju. Pošto sam im ao višeprogram ski
operativni sistem, svaku operaciju kopiranja m ogao sam da započnem kao zaseban pro-
ces i pustim ih da se izvršavaju paralelno - tako se ubrzavalo izvršavanje celog program a.
D ok je jedan proces blokiran, drugi m ogu da rade.
To je idealan prim er paralelnog izvršavanja. Svaki zadatak se izvršava kao proces u
sopstvenom adresnom prostoru, p a zadaci ne m ogu da utiču jedni na druge. Još je važnije
to što zadaci nem aju potrebe da kom uniciraju, pošto su p o tp u n o nezavisni. O perativni
sistem se stara o svim pojedinostim a koje obezbeđuju ispravno kopiranje datoteka. Stoga
nem a rizika i dobijam o brži program , zapravo besplatno.
Neki zagovaraju procese kao jedini razum an p ristu p paralelnom izvršavanju,1 ali je
nažalost broj procesa po pravilu ograničen, a njihovi režijski troškovi veliki. Zato se pri-
m enom procesa ne mogu rešiti svi zadaci iz spektra paralelnog izvršavanja.
Neki program ski jezici m eđusobno izoluju zadatke koji se izvršavaju paralelno.
O bično ih nazivamo fu n kcijski jezici, jer u njim a poziv funkcije ne prouzrokuje sporedne
uticaje (te ne može ni da utiče na druge funkcije) i stoga se m ože izvršavati kao zaseban
zadatak. Jedan od takvih jezika je Erlang, a on obuhvata m ehanizam sefa za kom unikaciju
jednog zadatka s drugim. Ukoliko shvatite da u delu vašeg program a m ora intenzivno da
se koristi paralelno izvršavanje, a pri pisanju tog dela nailazite na velike problem e, raz-
mislite o ideji da taj deo program a napišete na nam enskom jeziku za paralelno izvrša-
vanje kao što je Erlang.
U Javi je pristup tradicionalniji - podrška za višenitni rad dodata je na gotov
sekvencijalni jezik.2 Umesto da se ,,love“ spoljni procesi u višeprogram skom operativnom
sistemu, niti prave zadatke u n u ta r istog procesa koji predstavlja program koji se izvršava.
Jedna od prednosti koje se pri tom e postižu jeste nezavisnost od operativnog sistema, što
je bio važan cilj prilikom projektovanja Jave. Na prim er, pre verzije OS X operativnog
sistema M acintosh (prilično važna publika za prve verzije Jave) nisu podržavale višepro-
gramski rad. Da višenitni rad nije dodat u Javu, Java program i za paralelno izvršavanje ne
bi mogli da se izvršavaju na M acintoshu i njem u sličnim platform am a, čim e bi se narušio
zahtev „napiši jednom/izvršavaj svuda“.3
Definisanje zadataka
Nit izvršava jedan zadatak, pa m oram o imati načina da taj zadatak opišemo. Tome služi
interfejs R unnable. Zadatak ćete đefinisati kada realizujete R unnable i napišete rnetodu
r u n ( ) koja obavlja ono što zadatak treba da uradi.
Prim era radi, naredni zadatak L ansiranje prikazuje odbrojavanje pre lansiranja:
/ / : p a ra le ln o /L a n s ira n je .ja v a
/ / Prim er i n t e r f e j s a Runnable.
p u b l i c c la s s L a n s ir a n je implements Runnable {
p ro t e c t e d i n t o d b ro ja v a n je = 10; / / Podrazumevana
p r i v a t e s t a t i c i n t b ro jZ a d a tk a = 0;
p r i v a t e f i n a l i n t id = bro jZ a d a tk a + + ;
p u b l i c L a n s i r a n j e ( ) {)
p u b l i c L a n s i r a n j e ( i n t o d b ro ja v a n je ) {
t h i s . o d b ro ja v a n je = o d b r o ja v a n je ;
}
p u b lic S trin g s ta tu s () {
r e t u r n "#" + i d + " ( " +
( o d b r o ja v a n je > 0 ? o d b ro ja v a n je : " L a n s i r a n j e ! ") + " ) , ";
}
p u b l i c v o iđ r u n ( ) {
w h i l e ( o d b r o j a v a n j e - - > 0) {
S y s te m .o u t.p rin t(s ta tu s ());
T h re a d .y ie ld ();
}
}
} ///:-
Instance zadatka se ra/.likuju po pripadajućem identifikatoru id. Uz njega je rezervisa-
na reč final zato što ne očekujem o da će se p rom eniti nakon što bude inicijalizovan.
M etoda r u n ( ) obično im a neku vrstu petlje koja se izvršava sve dok zadatak ne posta-
ne nepotreban, pa m orate da utvrdite uslov za izlazak iz nje. Jedna m ogućnost bi bila da
jednostavno izađete (vratite se) iz m etode r u n ( ). M etodu r u n ( ) često pišu u obliku bes-
konačne petlje, što znači da će se ona izvršavati u nedogled ako je nešto ne prekine. (U na-
stavku poglavlja saznaćete kako da bezbedno okončate zadatak.)
Poziv statičke m etode T hread.yield( ) u n u tar m etode r u n ( ) predstavlja sugestiju
m eh a n izm u za raspodelu procesorskog vrem ena nitim a , engl. thread schedular (koji preba-
cuje procesor s jedne niti na drugu) koja kazuje: „Obavio sam važne delove svog ciklusa i
sada je vrem e za prebacivanje na drugi zadatak“. M etoda je neobavezna (opciona), a ovde
sam je upotrebio zato što u navedenim prim erim a daje zanimljivije rezultate: ona poveća-
va verovatnoću da će doći do prebacivanja s jednog zadatka na drugi.
U narednom prim eru, zadatkovu m etodu r u n ( ) ne izvršava zasebna nit; nju jedno-
stavno poziva m etoda m a in ( ). Zapravo, to jeste zasebna nit: ona koja se uvek dodeljuje
metodi m a in ( ):
//: p a ra le ln o /G la v n a N it.ja v a
p u b l i c c la s s G la vn a N it {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
L a n s ir a n je l a n s i r a j = new L a n s ir a n j e ( ) ;
1ansi r a j . r u n ( ) ;
}
} / * Is p is :
# 0 (9 ), # 0 (8 ), # 0(7), # 0(6), #0(5), # 0 (4 ), # 0 (3 ), # 0 (2 ), # 0 ( 0 ,
#0(Lansi r a n j e ! ) ,
* ///:-
Klasa Thread
Runnable objekat se pretvara u zadatak koji se izvršava najčešće tako što objekat
prosledite konstruktoru klase Thread. U sledećem prim eru ćem o pom oću klase Thread
pokrenuti izvršavanje objekta tipa Lansiranje:
892 Misliti na Javi
//: p a ra le ln o /O s n o v e N iti.ja v a
/ / N a j j e d n o s t a v n i j a u p o tr e b a k la s e Thread.
p u b l i c c la s s O snoveN iti {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) {
Thread t = new Thread(new L a n s i r a n j e O ) ;
t.s ta rtO ;
S y s te m . o u t . p r in t ln ( " Č e k a m o na L a n s i r a n j e " ) ;
}
} / * I s p i s : (90% podudaranja)
Čekamo na L a n s ir a n je
# 0 (9 ), # 0 (8 ), # 0 (7 ), # 0 (6 ), # 0 (5 ), # 0 (4 ), # 0 (3 ), # 0 (2 ), # 0(1),
# 0 (L a n s ira n je !),
* ///:-
K onstruktoru klase Thread treba sam o objekat tipa Runnable. N it se inicijali/.uje po-
zivom m etode s ta rt( ) objekta tipa Thread, a zatim se pozivom m etode r u n ( ) objekta tipa
Runnable pokreće zadatak u novoj niti. Mada izgleda kao da s ta r t( ) poziva neku m etodu
koja se izvršava dugo, iz ispisa rezultata možete videti - poruka „Čekamo na Lansiranje“
ispisuje se pre dovršenja odbrojavanja - da se m etoda s ta r t( ) vraća brzo. U stvari, pozvana
je m etoda L ansiranje.run( ) koja se još nije završila, ali pošto se Lansiranje.run( ) izvršava
u drugoj niti, u metodi m a in ( ) i dalje m ožem o da izvršavamo ostale operacije. (Ta spo-
sobnost nije ograničena na nit m etode m a in ( ) - svaka nit m ože da pokrene druge niti.)
DakJe, program izvršava dve m etode istovrem eno - m a in ( ) i Lansiranje.run( ). Kod m e -
tode r u n ( ) izvršava se istovrem eno sa ostalim nitim a program a.
Lako je dodati još niti za izvršavanje drugih zadataka. U narednom prim eru vidite sve
zadatke u uzajam no usklađenom izvršavanju:5
/ / : p a r a l e l n o / J o s O s n o v a N i t i . ja v a
/ / Dodavanje n i t i .
p u b l i c c la s s JosO sn ovaNiti {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
f o r ( i n t i = 0 ; i < 5; i+ + )
new Thread(new L a n s i r a n j e ( ) ) . s t a r t ( ) ;
S y s t e m . o u t . p r ir t ln ( " Č e k a m o na L a n s i r a n j e " ) ;
}
} / * I s p i s : ( p r im e r)
Čekamo na L a n s ir a n je
# 0 (9 ), # 1 (9 ), # 2 (9 ), # 3 (9 ), # 4 (9 ), # 0 (8 ), # 1 (8 ), # 2(8), # 3 (8 ), #4(8), #0(7),
# 1 ( 7 ) , # 2 ( 7 ) , # 3 ( 7 ) , # 4 ( 7 ) , # 0 ( 6 ) , # 1 ( 6 ) , # 2 ( 6 ) , # 3 ( 6 ) , # 4 ( 6 ) , # 0 ( 5 ) , # 1 (5 ) ,
# 2 (5 ), # 3 (5 ), # 4(5), # 0 (4 ), # 1 (4 ), # 2 (4 ), # 3 (4 ), # 4 (4 ), # 0 (3 ), # 1 (3 ), #2(3),
# 3 (3 ), # 4 (3 ), # 0(2), # 1 (2 ), # 2 (2 ), # 3 (2 ), # 4 (2 ), # 0(1), # 1 (1 ), #2(1), # 3 (1 ),
# 4 ( 1 ) , # 0 ( L a n s ir a n j e ! ) , # 1 ( L a n s i r a n j e ! ) , # 2 ( L a n s i r a n j e ! ) , # 3 ( L a n s i r a n j e ! ) ,
# 4(L ansi r a n j e ! ) ,
* ///:-
prim cru, klasa C achedT hreadP ool pravi po jed n u nit za svaki zadatak. O bratite pažnju
na to da se objekat tipa ExecutorService pravi statičkom m etodom klase Executors; ona
određuje koja će vrsta izvršioca biti napravljena:
/ / : p a ra le ln o /C a c h e d T h re a d P o o l. ja v a
im p o r t j a v a . u t i l . c o n c u r r e n t . * ;
p u b l i c c la s s CachedThreadPool {
p u b l i c s t a t l c vo id m a i n ( S t r i n g [ ] a rg s ) {
E x e cu to rS e rv ic e exec = Executors.newCach edThreadPool( ) ;
f o r ( i n t i = 0 ; i < 5 ; i+ +)
exe c.execute(new L a n s i r a n j e ( ) ) ;
exec.shutdown();
}
} / * I s p i s : ( p r im e r)
# 0 (9 ), # 0 (8 ), # 1 (9 ), # 2 (9 ), # 3 (9 ), # 4 (9 ), # 0 (7 ), # 1 (8 ), # 2 (8 ), # 3 (8 ), # 4(8),
# 0 (6 ), # 1 (7 ), # 2 (7 ), # 3 (7 ), # 4 (7 ), # 0 (5 ), # 1 (6 ), # 2 (6 ), # 3 (6 ), # 4 (6 ), # 0(4),
# 1 (5 ), # 2 (5 ), # 3 (5 ), # 4 (5 ), # 0 (3 ), # 1 (4 ), # 2 (4 ), # 3 (4 ), # 4(4), # 0 (2 ), # 1 (3 ),
# 2(3), # 3 (3 ), # 4 (3 ), # 0 (1 ), # 1 (2 ), # 2 (2 ), # 3 (2 ), # 4 (2 ), # 0 (L a n s ir a n je !) ,
# 1(1), #2(1), # 3 (1 ), # 4 (1 ), # l ( L a n s i r a n j e ! ) , # 2 (L a n s ira n je !) ,
# 3 (L a n s ira n je !), # 4 (L a n s ira n je !) ,
* ///:-
Veoma često, sam o jednim izvršiocem m ožete da napravite sve zadatke u sistemu i da
svima njim a upravljate.
Poziv m etode s h u td o w n () sprečava dodeljivanje novih zadataka tom izvršiocu. Te-
kuća nit - u ovom slučaju, ona koja izvršava m a in ( ) - nastaviće da izvršava sve zadatke
prijavljene pre poziva m etode s h u td o w n ( ). Program će prekinuti izvršavanje čim se
završe svi zadaci u tom izvršiocu.
C achedT hreadP ool iz prethodnog prim era lako m ožete zam eniti nekom drugom vr-
stom izvršioca. Za izvršavanje prijavljenih zadataka, F ixedT hreadPool koristi ograničen
skup niti:
/ / : p a r a le ln o / F i x e d T h r e a d P o o l. j a v a
im p o rt j a v a . u t i l . c o n c u r r e n t . * ;
p u b l i c c la s s FixedThreadPool {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) {
/ / Argument k o n s t r u k t o r a j e b r o j n i t i :
E x e c u to rS e rv ic e exec = E x e c u to rs.n e w F ix e d T h rea d P oo l( 5 ) ;
f o r ( i n t i = 0; i < 5; i++)
e xec.execute(new L a n s i r a n j e O ) ;
exec.shutdow n();
}
} / * Is p is : ( p r im e r)
Poglavlje 21: Paralelno izvršavanje 895
}
} / * Is p is :
# 0 (9 ), # 0(8), #0(7), # 0 (6 ), # 0 (5 ), # 0 (4 ), # 0 (3 ), # 0 (2 ), # 0 (1 ),
# 0 (L a n s ir a n je !) , # 1(9), # 1 (8 ), # 1 (7 ), # 1 (6 ), # 1 (5 ), # 1 (4 ), # 1 (3 ), # 1(2),
#1(1), # l( L a n s ir a n je !) , # 2 (9 ), # 2 (8 ), # 2 (7 ), # 2 (6 ), # 2 (5 ), # 2 (4 ), # 2(3),
# 2 (2 ), # 2(1), # 2 ( L a n s ir a n je !) , # 3 (9 ), # 3 (8 ), # 3 (7 ), # 3 (6 ), # 3 (5 ), # 3(4),
# 3 (3 ), # 3(2), #3(1), # 3 ( L a n s ir a n je ! ) , # 4 (9 ), # 4 (8 ), # 4 (7 ), # 4 (6 ), #4(5),
# 4(4), # 4(3), # 4(2), # 4 (1 ), # 4 (L a n s ir a n je !) ,
* ///:-
Kao drugi primer, pretpostavim o da više niti izvršava zadatke koji koriste sistem
datoteka. Za te zadatke m ožete u potrebiti izvršilac tipa SingleThreadExecutor koji jem či
da se u svakom trenutku u svakoj niti izvršava sam o po jedan zadatak. U tom slučaju ne
m orate da se bavite sinhronizovanjem deljenog resursa (a ni sistem datoteka nećete
zabrljati u m eduvrem enu). Ponekad je bolje rešenje sinhronizovanje resursa (o čem u ćete
više saznati u nastavku poglavlja), ali SingleThreadExecutor om ogućuje da preskoćite
pravilnu koordinaciju kada želite sam o da napravite neki prototip. Serijalizacijom
zadataka možete da uklonite potrebu za serijalizacijom objekata.
Vežba3: (1) Ponovite vežbu 1 s raznim vrstam a izvršilaca predstavljenim u ovom odeljku.
Vežba 4: ( 1) Ponovite vežbu 2 s raznim vrstam a izvršilaca predstavljenim u ovom odeljku.
/ / : p a r a l e l no/Prim erZaCal1a b l e . j a v a
im p o rt j a v a . u t i l . c o n c u r r e n t . * ;
im p o rt j a v a . u t i l . * ;
p u b l i c c la s s PrimerZaCal1a b le {
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) {
E x e cu to rS e rv ic e exec = Executors.newCachedThreadPool( ) ;
A rra y L is t< F u tu re < S trin g » r e z u lt a t i =
Poglavlje 2 1: Paralelno izvršavanje 897
M etoda s u b m it( ) proizvodi objekat tipa Future, param etrizovan za određeni tip re-
zultata koji vraća taj objekat tipa Callable. O bjekat tipa Future možete ispitivati meto-
dom isD o n e( ) kako biste videli da li je završio rad. Kada zadatak završi s radom i ima
rezultat, pozovite m etodu g e t( ) da biste ga saznali. Ukoliko g e t( ) pozovete bez prethod-
ne provere završenosti m etodom isD o ne( ), g e t( ) će se blokirati sve dok rezultat ne bude
sprem an. g e t( ) m ožete pozvati i uz vrem ensko odlaganje (engl. tim eout), ili pozovite
isD o n e( ) kako biste videli da li je zadatak završio s radom pre nego što pozovete g e t( ) da
pribavi njegov rezultat.
Preklopljena m etoda Executors.caIlable( ) prim a objekat koji realizuje interfejs
Runnable i proizvodi objekat tipa Callable. ExecutorService ima ,,pozivajuće“ m etode
koje izvršavaju kolekcije Callable objekata.
Vežba 5: (2) Izmenite vežbu 2 tako da zadatak realizuje interfejs Callable i sabira sve Fi-
bonaccijeve brojeve. Napravite nekoliko zadataka i prikažite rezultate.
Spavanje
M etodom sleep ( ) prekidate (blokirate) izvršavanje zadatka na odredeno vreme. Ako u
klasi Lansiranje zam enite poziv m etode y ie ld ( ) pozivom m etode sleep( ), dobićete ovo:
898 Misliti na Javi
/ / : p a r a le ln o / Z a d a t a k K o jiS p a v a . ja v a
/ / Prekidam iz v r š a v a n je zadatka
/ / na određeno vreme metodom s l e e p ( ) .
im p o rt j a v a . u t i l . c o n c u r r e n t . * ;
Prioritet
P rioritet niti pokazuje koliko je ona važna m ehanizm u za raspodelu procesorskog vrem e-
na. Iako je redosled kojim CPU izvršava skup niti neodređen, m ehanizam za raspodelu
procesorskog vrem ena daje prednost niti najvišeg p rioriteta koja čeka na izvršavanje. To
ne znači da se niti nižeg prioriteta uopšte ne izvršavaju. (Dakle, prioriteti ne m ogu da
prouzrokuju zastoj u izvršavanju). M ehanizam za raspodelu procesorskog vrem ena ređe
pokreće niti nižeg prioriteta i to je sva razlika.
Gotovo uvek bi sve niti trebalo da se izvršavaju s podrazum evanim prioritetom . Ručno
m enjanje prioriteta najčešće nije opravdano.
Sledi prim er s različitim nivoim a prioriteta. Prioritet postojeće niti čitam o m etodom
g e tP rio rity ( ), a zadajemo (kad god poželim o) m etodom setP rio rity ( ).
/ / : p a r a l e l n o / P r o s t i P r i o r i t e t i . ja v a
/ / Prim e r k o r i š ć e n ja p r i o r i t e t a n i t i .
im p o r t j a v a . u t i l . c o n c u r r e n t . * ;
p u b l i c c la s s P r o s t i P r i o r i t e t i implements Runnable {
p r i v a t e i n t o d b ro ja v a n je = 5;
p r i v a t e v o l a t i l e double d; / / Bez o p t i m i z a c i j e
p riv a te in t p r i o r it e t ;
p u b lic P r o s t i P r i o r i t e t i ( i nt p r i o r i t e t ) {
th is .p rio rite t = p rio rite t;
}
p u b lic S trin g to S tr in g ( ) {
r e t u r n T h r e a d .c u r r e n t T h re a d () + " : " + o d b r o ja v a n je ;
}
p u b l i c v o id r u n ( ) {
T h re a d .c u rre n tT h re a d ().s e tP rio rity (p rio rite t);
w h ile (tru e ) {
/ / Skupa o p e r a c ija koja se može p r e k i n u t i :
f o r ( i n t i = 1; i < 100000; i+ + ) {
d += (Math.PI + Math.E) / ( d o u b l e ) i ;
i f ( i % 1000 == 0)
Thread, y i e l d ( ) ;
}
S y s te m .o u t.p rin tln (th i s ) ;
i f ( - - o d b r o j a v a n j e == 0) r e t u r n ;
}
}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) {
900 Misliti na Javi
* ///:-
(onih 10 na ovih 7) neodređeno. Sunov Solaris im a 231nivoa. Jedini pristup koji je prenosiv
na sve platform e jeste držati se MAX_PRIORITY, NORM_PRIORITY i MIN_PRIORITY
prilikom podešavanja nivoa prioriteta.
Prepuštanje
Ukoliko znate da ste uradili ono što je trebalo tokom jednog prolaska kroz petlju u m etodi
r u n ( ), m ehanizm u za raspođelu procesorskog vrem ena nitim a m ožete saopštiti da je te-
kuća n it uradila dovoljno i predložiti m u da procesor dodeli nekom drugom zadatku. Taj
predlog (to i jeste sam o predlog, pošto nem a jem stva da će ga realizacija poslušati) im a
oblik m etode y ie ld ( ). Kada pozovete y ie ld ( ), predlažete prelazak na izvršavanje drugih
niti istog prioriteta.
Lansiranje.java m etodom y ield ( ) dobro raspodeljuje obradu na razne zadatke progra-
m a Lansiranje. U m etodi Lansiranje. r u n ( ) pozive Thread.yield( ) pretvorite u kom en-
tare, pa ćete videti razliku. M eđutim , za ozbiljnu kontrolu ili podešavanje aplikacije po
pravilu se ne možete osloniti na y ield ( ). Šta više, često se y ield( ) pogrešno upotrebljava.
Servisne niti
Servisne niti (engl. daem on threads) obezbeđuju neke usluge nam enjene program u u ce-
lini i izvršavaju se u pozadini tokom rada program a, ali ne spadaju u njegove suštinske
delove. Zato se program završava čim završe s radom sve obične niti. Važi i obratno: ako
još ima običnih niti koje se izvršavaju, program nastavlja rad - na prim er, ako postoji nit
koja izvršava m a in ( ).
//: paralelno/JednostavneServisneNiti.java
// Serv is ne niti ne sprečavaju da se program okonča.
import ja v a . u t i 1 .concurrent
import static n e t .mindview.uti1 .P r i n t .*;
*///:-
Pre pokretanja m etodom s ta r t( ), nit m orate m etodom setD aem on( ) najpre pretvo-
riti u servisnu (naterati je da se izvršava u pozadini).
Program nem a šta da radi kada m a in ( ) završi svoj posao, jer se tada ne izvršava ništa
drugo sem servisnih niti. Stavio sam m a in ( ) nakratko na spavanje da biste videli posle-
dice pokretanja svih servisnih niti. Bez toga biste videli samo neke rezultate pravljenja niti
koje se izvršavaju u pozadini. (Isprobajte pozive m etode sleep ( ) raznih dužina da biste vi-
deli to ponašanje.)
Program JednostavneServisneNiti.java pravi eksplicitne Thread olijekte kako bi
mogao da ih pretvori u servisne postavljanjem odgovarajućeg indikatora. A tribute niti
(da li je servisna, njen prioritet, ime) napravljenih pom oću izvršioca možete prilagoditi
pisanjem nam enske klase ThreadFactory:
Jedina razlika od obične ThreadFactory (proizvodne niti) jeste to što ova zadaje
vrednost tru e za status servisne niti. Nov objekat tipa D aemonThreadFactory sada
m ožem o proslediti kao argum ent m etodi Executors.newCachedThreadPool( ):
//: paralelno/DaemonThreadFactory.java
// Pravljenje servisnih niti pomoću ThreadFactory.
import java.uti1 .concurrent.*;
import net.mindview.util.*;
Poglavlj'e 2 1: Paralelno izvršavanje 903
A rgum ente za poziv konstruktora osnovne klase pronašao sam u izvornom kodu klase
Executors.java.
Pozivanjem m etode isD aem on( ) saznaćete da li je data nit servisna (da li se izvršava u
pozadini). Ako se neka nit izvršava u pozadini, to autom atski važi i za sve niti koje ona na-
pravi, kao što se vidi iz sledećeg prim era:
//: paralelno/ServisneNiti.java
// Servisna nit rađa druge servisne niti.
904 Misliti na Javi
import java.util.concurrent.*;
import static net.mindview.util.Print.*;
ServisnaNit uključuje svoj indikator servisne niti i zatim pravi gom ilu drugih niti
kako bi pokazala da su i one servisne, iako nisn eksplicitno pretvorene u servisne. Zatim
ServisnaNit ide u beskonačnu petlju koja poziva m etodu y ield ( ) da bi predala kontrolu
drugim procesima.
Im ajte u vidu činjenicu da servisne niti ne izvršavaju odredbe finally prilikom
okončanja svojih m etoda r u n ( ):
//: pa ra le lno/ServisneNitiNeIzvrsavajuOdređbeFinally.java
// Servisne niti ne izvršavaju odredbe finally
import java.util.concurrent.*;
import static ne t. mi nd vi ew .u ti1 . P r i n t .*;
Kada pokrenete ovaj program , videćete da se odredba finally ne izvršava, ali ako poziv
m etode s e tD a e m o n () pretvorite u kom entar, videćete da se odredba finally izvršava.
Takvo ponašanje je ispravno, iako ga m ožda ne očekujete na osnovu prethodnih
obećanja u vezi sa odredbom finally. Servisne niti bivaju naglo prekinute kada prestane
izvršavanje poslednje neservisne niti. Dakle, čim m a in ( ) izade, JVM odm ah prekida rad
svih servisnih niti, bez ikakvih form alnosti koje ste m ožda očekivali. Pošto se servisne niti
ne m ogu lepo prekinuti, one su retko kada podesne. Po pravilu su neservisni izvršioci
bolji, pošto se svi zadaci koje izvršilac kontroliše m ogu prekinuti odjednom . Kao što ćete
videti u nastavku poglavlja, u tom slučaju se gašenje odvija pravilno.
Vežba 7: (2) Eksperim entišite s različitim vrem enim a spavanja u program u Servisne-
N iti.java da biste videli šta se dešava.
906 Misliti na Javi
Varijante programiranja
U dosadašnjim prim erim a su sve klase zadataka realizovale interfejs Runnable. U veoma
jednostavnim slučajevima m oguč je i drugačiji pristup, nasleđivanje neposredno od klase
Thread, što se radi ovako:
//: paralelno/JednostavnaNit.java
// Neposredno nasleđivanje od klase Thread.
Nitima (objektima klase T h read ) dajemo imena tako što pozivamo odgovarajuće T hread
konstruktore. To ime ispisuje to S trin g () nakon što ga dobije od m etode getN am e().
M ožda ćete sresti i idiom objekta tipa R unnable koji sam sobom upravlja:
//: paralelno/Samoupravno.java
// Objekat tipa Runnable koji sadrži sopstvenu nit koja ga izvršava.
Poglavlje 2 1: Paralelno izvršavanje 907
Ovo se ne razlikuje m nogo od nasleđivanja od klase Thread, sem što je sintaksa nešto
neobičnija. M eđutim , realizacija interfejsa om ogućuje nasledivanje od druge kiase, dok
nasledivanje od klase Thread to ne om ogućuje.
O bratite pažnju na to da je m etoda s ta r t( ) pozvana iz konstruktora. Prim er je krajnje
jednostavan i zato verovatno bezbedan; ipak, m orate biti svesni da pokretanje niti iz kon-
struktora um e da bude veoma problem atično, pošto bi m oglo početi izvršavanje nekog
drugog zadatka pre nego što ko nstruktor završi svoj posao, što znači da taj zadatak može
da pristupi objektu koji je u nestabilnom stanju. I zbog toga je korišćenje izvršilaca bolje
od eksplicitnog pravljenja niti (objekata tipa Thread).
Ponekad kod za pravljenje niti treba sakriti u klasi p om oću unutrašnje klase, kao što
ću sada uraditi:
//: paralelno/VarijanteNiti.java
// Pravljenje niti pomoću unutrašnjih klasa.
import java.util.concurrent.*;
import static net.mindview.uti1 .Print.*;
Unutrasnja(String ime) {
super(ime);
start();
}
public void run() {
try {
while(true) {
print(this);
if(— odbrojavanje == 0) return;
sleep(lO);
}
} catch(InterruptedException e) {
print("prekinuto");
}
}
public String toStringO {
return getName() + ": " + odbrojavanje;
}
}
public UnutrasnjaNitl(String ime) {
unutrasnja = new Unutrasnja(ime);
}
}
}, ime);
t.start();
}
}
U nutrasnjaN itl pravi im enovanu unutrašnju klasu koja nasleđuje Thread i u kon-
struktoru pravi instancu te unutrašnje klase. To ima smisla ako unutrašnja klasa ima po-
sebne sposobnosti (nove m etode) potrebne u drugim m etodam a. M edutim , nit obično
pravim o sam o zbog sposobnosti klase Thread, pa nije n eophodno da pravim o imenova-
nu unutrašnju klasu. U nutrasnjaN itž pokazuje drugačiji pristup: u konstruktoru se pravi
ano nim na unutrašnja potklasa od Thread i svodi naviše na Thread referencu t. Ukoliko
Poglavlje 2 1: Paralelno izvršavanje 911
ostalim m etodam a te klase zatreba pristup objektu na koji upućuje t, m ogu to učiniti pre-
ko interfejsa T hread i bez poznavanja stvarnog tipa tog objekta.
Treća i četvrta klasa u prim eru ponavljaju prve dve klase, ali koriste interfejse Runna-
ble, a ne klasu Thread.
Klasa M etodaNit pokazuje pravljenje niti u n u tar m etode. M etodu pozivate kada ste
sprem ni da pokrenete tu nit, i m etoda vraća svoj rezultat nakon što nit otpočne s radom .
Ukoliko nit obavlja sam o pom oćne, a ne i osnovne operacije klase, to je verovatno koris-
niji i prikladniji pristup nego što je pokretanje niti u n u tar konstruktora klase.
Vežba 10: (4) Izm enite vežbu 5 po uzoru na prim er s klasom MetodaNit, tako da m etoda
ru n T ask ( ) p rim a kao argum ent broj Fibonaccijevih brojeva koje treba sabrati, i svaki
p u t kada pozovete run T ask ( ), ona vraća objekat tipa Future koji proizvodi poziv m eto-
de s u b m it().
Terminologija
Kao što se vidi iz prethodnog odeljka, paralelni program i se u Javi m ogu pisati na više
načina i to program era m ože da zbuni. Često problem nastaje zbog term inologije kojom
je opisan program za paralelno izvršavanje, naročito tam o gde su u pitanju niti.
Dosad je trebalo da uvidite kako postoji razlika izm eđu zadatka i niti koja taj zadatak
izvršava; ta razlika je naročito jasna u Javinim bibliotekam a, pošto nad klasom Thread za-
pravo nem am o nikakvu kontrolu (a još je jasnija kada se radi o izvršiocima koji um esto
nas prave niti i upravljaju njim a). Program er piše zađatak i na neki način m u dodeljuje nit
koja će ga izvršavati.
U Javi, nit (klasa Thread) sam a po sebi ne radi ništa. O na izvršava zadatak koji joj da-
mo. Pa ipak, u literaturi o višenitnom program iranju uvek se kaže ,,nit radi ovo ili ono“.
D obija se utisak da nit je s te zadatak. Kada sam upoznao Javine niti, utisak je bio toliko jak
da sam video jasnu relaciju JE, koja mi je govorila kako zadatak očigledno treba da izve-
dem iz klase Thread. Dodajte tom e loše odabrano ime interfejsa Runnable (koji se može
izvršavati); po m om mišljenju, trebalo je da se zove Task (zadatak). Ukoliko interfejs
očigledno nije ništa više od generičkog kapsuliranja svojih m etoda, onda je im enovanje
po kalupu„m ože da uradi lo i to“ odgovarajuće, ali ako treba da izrazi viši pojam - kao što
je Zadatak - o nda m u treba dati ime tog pojma.
Problem potiče od mešanja nivoa apstrakcije. Na nivou koncepta, želimo da napravi-
m o zadatak koji se izvršava nezavisno od drugih zadataka, pa bi trebalo da je moguće de-
finisati zadatak i reći ,,radi“, bez petljanja s detaljima. Ali fizički, pravljenje niti je skupo, pa
m oram o da ih štedim o i upravljam o njim a. Zato sa stanovišta realizacije im a smisla od-
vojiti zadatke od niti. Sem toga, Javine niti su napravljene na osnovu nekakvih p-niti koje
potiču još iz C-a, a u tom jeziku program er je opkoljen detaljim a i m ora poznavati do u
sitnicu sve što se događa. Delimično je takav pristup prešao i u realizaciju Jave, pa da bi-
sm o ostali na visokom nivou apstrakcije, m oram o biti disciplinovani pri pisanju progra-
ma. (Potrudiću se da u ovom poglavlju sam pokažem tu disciplinu.)
Da bi razm atranje bilo jasnije, term inom zadatak opisivaću ono što se radi, a term i-
nom nit - m ehanizam koji izvršava zadatak. Dakle, ako sistem razm atram o na koncep-
tualnom nivou, m ožem o upotrebiti term in zadatak, a da uopšte ne spom injem o
m ehanizam izvršavanja.
912 Misliti na Javi
//: paralelno/Joining.java
// Objašnjenje metode join().
import static net.mindview.util.Print.*;
//: paralelno/BrzoKorisnickoOkruzenje.java
// Brzina reagovanja korisničkog okruženja.
// {RunByHand)
class SporoKorisnickoOkruzenje {
private volatile double d = 1;
public SporoKorisnickoOkruzenje() throws Exception {
while(d > 0)
d = d + (Math.PI + Math.E) / d;
System.in.read{); // Ovo se nikada ne izvršava
}
}
Grupe niti
G rupa niti sadrži kolekciju niti. V rednost grupa niti sažeo je Joshua Bioch,8 projektant
softvera koji je (izm edu ostalog), dok je radio za Sun, popravio i znatno poboljšao biblio-
teku Java kolekcija u JDK 1.2:
„G rupe niti je najbolje sm atrati neuspešnim eksperim entom i zaboraviti da postoje.“
Ukoliko ste (kao ja) potrošili vreme i energiju pokušavajući da shvatite vrednost grupa
niti, m ožda se pitate zašto Sun o toj tem i nikada nije dao zvanično saopštenje. Isto važi i za
brojne druge prom ene sprovedene u Javi tokom godina. Izgleda da bism o mogli prim eniti
Teoriju rastućeg angažovanjd' - njen tvorac je Joseph Stiglitz (dobitnik Nobelove nagrade).
„Cenu nastavljanja grešaka plaćaju drugi, dok cenu priznavanja grešaka plaćam o sami.“
Hvatanje izuzetaka
Izuzetak koji je pobegao iz niti ne m ožete uhvatiti - takva je njihova priroda. Kada izuze-
tak izađe iz m etode r u n ( ) zadatka, prodreće do konzole ukoliko ne preduzm ete posebne
m ere za hvatanje takvih zabludelih grešaka. Pre Jave SE5, za hvatanje takvih izuzetaka ko-
ristili sm o grupe niti, ali sada problem m ožem o rešiti izvršiocima, te o grupam a niti više
ne m oram o ništa da znam o (sem onoliko koliko je po trebno da bism o razum eli stari kod;
pojedinosti o grupam a niti pročitajte u knjizi T h in kin g in Java, 2 n“E dition, koju m ožete
preuzeti sa adrese w w w .M indV iew .net).
Sledeći zadatak uvek baca izuzetak koji izlazi iz njegove m etode r u n ( ), a m a in ( ) po-
kazuje šta se dešava kada ga pokrenete:
//: paralelno/Nitlzuzetka.java
// {ThrowsException}
import java.uti1 .concurrent.*;
java.1ang.RuntimeException
at Nitlzuzetka.run(Nitlzuzetka.java:7)
at ThreadPoolExecutor$Worker.runTask(Unknown Source)
at ThreadPoolExecutor$Worker.run(Unknown Source)
at java.1ang.Thread.run(Unknown Source)
//: paralelno/NaivnaObradalzuzetka.java
// {ThrowsException}
import java.uti1 .concurrent.*;
1 na nckoliko drugih m esta u celoj lavi. Zašto da se o g ran ičim o sam o na Javu? Bio sam k o n su ltan t u
više projekata gde se pokazalo da je ta tv rdnja tačna.
916 Misliti na Javi
//: paralelno/HvatanjeNeuhvacenihlzuzetaka.java
import java.uti1 .concurrent.
t .setllncaughtExceptionHandler(
new MojBlokZaObraduNeuhvacenihlzuzetakaO);
System.out.println(
"eh = 11 + t.getUncaughtExceptionHandler());
return t;
}
}
Ispisao sam i dodatne oznake kako biste mogli proveriti da se nitim a koje proizvodi
Factory daje novi UncaughtExceptionHandler. Možete videti da neuhvaćene izuzetke
sada hvata m etoda neuhvacenlzuzetak( ).
G ornji prim er om ogućuje da blok za obradu (engl. handler) prilagođavate svakom po-
jedinom slučaju. Ukoliko nam eravate da isti blok za obradu izuzetaka koristite svuda, još
je jednostavnije da napravite p o d ra zu m eva n i blok za obradu neuhvaćenih izuzetaka koji
postavlja odredeno statičko polje u klasi Thread:
//: paralelno/PodraBlokZaObraduNeuhvacenihlzuzetaka.java
import java.util.concurrent.*;
Ovaj program se poziva sam o u slučaju da ne postoji blok za obradu neuhvaćenih izu-
zetaka prilagoden svakoj niti. Sistem proverava postoji li verzija za konkretnu nit i ako je
ne pronađe, proverava da li ta grupa niti im a sopstvenu specijalizovanu m etodu
neuhvacenlzuzetak( ); ako nema, poziva se defau!tUncaughtExceptionHandler.
918 Misliti na Javi
Deljenje resursa
Program s jednom n id m ožete da zamislite kao usam ljenu jedinku koja se kreće kroz pro-
stor vašeg problem a i rešava jedan po jedan zadatak. Pošto postoji samo jedna jedinka,
uopšte ne m orate da vodite računa o situaciji kada dve jedinke pokušavaju da iskoriste isti
resurs istovremeno, p o p u t dvoje koji pokušavaju da se parkiraju na istom mestu, ili da
prođu kroz vrata istovrem eno ili čak da sam o govore istovremeno.
Ako za rešavanje problem a upotrebite više niti, ne javlja se problem usam ljenih jedin-
ki, ali postoji m ogućnost da dve ili više niti pokušaju da upotrebe isti ograničen resurs u
istom trenutku, tj. da se uzajam no om etu. Sukobljavanje zbog nekog resursa m ora se
sprečiti ili će dve niti istovrem eno pokušati da pristupe istom bankovnom nalogu,
odštam paju nešto na istom štam paču, okrenu isti ventil itd.
G eneratorCelobroj ima m etodu can cel( ) za prom enu stanja boolean indikatora can-
celled i m etodu isC ancelled( ) koja utvrđuje da li je pravljenje objekta otkazano. Pošto je
indikator cancelled tipa boolean, on je a to m ski (nedeljiv), što znači đa takvo polje ne
m ožete pročitati u nekom m eđustanju usred jednostavnih operacija kao što su dodelji-
vanje i vraćanje vrednosti, jer se one ne m ogu prekidati. Indikatoru cancelled dodat je i
m odifikator volatile, da bi se obezbedila vidljivost. O atom skim indikatorim a i vidljivosti
saznaćete više u nastavku poglavlja.
Sledeća klasa ProveraParnosti može da testira svaki GeneratorCelobroj:
//: paralelno/ProveraParnosti.java
import java.util.concurrent.*;
Poglavlje 2 1: Paralelno izvršavanje 919
Zadaci ProvereParnosti stalno čitaju i ispituju brojeve koje je dao njim a pridruženi
GeneratorCelobroj. Ukoliko generator.isC ancelled( ) im a vrednost true, m etoda r u n ( )
vraća svoj rezultat i izlazi, što izvršiocu u m etodi ProveraParnosti.test( ) kazuje da je
zadatak obavljen. Svaki zadatak ProvereParnosti m ože da pozove m etodu cancel( ) za
svoj pridruženi G eneratorCelobroj, čim e prouzrokuje da sve druge ProvereParnosti tog
objekta tipa GeneratorCelobroj n orm alno okončaju svoj rad. U nastavku poglavlja vi-
dećete da Java im a i opštije m ehanizm e za okončavanje rada niti.
Prvi GeneratorCelobroj koji ćem o razm otriti im a m eto d u n e x t( ) koja proizvodi se-
riju parnih brojeva:
//: paralelno/GeneratorParnih.java
// Kada se niti sudare.
Može se desiti da jedan zadatak pozove n e x t ( ) nakon što je drugi zadatak obavio prvo
povećanje broja tekuciP arniB roj za 1, ali ne i drugo (na o nom m estu u program u gde
stoji kom entar „Ovo je opasno!“). Tim e je broj dospeo u nepravilno stanje. Da bih vam
dokazao da se to m ože dogoditi, P ro v e ra P a rn o sti.te st() pravi grupu objekata tipa Pro-
v era P a rn o sti koji stalno čitaju izlaz G en e ra to raP arn ih i ispituju parnost svakog od tih
brojeva. Kada se pronađe neki koji nije paran, prijavljuje se greška i program se gasi.
Taj program pre ili kasnije m ora da otkaže, zato što zadaci P ro v ereP arn o sti m ogu da
pristupe inform acijam a u G e n e ra to ru P a rn ih dok je on u nepravilnom stanju. M eđutim ,
problem ne m ora biti otkriven dok G en e ra to rP a rn ih ne izvrši m nogo ciklusa, čiji se broj
m enja u zavisnosti od operativnog sistema i pojedinosti realizacije. Ako želite da vidite ot-
kazivanje što pre, izm eđu prvog i drugog povećanja za 1 stavite poziv m etode y ie ld ( ). I to
je deo problem a s višenitnim program im a - um eju da daju privid ispravnosti čak i kada
greška postoji, ukoliko je verovatnoća otkazivanja veoma mala.
Važno je uočiti da se operacija povećanja za 1 interno sastoji od više koraka, i da više-
nitni m ehanizam može zaustaviti (prekinuti) zadatak usred tih koraka - drugim rečima,
u Javi povećanje za 1 nije atom ska operacija. Dakle, čak se ni povećanje za 1 ne može oba-
viti bezbedno ako zadatak nije zaštićen.
Poglavlje 21: Paralelno izvršavanje 921
Već ste naučili da podaci klasa m oraju biti privatni (im ati m odifikator private) u kodu
koji se isporučuje kupcima; toj m em oriji treba da pristupate sam o preko m etoda. Sukobe
ćete sprečiti tako što ćete deklarisati da su te m etode sinhronizovane, što se radi ovako:
Svi objekti autom atski dobijaju sopstvenu bravu (koristi se i ime m onitor). Kada pozo-
vete bilo koju sinhronizovanu m etodu, objekat se zaključava i nijedna druga sinhronizo-
vana m etoda tog objekta ne m ože biti pozvana dok se prva ne završi i ne otključa tu bravu.
Za prethodne m etode važi sledeće: ako jedan zadatak pozove f ( ) za neki objekat, drugi
zadaci ne m ogu pozivati ni f ( ) ni g ( ) za taj objekat dokle god f ( ) ne završi rad i ne otključa
bravu. Dakle, postoji samo jedna brava koju dele sve sinhronizovane m etode određenog
objekta, i ona sprečava da više zadataka istovremeno upisuje u m em oriju objekta.
Vodite računa o tom e da je za paralelno izvršavanje izuzetno važno da polja b u d u pri-
vatna; ako nisu, rezervisana reč synchronized ne m ože sprečiti druge zadatke da polju
pristupaju neposredno i tako izazivaju sukobe.
Isti zadatak može više puta zaključati objekat. To se dešava kada jedna m etoda za isti
objekat pozove drugu m etodu, a druga za isti objekat pozove treću itd. JVM prati broj
zaključavanja objekta. Broj zaključavanja otključanog objekta je nula. Kada zadatak prvi
put zaključa taj objekat, broj zaključavanja se poveća za jedan. Svaki p ut kada taj zadatak
ponovo zaključa taj objekat, broj zaključavanja se poveća za jedan. Naravno, samo
zadatak koji je prvi zaključao taj objekat m ože da ga zaključa više puta. Broj zaključavanja
se sm anjuje za jedan kad god taj zadatak izađe iz neke od sinhronizovanih m etoda; kada
broj zaključavanja padne na nulu, objekat je slobodan i m ogu ga koristiti drugi zadaci.
I svaka klasa autom atski dobija svoju bravu (koja je deo njenog objekta klase Class),
tako da sinhronizovane statičke m etode (označene kao synchronized i static) mogu jed-
na drugu sprečiti da istovrem eno pristupe statičkim podacim a u okviru pojedine klase.
Kada sinhronizovati? Prim enite B rajanovopravilo sinhronizacijc:'0
Sinhronizaciju m orate upotrebiti ukoliko pišete u prom enljivu koju bi zatim mogla da
pročita neka druga nit ili čitate prom enljivu koju je prethodno m ogla upisati neka druga
nit. Nadalje, i čitanje i pisanje m ora biti sinhronizovano pom oću iste brave (m onitora).
Ako u klasi im ate više m etoda koje rade s bitnim podacima, m orate sinhronizovati sve
relevantne metode. Ukoliko sinhronizujete sam o jednu od tih m etoda, ostale m ogu zane-
m ariti bravu na objektu i biti nesm etano pozvane. Ovo je važno: sve m etode koje pri-
stupaju kritičnom resursu m oraju biti sinhronizovane ili program neće raditi kako treba.
Sinhronizovanje GeneratoraParnih
Ako klasi G eneratorParnih.java dodam o rezervisanu reč synchronized, sprečićemo
neželjeno pristupanje toj niti:
10 Po Brianu Goetzu, koautoru ]ava Concurrency in Practice ; ostali autori su Tini Peierls, Joshua Bloch,
Joseph Bowbeer, David Holmes i Doug Lea (Addison-WesIey, 2006).
Poglavlje 2 1: Paralelno izvršavanje 923
//: paralelno/SinhronizovanGeneratorParnih.java
// Pojednostavljivanje uzajamno isključivih brava
// pomoću rezervisane reči synchronized.
// {RunByHand}
public class
SinhronizovanGeneratorParnih extends GeneratorCelobroj {
private int tekuciParniBroj = 0;
public synchronized int next() {
++tekuciParniBroj;
Thread.yield(); // Brže izaziva zakazivanje
++tekuci ParniBroj;
return tekuciParniBroj;
}
public static void main(String[] args) {
ProveraParnosti .test(new SinhronizovanGeneratorParnihO);
}
} ///= -
Poziv m etode T hread.yield( ) um etnut je između dva povećanja za 1 da bi se povećala
verovatnoća prebacivanja konteksta dok je tekuciParniBroj neparan. Pošto uzajam no
isključiva brava sprečava da više zadataka u isto vreme bude u kritičnom delu program a,
do otkazivanja neće doći, ali poziv m etode y ie ld ( ) pom aže da se otkazivanje ranije desi
ukoliko već može da nastane.
M etodu n e x t( ) zaključava prvi zadatalc koji uđe u nju, i svi drugi zadaci koji pokušaju
da je zaključaju bivaju blokirani sve dok je prvi zadatak ne otključa. Tada m ehanizam za
raspodelu procesorskog vrem ena odabire jedan od ostalih zadataka koji čekaju da se bra-
va otključa. Na taj način, u svakom trenutku samo po jedan zadatak može proći kroz kod
koji čuva uzajam no isključiva brava (mutex).
Vežba 11: (3) N apravite klasu koja sađrži dva polja podataka i m etodu koja radi s tim
poljim a u postupku koji traje više koraka, tako da tokom izvršavanja te m etode polja
budu u nepravilnom stanju (u odnosu na definiciju koju ćete sami dati). D odajte m etode
koje čitaju ta polja i napravite više niti za pozivanje raznih m etoda i pokažite da su poda-
ci vidljivi i kada su nepravilni. Rešite problem pom oću rezervisane reči synchronized.
//: paralelno/MutexGeneratorParnih.java
// Sprećavanje sukoba niti pomoću uzajamno isključivih brava (mutexa).
// {RunByHand)
import java.util.concurrent.locks.*;
924 Misliti na Javi
//: paralelno/PokusajZakljucavanja.java
// Brave u biblioteci concurrent dozvoljavaju
// da odustanete od pokušaja zaključavanja.
import java.uti1 .concurrent.*;
import java.util.concurrent.locks.*;
try (
System.out.println("tryLock(): " + zakljucano);
) finally {
if (zakl jucano)
brava.unlock();
1
}
public void odredjenoVremeO {
boolean zakljucano = false;
try {
zakljucano = brava.tryLock(2, TimeUnit.SECONDS);
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
try {
System.out.println("tryLock(2, TimeUnit.SECONDS): " +
zakljucano);
} finally {
if(zakljucano)
brav a .u n l o c k O ;
}
}
public static void main(String[] args) {
final PokusajZakljucavanja pz = new PokusajZakljucavanja();
pz.neodredjenoVremeO ; // True -- brava je otključana
pz.odredjenoVreme(); // True — brava je otključana
// Sada napravi zaseban zadatak koji će pokušati da zaključa:
new Thread() {
{ setDaemon(true); }
public void run() {
p z .brava.1ock();
System.out.pri ntln("zaključano");
}
}.start();
Thread.yield(); // Daj priliku drugom zadatku
pz.neodredjenoVremef); // False -- prethodni zadatak je več zaključao
pz.odredjenoVremeO; // False — prethodni zadatak je već zaključao
}
} /* Ispis:
tryLock(): true
tryLock(2, TimeUnit.SECONDS): true
zaključano
tryLock(): false
tryLock(2, TimeUnit.SECONDS): false
* ///:-
1'Po p re th o d n o p o m enutom B rajanu Gecu (Brian G oetz), stru ćn jak u za paralelno p ro g ram iran je koji
m i je po m o g ao da napišem ovo poglavlje na osnovu njegovih sam o đelim ičn o drskih kom entara.
2 Iz ovog testa n epo sredno sledi: ,Ako neko tvrdi da je paraleln o p ro g ram iran je lako i jednostavno,
po starajte se da ta osoba ne donosi važne odluke o projektu na kojem radite. Ukoliko to nikako ne
m ožete da izbegnete, im ate problem .“
Poglavlje 21: Paralelno izvršavanje 927
Katkada izgleda da bi atom ska operacija trebalo da bude bezbedna, ali ona to nije. Većina
ćitalaca ove knjige sigurno neće m oći da položi spom enuti Gecov test, te uopšte nisu kva-
lifikovani n i da pokušaju da sinhronizaciju zam ene atom skim operacijama. Pokušaj
uklanjanja sinhronizacije obično je znak prerane optim izacije i prouzrokuje grdne pro-
bleme, a dobitak je mali ili nikakav.
Na višeprocesorskim sistemima (koji se sada prave u obliku procesora s više jezgara
- to je više procesora u jednom integrisanom kolu), m nogo veći problem od neprekidno-
sti operacija je vidljivost. (Taj problem ne postoji u jednoprocesorskim sistemima.) Izme-
ne koje napravi jedan zadatak, čak i kada su atom ske, odnosno neprekidne, ne m oraju biti
vidljive drugim zadacima (jer sii privrem eno smešterte u lokalni keš procesora, na pri-
m er), pa razni zadaci dobijaju različite uvide u stanje aplikacije. S druge strane,
m ehanizam sinhronizacije obezbeđuje da izm ene koje na višeprocesorskom sistemu na-
pravi jedan zadatak b u du vidljive celoj aplikaciji. Bez sinhronizacije se ne m ože znati kada
će izm ene postati vidljive.
Vidljivost u celoj aplikaciji obezbeduje i rezervisana reč volatile. Ako deklarišete da je
neko polje volatile, to znači da će svi zadaci koji ga čitaju videti izmenu čim se upisivanje
u njega završi. To važi čak i za lokalnu keš m em oriju, zato što se polja označena s volatile
odm ah prepisuju u glavnu m em oriju, a sva čitanja se obavljaju iz glavne m em orije.
Nadam se da shvatate kako su neprekidnost i trenutna vidljivost različiti pojmovi.
Atomska operacija polja koje nem a m odifikator volatile ne m ora da bude o dm ah pre-
pisana u glavnu m em oriju, pa ni drugi zadaci koji čitaju to polje ne m oraju odm ah da
vide novu vrednost. Svako polje kojem pristupa više zadataka treba da im a m odifikator
volatile; u protivnom , polju treba pristupati sam o preko sinhronizacije. I sinhronizacija
prouzrokuje prepisivanje u glavnu m em oriju, pa polje koje je potpuno zaštićeno
sinhronizovanim m etodam a ili blokovim a ne m ora da ima m odifikator volatile.
Rezultati svih upisivanja koja zadatak obavi odm ah su vidljivi tom zadatku, pa polju
ne m orate da dodajete m odifikator volatile ukoliko ga čita sam o njegov zadatak.
volatile ne funkcioniše kada vrednost polja zavisi od njegove prethodne vrednosti
(takvo je povećanje vrednosti brojača za 1), niti funkcioniše za vrednosti koje su ogra-
ničene vrednostim a drugih polja, kao što su lovver (donja) i upper (gornja) granica ob-
jekta tipa Range koje m oraju da zadovolje ograničenje lovver <= upper.
U potreba m odifikatora volatile umesto rezervisane reči synchronized bezbedna je naj-
češće jedino u slučaju da klasa ima samo jedno prom enljivo polje. Ponavljam, najbolje je
izabrati rezervisanu reč synchronized - to je najbezbedniji pristup, a sve ostalo je rizično.
Koje operacije su atomske? Dodeljivanje i vraćanje vrednosti polja obično su atomske
operacije. M eđutim , u C++-U bi atom ske mogle biti čak i ove operacije:
U Javi gornje operacije sigurno nisu atomske, kao što vidite iz JVM instrukcija koje
proizvode sledeće metode:
//: paralelno/Neprekidnost.java
// {Pokretanje: javap -c NeprekidnostJ
void fl();
Code:
0: aload_0
1: dup
2: getfield #2; //Polje i:I
5: iconst_l
6: iadd
7: putfield #2; //Polje i:I
10: return
void f2();
Code:
0: aload_0
1: dup
2: getfield #2; //Polje i : I
5: iconst_3
6: iadd
7: putfield #2; //Polje i:I
10: return
* ///:-
Svaka instrukcija proizvodi jedan get i jedan p ut između kojih ima drugih instrukcija.
Dakle, izm eđu čitanja (get) i upisivanja (put) neki drugi zadatak može da izmeni vred-
nost polja; stoga te operacije nisu atomske.
Ako slepo prim enjujete neprekidnost, mogli biste pom isliti da je takva i m etoda
d a j V r e d n o s t ( ) u sledećem program u:
//: paralelno/TestNeprekidnosti.java
import java.uti1.concurrent.*;
//: paralelno/GeneratorSerijskihBrojeva.java
Idcja iz knjige Efikasno programiranje na ]avi Joshue Blocha (M ikro knjiga, 2004), s. 168.
930 Misliti na Javi
Polje serijskiBroj im a m odifikator volatile zato što svaka nit može da ima lokalni stek
i da u njem u održava kopije nekih promenljivih. Ako ispred definicije promenljive stavite
m odifikator volatile, tim e kazujete prevodiocu da ne sprovodi optimizacije kojima bi se
uklonila čitanja i upisivanja koja polje održavaju u sinhronizaciji s lokalnim podacim a u
nitim a. Zato se čitanja i upisivanja obavljaju neposredno u glavnoj m emoriji, a ne u kešu.
M odifikator volatile ograničava i prevodiočevo preraspoređivanje pristupa tokom optim i-
zacije. M eđutim , volatile ne utiče na činjenicu da povećanje za 1 nije atomska operacija.
U suštini, definišite da je polje volatile ukoliko m u više zadataka može istovremeno
pristupiti, a barem jedno od tih pristupanja je upisivanje. Prim era radi, polje koje se
upotrebljava kao indikator za zaustavljanje zadatka m ora biti deklarisano kao volatile; u
protivnom bi indikator m ogao biti keširan u nekom registru procesora, i ta vrednost se ne
bi izm enila kada vi iz nekog drugog zadatka izm enite vrednost indikatora, pa prvi
zadatak ne bi znao kada treba da se zaustavi.
Za ispitivanje GeneratoraSerijskihBrojeva treba nam skup kojem neće ponestati me-
m orije ni u slučaju da pronalaženje problem a potraje. Ovde prikazani KruzniSkup po -
novo upotrebljava m em oriju u kojoj su bili uskladišteni celi brojevi, uz pretpostavku da
će verovatnoća sukoba sa zam enjenim brojevim a biti m inim alna kada se s kraja skupa
vratim o na njegov početak. M etode a d d ( ) i co n tain s( ) sinhronizovane su da bi se spreći-
Io sudaranje niti:
//: paralelno/ProveraSerijskihBrojeva.java
// Operacije prestaju da budu bezbedne
// kada izvršavanje postane višenitno.
// {Args: 4}
import java.uti1 .concurrent.*;
atom sku operaciju koja pristupa objektu dok je o n u nekom nestabilnom m eđustanju.
D onositi bilo kakve zaključke o tom e škakljivo je i opasno. N ajrazboritije je prosto pri-
državati se Brajanovog pravila za sinhronizaciju.
Vežba 12: (3) Popravite TestNeprekidnosti.java pom oću rezervisane reči synchronized.
U m ete li da pokažete kako je program sada ispravan?
Vežba 13: (1) Popravite program ProveraSerijskihBrojeva.java p o m o ću rezervisane reči
synchronized. U mete li da pokažete kako je program sada ispravan?
Atomske klase
Java SE5 je donela i posebne atom ske klase prom enljivih kao što su Atomiclnteger, Ato-
micLong, AtomicReference itd., koje obezbeđuju atom sko uslovno ažuriranje oblika:
O ne su nam enjene za fino podešavanje p ri kom e se koriste atom ske operacije (nepie-
Iđdnosti) dostupne na nekim od savrem enih procesora, pa o njim a po pravilu ne bi tre-
balo da razbijate glavu. Ponekad m ogu da posluže i za obično program iranje, ali opet
tam o gde se radi o podešavanju perform ansi. Na prim er, izm enićem o TestN eprekidno-
sti.java tako da upotrebljava klasu A tom iclnteger:
//: paralelno/AtomicIntegerTest.java
import java.util.concurrent.*;
import java.uti1 .concurrent.atomic.*;
import java.util.*;
System.out.println(vre);
System.exit(0);
}
)
}
} ///= -
U potrebom klase Atom iclnteger izbegli sm o korišćenje rezervisane reči synchroni-
zed. Pošto ovaj program ne otkazuje, dodat je Tim er koji autom atski prekida rad nakon
5 sekundi.
Evo kako izgleda M utexG eneratorParnih.java izm enjen tako da upotrebljava klasu
Atomiclnteger:
//: paralelno/AtomskiGeneratorParnih.java
// Atomske klase se povremeno koriste i u običnim programima.
// {RunByHand}
import java.util.concurrent.atomic.*;
Kritični odeljci
Ponekad treba izolovati sam o deo koda u n u ta r m etode r u n ( ), a ne celu m etodu. Deo koji
treba izolovati naziva se kritičan (engl. critical), a rezervisana reč synchronized se u kri-
tičnom delu program a koristi drugačije. U sledećem prim eru reč synchronized zaključa-
va bravu određenog objekta da bi sinhronizovala obuhvaćeni blok koda:
synchronized(brava) {
// Ovom kodu može da pristupi samo jedna nit u jednom trenutku
93 4 Misliti na Javi
O buhvaćeni blok koda naziva se sinhrortizovani blok, pre ulaska u sinhronizovani blok,
raorate da zaključate objekat brava. Ako je neka druga nit već zaključala taj objekat, u taj
blok nije moguće ući sve dok ne bude otključan.
U sledećem prim eru upoređena su oba p ristupa sinhronizađji. Pokazano je da se vre-
m e tokom kojega je objekat dostupan d rugim zadacim a znatno produžava kada se koristi
sinhronizovani blok um esto sinhronizovanja cele m etode. Pored toga, pokazano je kako
se nezaštićena klasa m ože upotrebiti u višenitnoj situaciji ukoliko je kontroliše i štiti dru-
ga klasa:
//: paralelno/KriticanDeo.java
// Sinhronizovanje blokova umesto celih metoda. Pokazuje i zaštitu klase
// nebezbedne u višenitnom izvršavanju pomoću druge koja je bezbedna.
package concurrency;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util
} /* Ispis: (primer)
mpl: Par: x: 15, y: 15 brojacProvera = 272565
mp2: Par: x: 16, y: 16 brojacProvera = 3956974
* ///:-
Kao što rekoh, Par nije bezbedan u višenitnom izvršavanju zato što njegova invarijan-
ta (priznajem , sasvim proizvoljno) zahteva da obe prom enljive zadrže istu vrednost. Sem
toga, već ste u p retho dn om delu poglavlja videli da operacije povećanja za jedan nisu bez-
bedne u višenitnom izvršavanju, a pošto nijedna od m etoda nije sinhronizovana, ne
m ožete očekivati da će objekat tipa Par ostati nepokvaren u višenitnom program u.
Recimo da vam je neko dao klasu Par koja nije bezbedna u višenitnom izvršavanju, a vi
m orate da je upotrebite u višenitnom okruženju. To ćete postići pravljenjem kiase Mena-
dzerPara koja sadrži objekat tipa Par i kontroliše pristupanje njemu. Vodite računa o tom e
da su jedine javne m etode d ajP ar( ), koja je sinhronizovana, i apstraktna m etoda povecaj-
Z ajed an ( ). M etoda povecajZaJedan( ) biće sinhronizovana na m estu svoje realizacije.
S truktura klase MenadzerPara, u kojoj funkcije realizovane u osnovnoj klasi
upotrebljavaju jednu ili više apstraktnih m etoda definisanih u izvedenim klasama, naziva se
projektni obrazac Template M ethod.'5 Projektni obrasci om ogućuju kapsuliranje prom ena
u kodu; deo koji se ovde menja je metoda povecajZaJedan( ). U klasi MenadzerParal
sinhronizovana je cela metoda povecajZaJedan( ), dok je u klasi MenadzerPara2 pom oću
sinhronizovanog bloka sinhronizovan samo deo metode povecajZaJedan( ). O bratite
pažnju na to da rezervisana reč synchronized ne spada u potpis m etode i stoga se m ože do-
dati prilikom preklapanja.
M etoda sacuvaj( ) dodaje olijekat tipa Par sinhronizovanoj listi tipa ArrayList, pa je ta
operacija bezbedna u višenitnom izvršavanju. Stoga nju ne m oram da štitim , pa sam je
sm estio izvan sinhronizovanog bloka u klasi MenadzerPara2.
RukovalacPara je napravljen da bi pozivanjem m etode povecajZaJedan( ) iz zadatka te-
stirao dve različite realizacije apstraktne klase MenadzerPara, dok se ProveraPara obavlja
iz drugog zadatka. Svaki put kada je ProveraPara uspešna, ona brojacProvera poveća za 1
i tako prati učestalost prilika da obavi testiranje. U metodi m a in ( ), prave se dva objekta tipa
RukovalacPara koji neko vreme rade pa se potom prikazuju rezultati svakog od njih.
Iako će se izlazni rezultati verovatno m nogo razlikovati od jednog do drugog izvrša-
vanja, videćete da m etoda M enadzerPara).povecajZaJedan( ) nc dozvoljava klasi Prove-
raPara pristup ni približno onoliko često kao m etoda M enadzerPara2.povecajZaJedan( )
koja ima sinhronizovan blok i zato duže radi bez zaključavanja. O bično je baš to razlog za
upotrebu sinhronizovanog bloka umesto sinhronizacije cele metode: om ogućiti drugim
zadacim a češći i duži pristup (koliko je to bezbedno).
Za zaštitu kritičnih delova program a možete upotrebiti i eksplicitne objekte tipa Lock:
//: paralelno/EksplicitanKriticanDeo.java
// Zaštita kritičnih delova programa eksplicitnim objektima tipa Lock.
package concurrency;
import java.uti1.concurrent.locks.*;
// Kritičan deo:
class EksplicitanMenadzerPara2 extends MenadzerPara {
private Lock brava = new ReentrantLock();
public void povecajZaJedan () {
Par privr;
brava.lockO;
try {
p.povecajXzaJedan();
p.povecajYzaJedan();
privr = dajPar();
} finally {
brava.unlockO ;
}
sacuvaj(pri vr);
}
//: paralelno/ObjekatSinhronizacije.java
// Sinhronizacija s drugim objektom.
import static net.mindview.util.Print.*;
class DvojnaSinhronizacija (
private Object objekatSinhronizacije = new Object();
public synchronized void f() {
for(int i = 0; i < 5; i++) {
print("f()");
Thread.yield();
)
}
public void g() {
synchroni zed(objekatSinhronizacije) {
for(int i = 0 ; i < 5 ; i++) {
pri n t ("g()");
Thread.yield();
}
}
}
f()
9 ()
f()
g()
f()
g()
f()
* ///:-
DvojnaSinhronizacija.f( ) sinhronizuje na objekat this (sinhronizacijom cele m eto-
de), a g ( ) im a sinhronizovan blok koji sinhronizuje na objekatSinhronizacije. Dakle, te
dve sinhronizacije su nezavisne. To je dokazano u m etodi m a in ( ) pravljenjem niti
(objekta tipa Thread) koja poziva f ( ). Glavna nit - m etode m a in ( ) - upotrebljena je za
poziv g ( ). Iz rezultata vidite da se obe m etode izvršavaju istovrem eno, dakle sinhroniza-
cija jedne ne blokira onu drugu.
Vežba 15: (1) Napravite klasu s tri m etode koje sadrže kritične delove, a sve su
sinhronizovane sa istim objektom . N apravite više zadataka kako biste pokazali da se u
svakom trenutku može izvršavati sam o jedan od njih. Potom m odifikujte m etode tako
da se sinhronizuju s različitim objektim a i pokažite da se sve tri m etode m ogu izvršavati
istovremeno.
Vežba 16: (1) Izmenite vežbu 15 tako da se upotrebljavaju eksplicitni Lock objekti.
* ///:-
Objekti tipa ThreadLocal obično se skladište kao statička polja. Kada napravite objekat
tipa ThreadLocal, sadržaju tog objekta možete pristupiti sam o m etodam a g e t( ) i s e t( ).
Metoda g e t( ) vraća kopiju objekta pridruženu toj niti, dok s e t( ) umeće svoj argum ent u
objekat uskladišten za tu nit i vraća stari objekat koji je bio u tom skladištu. To pokazuju
m etode povecajZaJeđan( ) i g e t( ) program a ThreadLocalSpremnikPromenljivih.
O bratite pažnju na to da m etode povecajZaJedan( ) i g e t( ) nisu sinhronizovane, zato što
ThreadLocal jemči da neće doći do uslova za trku.
Kada pokrenete ovaj program , videćete dokaz da se svakoj niti dodeljuje njeno sop-
stveno skladište, pošto svaka od njih održava sopstveni brojač iako postoji sam o jedan ob-
jekat ThreadLocalSpremnikPromenljivih.
942 Misliti na Javi
Gašenje zadataka
U nekim a od prethodnih prim era, m etode cancel( ) i isCanceIled( ) sm eštene su u klasu
vidljivu svim zadacima. Zadaci ispituju vrednost m etode isC ancelled( ) da bi znali kada
da se ugase. To je razborit pristup problem u. M eđutim , u nekim situacijam a zadatak
m ora biti ugašen naglije. U ovom odeljku saznaćete sve o takvom gašenju i problem im a
zbog njega.
Prvo, razm otrim o prim er koji ne sam o da pokazuje problem gašenja, nego je i doda-
tan prim er deljenja resursa.
Ukrasna bašta
U ovoj simulaciji, baštenska komisija bi htela da zna koliko Ijudi svakog dana uđe u baštu
kroz neku od njenih kapija. Svaka kapija im a blokirajuću vrtešku ili neku dru g u vrstu
brojaća, i nakon povećanja brojača blokirajuće vrteške za 1, povećava se i zajednički bro-
jač koji predstavlja ukupan broj posetilaca bašte.
//: paralelno/UkrasnaBasta.java
import java.util.concurrent.*;
import java.util
import static net.mindview.uti1 .Print.*;
class Broj {
private int broj = 0;
private Random slucajan = new Random(47);
// Ako uklonite rezervisanu reć synchronized,
// prebrojavanje će zakazati:
public synchronized int povecajZaJedan() {
int privr = broji;
if(slucajan.nextBoolean()) // Pola vremena prepusti drugima
Thread.yield();
return (broj = ++privr);
}
public synchronized int value() { return broj; }
}
Ulaz 1: 1 Ukupno: 2
Ulaz 4: 1 Ukupno: 5
Ulaz 3: 1 Ukupno: 4
Ulaz 2: 2 Ukupno: 6
Ulaz 4: 2 Ukupno: 7
Ulaz 0: 2 Ukupno: 8
i stoga završe svoje izvršavanje, objekti tipa Ulaz i dalje su validni zato što je u konstruk-
to ru svaki objekat Ulaz uskladišten u statičkoj listi ulazi tipa List<Ulaz>. Zato
saberiU laze( ) i dalje radi s validnim objektim a Ulaz.
D ok ovaj program bude radio, prikazivaće ukupan broj posetilaca i broj posetilaca na
svakom ulazu koji p ro đ u kroz blokirajuću vrtešku. Ako uklonite deklaraciju synchroni-
zed ispred m etode Broj.povecajZaJedan( ), videćete da ukupan broj posetilaca nije jed-
nak očekivanom . Broj posetilaca koji se prebroji na svim blokirajućim vrteškam a neće
biti jednak iznosu prom enljive broj. Sve ispravno radi dok postoji uzajam no isključiva
brava (m utex) koja sinhronizuje pristupanje objektu Broj. Imajte u vidu da m etoda
Broj.povecajZaJedan( ) pom oću prom enljive p riv r i m etode y ield ( ) n am erno povećava
m ogućnost otkazivanja. U pravim višenitnim problem im a, m ogućnost otkazivanja m ože
biti statistički mala, pa ćete lakše poverovati u zabludu da vaš program radi ispravno. Kao
u gornjem prim eru, verovatno će postojati i skriveni problem i kojih niste svesni, pa bu-
dite izuzetno pažljivi kada pregledate program e za paralelno izvršavanje.
Vežba 17: (2) N apravite Gajgerov brojač koji m ože im ati proizvoljan broj daljinskih
senzora.
Stanja niti
Nit m ože biti u jed no m od četiri stanja:
1. Nova: Nit je u ovom stanju privrem eno, dok je prave. O na dodeljuje potrebne si-
stemske resurse i obavlja inicijalizaciju. O d tog trenutka postaje podobna za pri-
m anje procesorskog vrem ena. Potom m ehanizam za raspodelu vrem ena menja
stanje niti u sprem na za izvršavanje ili blokirana.
2. Sprem na za izvršavanje: Znači da se nit m ože pokrenuti kada m ehanizam podele
procesorskog vrem ena bude im ao slobodnih procesorskih ciklusa za nju. M ožda se
ta nit izvršava, a m ožda i ne, ali ništa ne m ože sprečiti njeno izvršavanje ukoliko joj
m ehanizam za raspodelu vrem ena dodeli procesor. D rugim rečima, nit nije ni m rt-
va ni blokirana.
3. Blokirana: Nit bi mogla da se izvršava, ali je nešto sprečava. D ok je nit blokirana,
m ehanizam za raspodelu vrem ena je preskače i ne dodeljuje joj procesor. Sve dok
ta nit ponovo ne postane sprem na za izvršavanje, neće biti pokretana.
4. M rtva: M rtva ili ugašena (engl. term inated) nit više nije podobna za raspoređivanje
i procesorsko vrem e joj se ne dodeljuje. Njen zadatak je završen i ona se više ne
može pokrenuti. Zadatak norm alno um ire kada se vrati iz svoje m etode r u n ( ), ali
i njegova nit m ože biti prekinuta, kao što ćete uskoro videti.
946 Misliti na Javi
Prekidanje izvršavanja
Kao što m ožete zamisliti, m nogo je teže izaći iz m etode R unnable.run( ) pre njenog kraja
nego sačekati da ona dospe do mesta gde ispituje vrednost indikatora ,,otkaži“ ili do ne-
kog drugog m esta gde je program er sprem an da napusti tu m etodu. Kada izađete iz blo-
kiranog zadatka, često treba da počistite resurse. Zato izlazak iz m etode r u n ( ) datog
zadatka pre njenog kraja, najviše liči na bacanje izuzetka, pa se u Javinim nitim a za tu vr-
stu prekidanja koriste izuzeci.16 (Ovo je na samoj ivici nekorektne upotrebe izuzetaka,
pošto znači da ih često upotrebljavate za kontrolu toka program a.) Da biste se prilikom
ovakvog gašenja zadatka vratili na neko od poznatih dobrih stanja, m orate pažljivo raz-
m otriti putanje izvršavanja koda i napisati odredbu catch tako da pravilno sve počisti.
Klasa Thread sadrži m etodu in te rru p t( ) koja služi za gašenje blokiranih zadataka. O na
niti daje status prekinute. Nit kojoj je postavljen status prekinuta baca izuzetak Interrup-
tedException ukoliko je već blokirana ili pokušava da izvrši operaciju koja prouzrokuje
blokiranje. Status prekinuta biće resetovan kada se baci izuzetak ili kada zadatak pozove
m etodu T h read .interru p ted ( ). Kao što ćete videti, m etoda T hread.interrupted( ) pruža
drugi način izlaska iz petlje r u n ( ), bez bacanja izuzetka.
16 M eđ u tim , izuzeci se nikada ne generišu asinhrono. Stoga n en ia o p asn osti da nešto prekine instruk-
ciju/poziv m eto d e. Ukoliko p rilikom u p o treb e m utexa (za razliku o d rezervisane reči sy n c h ro n ized )
b u d ete upotrebljavali idiom try-fin ally, te u zajam no isključive brave (m utex i) biće autom atski ot-
k ljučane kada se baci izuzetak.
Poglavlje 21: Paralelno izvršavanje 947
Da biste pozvali in te rru p t( ), m orate im ati nit, tj. objekat tipa Thread. M ožda ste
uočili da nova biblioteka concurrent (za paralelno izvršavanje) kao da izbegava neposre-
dan rad s nitim a; ona sve pokušava da ostvari izvršiocima. Ako pozovete shutdow n-
N ow ( ) za nekog izvršioca, on će poslati poziv in te rru p t( ) svim nitim a koje je pokrenuo.
To im a smisla, pošto najčešće hoćem o odjednom da ugasim o sve zadatke odredenog
izvršioca, kada završimo deo projekta ili ceo program . M eđutim , im a slučajeva kada bi-
sm o da ugasim o sam o jedan zadatak. Ako radite sa izvršiocem, kontekst zadatka koji ste
pokrenuli m ožete da uhvatite tako što ćete pozvati m etodu su b m it( ) um esto m etode
execute( ). s u b m it( ) vraća generički objekat Future<?> s nespeđficiranim param etrom ,
zato što za taj objekat nikada n'ećete zvati g e t( ) - svrha posedovanja ovakvog objekta tipa
Future jeste da za njega m ožete pozvati cancel( ) i tako ga upotrebiti za prekidanje
određenog zadatka. Ukoliko m etodi cancel( ) prosledite true, ona im a dozvolu da pozove
in te rru p t( ) za tu nit da bi je prekinula; dakle, m etoda cancel( ) predstavlja jedan od nači-
na prekidanja niti koju je pokrenuo izvršilac.
U narednom prim eru videćete osnove upotrebe m etode in te rru p t( ) u izvršiocu:
//: paralelno/Prekidam.java
// Prekidam blokirane niti.
import java.util.concurrent.*;
import java.io.*;
import static net.mindview.util.Print.*;
//: paralelno/ZatvoriResurs.java
// Prekidam blokirani zadatak zatvaranjem pripadnog resursa.
// {RunByHand}
import java.net.*;
import java.util.concurrent.*;
import java.io.*;
import static net.mindview.uti1.Print.*;
1 Neka izdanja JDK podržavaju i In te rru p tc d IO E x c e p tio n . M eđ u tim , taj izuzetak je te k d e lim ić n o rea-
lizovan, i to sanio na nekim platform am a. Bacanje tog izuzetka p ro u zro k u je da U /I o bjekat postane
neupotrebljiv. M alo je Verovatno da će bu d u ća izdanja podržavati taj izuzetak.
950 Misliti na Javi
exec.execute(new BlokiranoUIOperacijom(System.in));
TimeUni t.MILLISECONDS.sleep(lOO);
print("Gasim sve niti");
exec.shutdownNow();
TimeUnit.SECONDS.sleep(l);
printC'Zatvaram " + ul azIzUticnice.getClass() . g e t N a m e O ) ;
ulazIzUticnice.close(); // Oslobađa blokiranu nit
TimeUnit.SECONDS.sleep(l);
print("Zatvaram " + System.in.getClass().getName());
System.in.close(); // Oslobađa blokiranu nit
}
} /* Ispis: (85% podudaranja)
Čekamo na read():
Čekamo na read():
Gasim sve niti
Zatvaram java.net.SocketlnputStream
Prekinuto iz blokirane U/I operacije
Izlazim iz niti BlokiranoUIOperacijom.run()
Zatvaram java.io.BufferedlnputStream
Izlazim iz niti B1okiranoUIOperacijom.run()
* ///:-
//: paralelno/NIOPrekid.java
// Prekidam blokiran NIO kanal.
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.uti1 .concurrent.*;
import java.io.*;
import static net.mindview.util.Print.*;
} catch(IOException e) {
throw new RuntimeException(e);
}
print(''Izlazim iz niti NIOBlokiran.run() " + this);
}
}
Kao što vidite, deblokiranje možete izvršiti i zatvaranjem pripadnog kanala, m ada bi
to retko trebalo da bude neophodno. Vodite računa o tom e da biste pokretanjem oba
zadatka m etodom e x e c u te () i pozivom m etode e.sh u td o w n N o w () lako sve pogasili;
hvatanje objekta tipa F uture u gornjem prim eru bilo je potrebno samo da bi se izuzetak
poslao jednoj niti, a ne i onoj drugoj.18
Vežba 18: (2) N apravite klasu bez zadataka, ali s m etodom koja poziva s le e p () za dugač-
ko razdoblje. Napravite zadatak koji poziva tu m etodu u klasi bez zadataka. U m etodi
m a in ( ) pokrenite zadatak, a zatim pozovite in te r r u p t( ) da biste ga ugasili. Treba da po-
stignete da se zadatak ugasi bezbedno.
Vežba 19: (4) Izmenite program U krasnaB asta.java tako da upotrebite m etodu
in te r r u p t( ).
Vežba 20: (1) Izmenite program C achedT hreadPooI.java tako da svi zadaci prim e
in te r r u p t( ) pre nego što budu završeni.
//: paralelno/VisePutaZakljucanaBrava.java
// Nit može više puta da zaključa istu bravu.
import static net.mindview.util.Print.*;
pozivu m etode f 2 ( ) itd. To im a smisla zato što bi zadatak trebalo da može da poziva sin-
hronizovane m etode u n u tar istog objekta; taj zadatak je već zaključao objekat (bravu).
Kao što je već bilo rečeno za ulazno/izlazne operacije koje nije moguće prekinuti, kad
god zadatak m ože biti tako blokiran da ga se ne m ože prekinuti, postoji m ogućnost da će
to zaključati ceo program . U biblioteke za paralelno izvršavanje Jave SE5 dodata je i m o-
gućnost prekidanja zadataka blokiranih na bravam a (objektim a) tipa R eentrantL ock, za
razliku od zadataka blokiranih na sinhronizovanim m etodam a ili kritičnim delovima:
//: paralelno/Prekidam2.java
// Prekidam zadatak blokiran pomoću objekta tipa ReentrantLock.
import java.uti1 .concurrent.*;
import java.util.concurrent.locks.*;
import static net.mindview.util.Print.*;
class BlokiranMutex {
private Lock brava = new ReentrantLock();
public BlokiranMutex() {
// Odmah ćemo ga zaključati, da bismo pokazali prekidanje
// zadatka blokiranog na bravi Re en tr an tL oc k:
br av a.1o c k ( ) ;
}
public void f() {
try {
// Drugim zadacima ovo nikada neće biti dostupno
b r av a.1o c k l n t e r r u p t i b l y (); // Poseban poziv
print("zaključana brava u f ()");
} catch(InterruptedException e) {
p r i n t ("Prekinuto u pokušaju zaključavanja brave u f ()");
}
}
}
} /* Ispis:
Čekamo na f() u klasi BlokiranMutex
Pozivam n.interrupt()
Prekinuto u pokušaju zaključavanja brave u f()
Izašao iz blokiranog poziva
* ///•-
Klasa BlokiranMutex im a k onstruktor koji zaključa bravu (objekat tipa Lock) samog
objekta i nikada je ne otključa. Zbog toga ćete, ukoliko pokušate da pozovete f ( ) iz nekog
drugog zadatka (različitog od onoga koji je napravio objekat BlokiranMutex), uvek biti
blokirani pošto je ta uzajam no isključiva brava stalno zaključana. U klasi Blokirana2,
m etodu r u n ( ) zaustaviće poziv m etode b lo k irana.f( ). Kada pokrenete program , videćete
da, za razliku od poziva ulazno/izlaznih operacija, in te rru p t( ) m ože da izađe iz poziva
koji je blokirala uzajamno isključiva brava (m u tex ).'9
//: paralelno/IdiomPrekidanja.java
// Opšti idiom za prekidanje zadatka.
// {Args: 1100}
import java.util.concurrent.*;
import static net.mindview.util.Print.*;
class TrebaPocistiti {
private final int id;
|g lako je to m a!o verovatno, im ajte u vidu da bi se poziv m eto d e t . i n t e r r u p t ( ) zapravo m ogao desiti
p re poziva m eto de b lo k ira n a .f( ).
Poglavlje 21: Paralelno izvršavanje 955
Kada je taj problem rešen, sledeći korak je naučiti kako da zadaci sarađuju jedan s dru-
gim, da bi više zadataka m oglo zajednički da reši problem . Sada se ne postavlja pitanje
kako da se zadaci m edusobno ne om etaju, nego kako da rade usaglašeno, pošto se takvi
p roblem i m oraju rešavati postepeno, utvrđenim redosledom . Kao u građevinarstvu: naj-
pre se m o ra iskopati rupa za temelje, ali se arm atura m ože položiti paralelno sa pra-
vljenjem betonskih blokova, i oba ta zadatka m oraju biti dovršena da bi se nalili temelji.
Cevi m oraju biti položene pre nalivanja betonske ploče, ploča m ora biti nalivena pre nego
što počne podizanje zidova itd. Neki od ovih zadataka m ogu se izvršavati paralelno, ali
određeni koraci ne m ogu se izvesti ukoliko svi zadaci nisu završeni.
U m eđusobnoj saradnji zadataka glavno pitanje je prim opredaja signala. Za tu p rim o-
predaju upotrebljavam o zajednički temelj: uzajam no isključivu bravu (m utex) koji u
ovom slučaju jem či da će na određeni signal odgovoriti sam o jedan zadatak. Tim e se iz-
begavaju svi eventualni uslovi za trku. Pored mutexa, dodaćem o način da se zadatak zau-
stavi do k se ne prom eni neko spoljno stanje (npr. „Cevi su položene“), koje pokazuje da
je vrem e da se taj zadatak pokrene. U ovom odeljku, razm otrićem o prim opredaju signala
izm eđu zadataka, koju bezbedno realizuju m etode w a it( ) i notifyA ll( ) klase Object. Bi-
blioteka za paralelno izvršavanje Jave SE5 ima i klasu Condition, s m etodam a aw ait( ) i
s ig n a l(). Videćemo koji problem i m ogu nastati i kako se rešavaju.
wait() i notifyAII()
M etoda w a it( ) se obično koristi dok čekate na prom enu uslova čije ispunjenje određuju
sile kojim a ne upravlja tekuća m etoda. Cesto neki drugi zadatak m enja taj uslov. Ne želite
da program stoji besposleno u niti, stalno ispitujući taj uslov; to se naziva zauzetost če-
kanjem (engl. busy vvaiting) i obično pređstavlja loš način korišćenja procesorskog vrem e-
na. Stoga w a it( ) om ogućava da uspavate tu nit dok čeka da se svet prom eni. O na se budi
i ispituje prom ene tek kad naiđe na m etode n o tify( ) ili notifyA ll( ) koje nagoveštavaju da
se m ožda desilo nešto važno. Tako je dobijen još jedan način sinhronizacije niti.
Važno je shvatiti da ni sleep( ) niti yield( ) ne otključavaju objekat kada ih pozovete.
S druge strane, kada zadatak iz neke m etode pozove m etodu w a it( ), ona otključava obje-
kat pre nego što zaustavi izvršavanje niti. Pošto w a it( ) otključava bravu čim je pozovete,
to znači da drugi zadaci mogu da pribave tu bravu, pa tokom izvršavanja m etode w a it ( )
možete da pozivate druge sinhronizovane m etode tog (sada otključanog) objekta. To je
neophodno, pošto obično baš te druge metode prouzrokuju p rom enu koju zaustavljena
nit čeka da bi se probudila. Dakle, kada pozovete w a it( ), saopštavate: „Zasad sam uradio
sve što sam mogao, pa ću na ovom m estu da pričekam , ali hoću da om ogućim drugim
sinhronizovanim operacijam a da se izvršavaju ako mogu.“
Postoje dva oblika m etode wait( ). Prvi prim a argum ent u milisekundam a, čije je
značenje jednako onom u m etodi sle ep (), dakle zaustavlja rad u zadatom periodu vreme-
na. Za razliku od prim ene m etode sle ep (), kada se koristi w ait(pauza), dešava se sledeće:
1. O tključava se objekat tokom čekanja izvršavanja m etode w a it( ).
2. Iz m etode w a it( ) možete da izađete i zbog obaveštenja koje šalju m etode n o tify () ili
notifyA U (), ili će se izvršavanje samo nastaviti po isteku zadatog vrem ena čekanja.
958 Misliti na Javi
Drugi, češće upotrebljavani oblik m etode wait( ) ne prim a nikakve argum ente, što
znači da će m etoda čekati sve dok ta n it ne prim i poruku n o tify ( ) ili notifyAIl( ); taj oblik
m etode w a it( ) ne završava zadatak autom atski posle isteka nekog vremena.
Jedna od posebnih osobina m etoda w a it( ), n o tify ( ) i notifyA ll( ) jeste to što su one
delovi osnovne klase Object, a ne klase Thread. M ada to na prvi pogled izgleda čudno
- da nešto što služi isključivo za niti b ude pripadnik univerzalne osnovne klase - ali je
neizbežno, jer te m etode rade s bravam a koje su takođe deo svakog objekta. Zato w a it( )
m ožete da smestite u svaku sinhronizovanu m etodu, bez obzira na to da li u toj konkret-
noj klasi postoji ili ne postoji više niti, tj. da li je izvedena iz klase Thread ili realizuje in-
terfejs Runnable. U stvari, w a it( ), n o tify ( ) i notifyAll( ) možete da pozovete jedino iz
sinhronizovanih m etoda ili blokova (sleep( ) možete pozivati i iz nesinhronizovanih me -
toda, pošto ona ne radi s bravam a). Ako bilo koju od tih m etoda pozovete iz m etode koja
nije sinhronizovana, program će se ispravno prevesti, ali ćete tokom izvršavanja dobiti
izuzetak tipa IllegalM onitorStateException uz pom alo nerazum ljivu poruku „current
thread not ow ner“ (tekuća nit tren u tn o nije vlasnik brave objekta s kojim hoće da radi).
Poruka znači da zadatak koji poziva w a it( ), notify( ) ili notifyA ll( ) m ora zaključati bravu
objekta pre nego što pozove jed n u od tih metoda.
M etode w a it( ), n o tify ( ) ili notifyA ll( ) možete da pozivate sam o za svoje brave. Nema
petljanja s tuđim bravama, ali od drugog objekta možete zatražiti da obavi operaciju koja
radi s njegovom bravom. P rethodno m orate da pribavite bravu tog objekta. Prim era radi,
ako objektu x želite da pošaljete obaveštenje notifyAll( ), to možete uraditi u n u ta r sinhro-
nizovanog bloka koji pribavlja bravu od x, tj. zaključava x:
synchronized(x) {
x.notifyAll();
}
Razm otrićem o jednostavan prim er. VoskOMatik.java ima dva procesa: nanošenje
voska na Kola i poliranje. Poliranja nem a dok se ne završi nanošenje voska, a zadatak na-
nošenja m ora da sačeka dok se zadatak poliranja završi - tek onda se može naneti sledeći
sloj voska. I Voskalma i VoskaNema rade sa istim objektom tipa Kola koji m etodam a
w a it( ) i notifyA ll( ) zaustavlja i ponovo pokreće zadatke dok oni čekaju da se prom eni
određeni uslov:
//: paralelno/voskomatik/VoskOMatik.java
// Osnove međusobne saradnje zadataka.
package paralelno.voskomatik;
import java.util.concurrent.*;
import static net.mindview.uti1 .Print.*;
class Kola {
private boolean voskalma = false;
public synchronized void voskiranoO {
voskalma = true; // Spremno za poliranje
noti fyAl1 ();
Poglavlje 2 1: Paralelno izvršavanje 959
Kola im aju samo jednu prom enljivu tipa boolean pod im enom voskalm a koja po-
kazuje stanje procesa nanošenja voska i poliranja.
U m etodi cekajNaVoskiranje( ) proverava se indikator voskalm a, i ako je njegova
vrednost false, pozivajući zadatak se zaustavlja tako što se poziva m etoda w a it( ). Važno
je da se to dešava u sinhronizovanoj m etodi, gde je zadatak pribavio bravu. Kada pozovete
w a it( ), nit se zaustavlja, a brava otključava. N eophodno je da brava bude otključana zato
što drugi zadaci m oraju im ati m ogučnost da je pribave (zaključaju) da bi stanje tog
objekta moglo biti bezbedno prom enjeno (na prim er, da bi vrednost indikatora voska-
Im a prom enili u true, što m oram o da uradim o da bi zaustavljeni zadatak uopšte dobio
priliku da nastavi rad). U ovom prim eru, kada drugi zadatak pozove voskirano( ) da bi
pokazao kako je vreme da se nešto uradi, brava m ora biti pribavljena (zaključana) da bi
se vrednost indikatora voskalma prom enila u true. Zatini vosk iran o ( ) poziva metodu
notifyA ll( ) koja budi zadatak zaustavljen pozivom m etode w a it( ). Da bi se zadatak pro-
budio iz stanja čekanja koje prouzrokuje m etoda w a it( ), najpre m ora ponovo da zaključa
bravu koju je otključao kada je ušao u taj w a it( ). Zadatak se neće probuditi dok ta brava
ne postane dostupna, tj. otključana.20
20 Na nckim platfo rm am a postoji i treći naćin izlaska iz m etode w a it(): to je takozvano shičajno
budetije. U suštini, slučajno b u đ en je p ro u zro k u je nit koja p rera n o prestane da blokira (dok čeka na
prom enljivu ili sem afor uslova), a da je nisu pro b u d ile m etode notify( ) ili n otifyA ll() (ili njihovi
ekvivalenti za objekte nove klase Condition). N it se jednostavno p ro b u d i, naizgled sam a od sebe.
Slučajno b u đ en je postoji zato što realizacija PO SlX niti (ili njinia ekvivalentnih) na nekim plat-
fo rm am a nije onoliko jednostavna koliko bi Sun hteo da bude. No, posao pisanja p th read biblioteke
za te p latfo rm e m n o go je lakši ako se proglasi da slučajna bu denja ,,ne sm etaju toliko“.
Poglavlje 21: Paralelno izvršavanje 961
Propušteni signali
Kada je rad dve niti usklađen m etodam a notify( )/w a it() ili notifyAH( )/w a it(), može se
propustiti signal. Pretpostavim o da je N1 nit koja obaveštava nit N2 i da su obe realizo-
vane na sledeći (pogrešan) način:
962 Misliti na Javi
N1 :
synchronized(deljeniMoni tor) {
<pripremo u slo v a za N2>
deljeniMonitor.notify();
}
N2:
while(nekillslov) {
// Tačka 1
synchronized(deljeniMonitor) {
deljeniMonitor.wait();
}
}
<priprema uslova za N2> treba da spreči N2 da zove w a it(), ako to N2 već nije učinila.
Pretpostavim o da N2 izračuna nekiUsIov i dobije rezultat true. U Tačkil, m ehanizam
za raspodelu procesorskog vrem ena može prebaciti na nit N l. N1 izvršava svoju pripre-
m u i zatim poziva n o tify (). Kada N2 nastavi da se izvršava, za nju je prekasno da shvati
kako se uslov u m eđuvrem enu prom enio, i ona slepo ulazi u w a it(). Poruka m etode
n o tify () biče propuštena i N2 će čekati beskonačno dugo na signal koji je već bio poslat;
dobili sm o uzajam nu blokadu (engl. deadlock).
Rešenje je da se spreči uslov za trku prim en om prom enljive nekiUslov. Ovo je ispra-
van način realizacije N2:
synchronized(deljeniMonitor) {
while(nekiUslov)
deljeniMonitor.wait();
}
U ovoj realizaciji važi sledeće: ako nit N1 prva dobije priliku da se izvršava, kada se
kontrola vrati niti N2 ona će shvatiti da se uslov prom enio i neće ući u w a it(). U protiv-
nom , ukoliko nit N2 prva dobije priliku da se izvršava, ući će u w a it() i kasnije će je pro-
buditi N l. Dakle, signal ne može biti propušten.
class Blokator {
synchronized void cekatnPoziv() {
try {
while(!Thread.interrupted()) {
wait();
System.out.print(Thread.currentThread() + " ");
}
} catch(InterruptedException e) {
// Ovo je prihvatljiv način izlaska
}
}
synchronized void probudi() { notify(); }
synchronized void probudiSve() { notifyAll(); }
}
} else {
System.out.print("\nnotifyAl 1() ");
Zadatak.blokator.probudiSve();
probudi = true;
}
}
}, 400, 400); // Pokreni na svake 0,4 sekunde
Timellnit.SEC0NDS.sleep(5); // Neka radi neko vreme...
meracVremena.cancel();
System.out.println("\nMerač vremena otkazan");
TimeUni t .MILLISECONDS.sleep(500);
System.out.print("Zadatak2.blokator.probudiSve() ");
Zadatak2.blokator.probudiSve();
TimeUnit.MILLISEC0NDS.sleep(500);
System.out.println("\nGasim");
exec.shutdownNow(); // Prekini sve zadatke
}
} /* Ispis: (primer)
notify() Thread[pool-l-thread-l,5,main]
notifyAl1() Thread[pool-l-thread-l,5,main] Thread[pool-l-thread-5,5,main]
Thread[pool-l-thread-4,5,main] Thread[pool-l-thread-3,5,main] Thread[pool
-l-thread-2,5,main]
notify() Thread[pool-l-thread-l,5,main]
notifyAl1() Thread[pool-1-thread-l,5,main] Thread[pool-l-thread-2,5,main]
Thread[pool-l-thread-3,5,main] Thread[pool-l-thread-4,5,main] Thread[pool
-l-thread-5,5,main]
notify() Thread[pool-1-thread-l,5,main]
notifyAl1 () Thread[pool-1-thread-l,5,main] Thread[pool-l-thread-5,5,main]
Thread[pool-l-thread-4,5,main] Thread[pool-l-thread-3,5,main] Thread[pool
-l-thread-2,5,main]
notify() Thread[pool-l-thread-l,5,main]
notifyAl1 () Thread[pool-l-thread-l,5,main] Thread[pool-l-thread-2,5,main]
Thread[pool-l-thread-3,5,main] Thread[pool-l-thread-4,5,main] Thread[pool
-l-thread-5,5,main]
notify() Thread[pool-1-thread-l,5 ,main]
notifyAll() Thread[pool-1-thread-l,5,main] Thread[pool-l-thread-5,5,main]
Thread[pool-l-thread-4,5,main] Thread[pool-l-thread-3,5,main] Thread[pool
-l-thread-2,5,main]
notify() Thread[pool-1-thread-l,5,main]
notifyAl1 () Thread[pool-1-thread-1,5,main] Thread[pool-l-thread-2,5,main]
Thread[pool-l-thread-3,5,main] Thread[pool-l-thread-4,5,main] Thread[pool
-l-thread-5,5,main]
Merač vremena otkazan
Zadatak2.blokator.probudiSve() Thread[pool-l-thread-6,5,main]
Gasim
*///:-
Zadatak i Zadatak2 imaju sopstvene Blokator objekte, pa svaki objekat tipa Zada-
tak.blokator blokira svaki objekat tipa Zadatak, i svaki objekat tipa Zadatak2.blokator
blokira svaki objekat tipa Zadatak2 . U m etodi m a in (), objekat tipa java.util.Timer biva
Poglavlje 21: Paralelno izvršavanje 965
Proizvođači i potrošači
Zam islite restoran s jednim kuvarom i jednim konobarom . Konobar m ora da sačeka dok
kuvar prip rem i jelo. Kuvar obaveštava konobara kada je jeio zgotovljeno. Tada konobar
uzim a jelo, poslužuje ga pa se vraća i čeka. To je prim er saradnje zadataka: kuvar predsta-
vlja proizvođača, a konobar potrošača. Zadaci m oraju da razm enjuju signale tokom
proizvodnje i potrošnje jela, i sistem m ora da se uredno ugasi. Ovo je ta priča oblikovana
kao kod:
//: paralelno/Restoran.java
// Saradnja zadataka kao saradnja proizvođača i potrošača.
import java.util.concurrent.*;
import static net.mindview.util .Print.*;
class Jelo {
private final int brojNarudzbe;
public Jelo(int brojNarudzbe) { this.brojNarudzbe = brojNarudzbe; }
public String toStringf) { return "Jelo " + brojNarudzbe; }
}
whi1e(uslovNijelspunjen)
wai t();
968 Misliti na Javi
Time se jem či da če uslov biti ispunjen pre nego što izađete iz petlje čekanja, i ako ste
obavešteni o nečem u što nem a veze sa uslovom - što se m ože desiti kada je poruka
n o tifyA ll() - ili se uslov pro m eni pre nego što p o tp u n o izađete iz petlje čekanja, svakako
ćete se vratiti u čekanje.
Vodite računa o tom e da poziv n o tify A ll() m ora najpre zaključati bravu konobara.
Poziv m etode w a it() iz m etode K onobar.run() autom atski otkJjučava bravu, pa je to
moguće. Pošto brava m ora biti zaključana da bi n o tify A ll() m ogla biti pozvana, dva
zadatka koji pokušavaju da pozovu n o tify A ll() za isti objekat zajemčeno neće zasmetati
jedan drugom .
O be m etode r u n () uredno se gase tako što je cela m etoda r u n () u m etnuta u blok try.
O dredba catch završava se neposredno pre završne vitičaste zagrade m etode r u n (), pa
ako zadatak prim i InterruptedException, završiće se o dm ah čim uhvati izuzetak.
U klasi Kuvar, obratite pažnju na to da bism o nakon poziva m etode sh utdow n N ow ()
običnom naredbom return mogli da se jednostavno vratim o iz m etode r u n ( ), i to
najčešće i treba da uradite. M eđutim , ovako je m alo zanimljivije. Prisetite se da
sh utdow n N ow () šalje in terru p t() svim zad ađ m a koje je ExecutorService pokrenuo. Ali
u slučaju klase Kuvar, zadatak se ne gasi neposredno po prijem u poruke in terru p tf),
pošto prekid m ože da generiše izuzetak InterruptedException jedino kada zadatak
pokuša da uđe u (neprekidnu) operaciju koja blokira. Zato se najpre prikazuje „Evo
narudzbe!“ i zatim baca izuzetak InterruptedException kada Kuvar pokuša da pozove
sle e p (). Ako uklonite poziv m etode sle e p (), zadatak će doći na vrh petlje r u n () i izaći
zbog ispitivanja vrednosti m etode Thread.interrupted(), a da ne baci izuzetak.
U p retho dn om prim eru, zadatak na sam o jedno m esto m ože da smesti objekat koji će
drugi zadatak kasnije upotrebiti. M eđutim , u tipičnoj realizaciji proizvodača i potrošača,
za skladištenje objekata koji se proizvode i troše prim enjuje se princip FIFO (fir$t-inyfirst-
out —prvi izlazi onaj koji je prvi ušao). Više reči o takvim redovim a za čekanje biće u na-
stavku poglavlja.
Vežba 24: (1) M etodam a w a it() i n o tify A ll() rešite problem jednog proizvođača i jed-
nog potrošača. Proizvođač ne sme da prepuni prim aočev bafer, što se može desiti ukoliko
je proizvođač brži od potrošača. Ako je potrošač brži od proizvođača, onda isti podatak
treba da pročita samo jednom . Ne sm ete ništa pretpostaviti o relativnim brzinam a
proizvođača i potrošača.
Vežba 25: (1) U klasi Kuvar program a Restoran.java, vratite se naredbom return iz me-
tode r u n () nakon poziva m etode sh u td ow n N o w () i posm atrajte razliku u ponašanju.
Vežba 26: (8) Program u Restoran.java dodajte klasu Pom ocnik. Kada posluži jelo, Ko-
nobar treba da obavesti P om ocnika da počisti.
Zadatak zaustavljate pozivom m etode a w ait() za objekat tipa Condition. Kada nastanu
spoljne prom ene stanja koje m ožda znače da neki zadatak treba da nastavi izvršavanje, taj
zadatak obaveštavate poru ko m sig n a l(), odnosno sign alA ll() da biste probudili sve
zadatke koji su se zaustavili do ispunjenja uslova tj. tog objekta C ondition (kao i
notifyA Il(), sign alA ll() je bezbednije koristiti).
Evo kako izgleda program VoskOMatik.java preraden tako da sadrži C ondition po-
m oću kojeg se određeni zadatak zaustavlja u n u ta r m etoda cekajNaVoskiranje() ili
cekajN aPoliranje():
//: paralelno/voskomatik2/VoskOMatik2.java
// Upotreba objekata Lock i Condition.
package paralelno.voskomatik2;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import static net.mindview.util.Print.*;
class Kola {
private Lock brava = new ReentrantLock();
private Condition uslov = brava.newCondition();
private boolean voskalma = false;
public void voskirano() {
brava.lockO ;
try {
voskalma = true; // Spremna za poliranje
usl o v .signalAll ();
} finally {
b r ava . u n l o c k O ;
}
}
public void polirano() {
brava.1ock();
try {
voskalma = false; // Spremna za nanošenje sledećeg sloja voska
uslov.signalA11 ();
} fi nal 1y {
brava.unlock();
U ko nstruktoru Kola, jedna brava (objekat tipa Lock) proizvodi uslov, tj. objekat tipa
C ondition pom oću kojeg se upravlja kom unikacijom izm eđu zadataka. M eđutim , obje-
kat C ondition ne sadrži inform acije o stanju procesa, pa su potrebne dodatne inform a-
cije kojim a pokazujete stanje procesa, a to je prom enljiva voskalm a tipa boolean.
Iza svakog poziva m etode lo c k () m ora neposredno slediti odredba try-finally koja
jem či da če se u svim slučajevima ostvariti otključavanje. Kao u ugrađenim verzijama,
zađatak m ora zaključati bravu da bi mogao da pozove a w a it(), sig n a l() ili sig n a lA ll().
Im ajte u vidu da je ovo rešenje složenije nego prethodno, a ta složenost u ovom slučaju
nije donela ništa dobro. Objekti tipa Lock i Condition potrebni su sam o za teže problem e
višenitnog izvršavanja.
Vežba 27: (2) Izmenite Restoran.java tako da korisli eksplicitne objekte tipa Lock i
Condition.
//: paralelno/TestBlokirajucihRedovaZaCekanje.java
// {RunByHand}
import java.util.concurrent.*;
import java.io.*;
import static net.mindview.util.Print.*;
static void
test(String prk, BlockingQueue<Lansiranje> redZaCekanje) {
print(prk);
IzvrsavaLansiranje izvrsilac = new IzvrsavaLansiranje(redZaCekanje);
Thread n = new Thread(izvrsilac);
n.start();
for(int i = 0 ; i < 5 ; i++)
izvrsilac.add(new Lansiranje(5));
getkey("Pritisnite 'Enter' (" + prk + ")");
n.i nterrupt();
print("Završen " + prk + " test");
}
public static void main(String[] args) {
test("LinkedBlockingQueue", // Neograničena veličina
new LinkedBlockingQueue<Lansiranje>());
test("ArrayBlockingQueue", // Nepromenljiva veličina
new ArrayBlocking0ueue<Lansiranje>(3));
test("SynchronousQueue", // Veličine 1
new SynchronousQueue<Lansiranje>());
}
} ///:-
M etoda m a in () smešta zadatke u BlockingQueue, iz kojeg ih vadi m etoda Izvr-
savaLansiranje. O bratite pažnju na to da IzvrsavaLansiranje može da zaboravi na
sinhronizaciju zato što to rešava BlockingQueue.
Vežba 28: (3) Izmenite TestBlokirajucihRedovaZaCekanje.java dodavanjem novog
zadatka koji stavlja Lansiranje u BlockingQueue, um esto da se to radi u m etodi m a in ().
//: paralelno/TostOMatik.java
// Toster koji upotrebljava redove za čekanje.
import java.util.concurrent.*;
import java.uti1 .*;
import static net.mindview.uti1 .Print.*;
class Tost {
public enum Status { SUV, SMASLACEM, SPEKMEZOM }
private Status status = Status.SUV;
private final int id;
public Tost(int idn) { id = idn; }
public void namaziMaslacem() { status = Status.SMASLACEM; }
public void namaziPekmezom() { status = Status.SPEKMEZOM; }
public Status dajStatus() { return status; }
974 Misliti na Javi
print("MazeMaslacem isključen");
}
}
// Potroši tost:
class Gost implements Runnable {
private RedZaCekanjeTosta redGotovih;
private int brojac = 0;
public Gost(RedZaCekanjeTosta gotovi) {
redGotovih = gotovi;
}
public void run() {
try {
while(!Thread.interrupted()) {
// Blokira se dok ne dobije sledeće parče tosta:
Tost n = redGotovih.take();
// Proveri da li tost uredno dolazi,
// i da li sva parčad bivaju namazana pekmezom:
if(n.dajld() != brojac++ ||
n.dajStatus() != Tost.Status.SPEKMEZOM) {
p r i n t ( " » » Greška: " + n);
System.exit(l);
} else
print("Njam! " + n);
}
} catch(InterruptedException e) {
976 Misliti na Javi
print("Gost prekinut");
}
print("Gost isključen");
}
}
Tost je odličan prim er vrednosti nabrojanih tipova. Vodite računa o tom e da nema
eksplicitne sinhronizacije (ne upotrebljavaju se objekti tipa Lock niti rezervisana reč syn-
chronized), pošto sinhronizaciju im plicitno (interno) obavljaju redovi za čekanje i
projekat sistema - svako parče Tosta u svakom trenutku predm et je obrade sam o jednog
zadatka. Pošto redovi za čekanje blokiraju izvršavanje, procesi se zaustavljaju i nastavljaju
autom atski. N adam se da vidite kako pojednostavljenje koje su doneli blokirajuči redovi
za čekanje um e da bude veom a veliko. Izbegnute su veze izm eđu klasa koje bi postojale uz
eksplicitne naredbe w a it() i n o tifyA ll(), jer svaka klasa kom unicira samo sa svojim blo-
kirajućim redom za čekanje.
Vežba 29; (8) Izmenite TostOMatik.java tako da na dve zasebne linije pravi sendviče od
tosta nam azanog maslacem i m arm eladom (jedna za tost nam azan maslacem, druga za
m arm eladu, zatim spojite linije).
//: paralelno/UICevovod.java
// Ulazno/izlazna komunikacija zadataka kroz cevi
import java.util.concurrent.*;
Poglavlje 21: Paralelno izvršavanje 977
import java.io.*;
import java.util.*;
import static net.mindview.util.Print.*;
Uzajamna blokada
Sada znate da objekat može im ati sinhronizovane m etode ili druge oblike zaključavanja
koji sprečavaju zadatke da pristupaju tom objektu dok se ne otkijuča uzajam no isključiva
brava (mutex). Saznali ste i da zadaci m ogu postati blokirani. Dakle, m oguće je da se je-
dan zadatak zaglavi čekajući na drugi, koji ćeka na treći itđ., dok lanac ne đođe do zadatka
koji čeka na onaj prvi zadatak - neprekidan lanac zadataka koji čekaju jedan na drugog i
nijedan od njih ne može da se pom eri. To se naziva uzajamna blokada (engl. deadlock).1'
Ako pokrenete program i on odm ah uđe u uzajam nu blokadu, grešku ćete odm ah
moći da pronađete. Pravi problem je kada program naizgled radi dobro, ali sadrži skrive-
nu m ogućnost uzajam ne blokade. U tom slučaju, uopšte ne m orate dobiti nikakvu na-
znaku da m ogućnost uzajam ne blokade postoji, pa će greška biti latentna u program u
dok se neočekivano ne desi kupcu (na način koji će gotovo sigurno biti teško ponoviti).
Zato je pažljivo projektovanje program a kako bi se sprečila uzajam na blokada, ključan
deo razvoja paralelnih sistema.
Klasičan prim er uzajam ne blokade jeste problem večercfilozofa, koji je smislio Edsger
Dijkstra. U njegovoj verziji, filozofa im a pet, ali u prim eru koji ću ja ovde opisati, broj fi-
Iozofa je proizvoljan. Ti filozofi deo vrem ena provode u razm išljanju, a deo u jelu. Dok
razmišljaju, ne koriste deljene resurse, ali za jelo imaju na raspolaganju ograničenu
//: paralelno/Stapic.java
// Štapići za večeru filozofa.
//: paralelno/Fi1ozof.java
// Filozof večera
import java.util.concurrent.*;
import java.uti1 .*;
import static net.mindview.uti 1 .Print.*;
//: paralelno/UzajamnoBlokiraniFilozofiKojiVeceraju.java
// Pokazuje kako u programu može biti prikrivena uzajamna blokada.
// {Args: 0 5 odmor}
import java.util .concurrent.*;
Poglavfje 2 1: Paralelno izvršavanje 981
Prim etili ste sledeće: ukoliko Filozofi provode malo vrem ena razmišljajući, kada po-
kušaju da jedu svi će jedan drugom konkurisati za Stapice i uzajam na blokada će nastu-
piti m nogo brže.
Prvi argum ent na kom andnoj liniji zadaje težinski faktor ponder koji utiće na količinu
vrem ena koje svaki Filozof provodi u razmišljanju. Ako Filozofa im a m nogo ili oni vaz-
dan razmišljaju, m ožda uopšte nećete videti uzajam nu blokadu, iako je ona i dalje m o-
guća. N ula kao argum ent na kom andnoj liniji čini da program prilično brzo uđe u
uzajam nu blokadu.
O bratite pažnju na to da objektim a tipa Stapic nisu potrebni interni identifikatori;
njih identifikuje njihov položaj u nizu stapici. K onstruktor svakog objekta tipa Filozof
dobija referencu na levi i desni Stapic. Svaki Filozof (sem poslednjeg) u inicijalizaciji biva
smešten izm edu sledećeg para Stapica. Poslednji Filozof dobija nulti Stapic kao svoj de-
sni i tako se obišao krug oko stola - naime, poslednji Filozof sedi odm ah do prvog i obo-
jica dele nulti Stapic. Ukoliko sada svi Filozofi istovrem eno pokušaju da jedu, svaki od
njih će m orati da čeka dok susedni Filozof ne spusti svoj Stapic. Program će zato ući u
uzajam nu blokadu.
Ukoliko Filozofi više vrem ena provode u razmišljanju nego u jelu, onda je i verovat-
noća da će im zatrebati deljeni resursi (Stapici) m nogo m anja, i vi ćete biti uvereni da pro-
gram ne m ože ući u uzajam nu blokadu (ako za ponder zadate vrednost različitu od nule
ili veliki broj Filozofa), iako to nije istina. Ovaj prim er je zanimljiv upravo zato što pokazu-
je da program može naizgled da radi ispravno, a da u stvari krije uzajam nu blokadu.
982 Misliti na Javi
//: paralelno/PopravljeniFi1ozofiKojiVeceraju.java
// Filozofi koji večeraju bez uzajamne blokade.
// {Args: 5 5 odmor}
import java.util.concurrent
U zajam nu blokadu sm o uklonili tako što poslednji Filozof sada uzima i pušta levi Sta-
pic pre desnog, i program će raditi dobro.
Java nem a podršku za sprećavanje uzajam ne blokade; m orate je sami izbeći tako što
ćete pažljivije projektovati program . To nisu utešne reči za onoga ko pokušava da otkrije
i otkloni grešku iz program a koji upada u uzajam nu blokadu.
Vežba 31: (8) Izmenite program UzajamnoBiokiraniFilozofiKojiVeceraju.java na sle-
deći način: kada filozof upotrebi štapiće, neka ih ne spušta na sto nego u zdelu. Kada fi-
lozof hoće da jede, uzim a prva dva dostupna štapića iz te zdele. Da li je tim e izbegnuta
m ogućnost uzajam ne blokade? Da li ćete prostim sm anjenjem broja dostupnih štapića
ponovo uvesti m ogućnost uzajam ne blokade?
to odbrojavanje se obično radi kada zadatak obavi svoj posao. CountDownLatch se ko-
risti jednokratno; vrednost se ne m ože resetovati. Kada vam zatreba verzija koja resetuje
vrednost, upotrebite CyclicBarrier.
Zadaci koji pozovu co u n tD o w n () ne blokiraju se zbog tog poziva. Blokiran je sam o
poziv m etode aw ait() dok se vrednost ne izjednači s nulom .
Ova klasa se obično koristi tako što se problem podeli na n nezavisno rešivih zadataka
i napravi objekat tipa CountDownLatch kojem se dodeli početna vrednost n. Svaki
završen zadatak poziva co u n tD o w n () za taj objekat. Zadaci koji čekaju da se problem reši
pozivaju a w ait() za taj objekat, da bi sačekali dok problem bude rešen. Tu tehniku prika-
zuje sledeći prim er:
// Čeka na CountDownLatch:
class ZadatakKojiCeka implements Runnable {
private static int brojac = 0;
private final int id = brojac++;
private final CountDownLatch brava;
ZadatakKojiCeka(CountDownLatch brava) {
this.brava = brava;
Poglavjje 21: Paralelno izvršavanje 985
}
public void run() {
try {
brava.await();
print(this + " prošao barijeru brave");
} catch(InterruptedException izz) {
print(this + " prekinut");
}
}
public String toStringO {
return String.format("ZadatakKojiCeka %l$-3d ", id);
}
}
DeoZadatka spava tokom nasum ično odabranog vrem enskog razdoblja i tako sim u-
lira izvršavanje dela zadatka, a ZadatakKojiCeka predstavlja deo sistema koji m ora da
sačeka dok se prvi deo problem a ne reši. Svi zadaci rade sa istim objektom tipa Count-
DownLatch definisanim u m etodi m a in ().
Vežba 32: (7) Upotrebite CountDownLatch za korelaciju rezultata raznih Ulaza u pri-
m eru UkrasnaBasta.java. Iz nove verzije prim era uklonite nepotreban kod.
CyclicBarrier
Klasu CyclicBarrier koristite u situacijam a kada hoćete da napravite gru p u zadataka koji
se izvršavaju paralelno, a zatim da sačekate dok se svi oni ne završe pre nego što pređete
na sledeći korak (nešto kao j o in ( ), recim o). Prilikom prolaska kroz barijeru svi paralelni
zadaci su završeni, pa napred m ožete da krenete sa rezultatim a svih njih. To je veom a slič-
no klasi CountDownLatch, sem što je objekat tipa CountDownLatch za jed n o k ratn u
upotrebu, dok se objekat tipa CyclicBarrier može koristiti više puta.
Simulacije m e fasciniraju od prvih susreta s računarom , a paralelno izvršavanje je
ključni faktor koji om ogućuje simulacije. Prvi program koji sam napisao22 bila je sim ula-
cija: igra s konjskim trakam a napisana u BASIC-u, nazvana (zbog ograničene dužine
im ena datoteka) HOSRAC.BAS. Sledi objektno orijentisana, višenitna verzija tog progra-
ma, u kojoj je upotrebljena klasa CyclicBarrier:
//: paralelno/TrkaKonja.java
// Upotreba klase CyclicBarrier.
import java.util.concurrent.*;
import java.util.*;
import static net.mindview.util.Print.*;
O bjektu tipa CyclicBarrier m ože biti priđ od ata „akcija barijere", a to je klasa koja rea-
lizuje interfejs Runnable i izvršava se autom atski kada se (na početku zadat) broj izjed-
nači s nulom - to je druga razlika izm eđu klasa CyclicBarrier i CountdownLatch. Ovde
je akcija barijere anonim na klasa koja se daje k o nstru k to ru objekta tipa CyclicBarrier.
Probao sam da postignem da svaki konj odštam pa kada je stigao na cilj, ali se u tom
slučaju redosled prikazivanja m enja u zavisnosti od m ehanizm a za raspodelu procesor-
skog vrem ena nitim a (zadacim a). Klasa CydicBarrier om ogućuje svakom konju da radi
sve što treba kako bi došao do cilja, a zatim m ora da čeka na barijeri dok ne dođu svi ostali
konji. Kada svi konji stignu, objekat tipa CyclicBarrier autom atski poziva svoju akciju
barijere, tj. zadatak koji realizuje interfejs Runnable i prikazuje konje redom kojim su
stigli, kao i ogradu trkališta.
Kada svi zadaci prođ u barijeru, ona je autom atski sprem na za sledeću rundu.
Ako želite da postignete efekat veom a jednostavne animacije, sm anjite prozor konzole
tako da se vide sam o konji.
De!ayQueue
Ovo je neograničen BlockingQ ueue (blokirajući red za čekanje) objekata koji realizuju
interfejs Delayed, tj. svaki je odložen na neko vreme (engl. delayed). O bjekat može biti
uzet iz reda za čekanje tek kada njegovo vrem e odlaganja istekne. Red je tako uređen da
je čeonom objektu preostalo vrem e odlaganja koje se najviše skratilo od početka izvrša-
vanja. Ako se nijedno vrem e odlaganja nije skratilo, onda nem a čeonog elem enta i meto-
da p o ll( ) vraća null (zato u taj red za čekanje ne m ožete stavljati null elemente).
U sledećem prim eru D elayed objekti su i sami zadaci, a P otrosacO dlozenihZ adataka
iz rada za čekanje uzim a najhitniji zadatak (onaj kojem se vreme odlaganja najviše skra-
tilo od početka izvršavanja) i izvršava ga. Dakle, D elayQ ueue je varijacija prioritetnog
reda za čekanje.
//: paralelno/DelayQueuePrimer.java
import java.util.concurrent.*;
import java.uti1 .*;
import static java.util.concurrent.TimeUnit.*;
import static net.mindview.util.Print.*;
new ArrayList<OdlozeniZadatak>();
public OdlozeniZadatak(int odlaganjeUMilisekundama) {
delta = odlaganjeUMilisekundama;
okidac = System.nanoTime() +
NANOSECONDS.convert(delta, MILLISECONDS);
sekvenca.add(this);
}
public long getDelay(TimeUnit jedinica) {
return jedinica.convert(
okidac - System.nanoTime(), NANOSECONDS);
}
public int compareTo(Delayed arg) {
OdlozeniZadatak onaj = (OdlozeniZadatak)arg;
if(okidac < onaj.okidac) return -1;
if(okiđac > onaj.okidac) return 1;
return 0;
}
public void run() { printnb(this + " "); }
public String toString() {
return St ri ng .f or ma t("[%l$-4d]", delta) +
" Zadatak " + id;
}
public String sazetak() {
return "(" + id + + delta + ")";
}
public static class StrazarNaCilju extends OdlozeniZadatak {
private ExecutorService exec;
public StrazarNaCilju(int odlaganje, ExecutorService e) {
su pe r( od la ga nj e);
exec = e;
}
public void run() {
for(OdlozeniZadatak iz : sekvenca) {
p r i n t n b ( i z .s a z e t a k O + " ");
}
pri nt ();
print(this + " poziva shutdo wn No w( )");
ex e c . s h u td ow nN ow ();
}
NAN0SEC0NDS.convert(delta, MILLISECONDS);
U metodi getD elay(), željena jedinica se prosleduje kao argum ent jedinica i pom oću
nje vreme proteklo od okidača konvertujem o u jedinice koje zahteva pozivalac, a da čak i
ne znam o koje su. (Ovo je jednostavan prim er projektnog obrasca Strategy, u kojem se
deo algoritm a prosleđuje kao argum ent.)
Poglavlje 21: Paralelno izvršavanje 991
PriorityBlockingQueue
U suštini, ovo je p rio ritetn i red za čekanje čije se operacije vađenja iz reda blokiraju. U na-
rednom prim eru, objekti u prioritetn o m redu za čekanje su zadaci koji se iz reda za če-
kanje pojavljuju po redosledu njihovih prioriteta. ZadatakSPrioritetom dobija broj
prioriteta iz kojeg sledi taj redosled:
//: paralelno/PriorityBlockingQueuePrimer.java
import java.uti1.concurrent.*;
import java.uti 1 .*;
import static net. mi nd vi ew .u ti1.P r i n t .*;
class Za da ta kS Pr io ri te to m implements
Runnable, Co mp ar ab le <ZadatakSPrioritetom> (
private Random slucajan = new Random(47);
private static int brojac = 0;
private final int id = brojac++;
private final int prioritet;
protected static List<ZadatakSPrioritetom> sekvenca =
new A r r a y L i s t < Za da ta kS Pri or it et om >( );
public ZadatakSPrioritetom(int prioritet) {
thi s .priori tet = prioritet;
sekvenca.addfthi s ) ;
}
public int co mp ar eT o(ZadatakSPrioritetom arg) {
return prioritet < arg.prioritet ? 1 :
(prioritet > a r g .prioritet ? -1 : 0);
}
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(sl uc a j a n . n e x t l n t (250));
) catch(InterruptedException e) {
// Prihvatljiv način izlaska
}
pri nt(thi s ) ;
992 Misliti na Javi
//: paralelno/RasporedjivacStaklenika.java
// Prerada programa unutrasnjeklase/KontrolerStaklenika.java
// u kojoj je upotrebljen ScheduledThreadPoolExecutor.
// (Args: 5000}
import java.util.concurrent.*;
import java.uti1.*;
podaci,add(new RadnaTacka((Calendar)pos1ednjiPut.clone(),
poslednjaTemp, poslednjaVlaznost));
}
}
}
public static void main(String[] args) {
RasporedjivacStaklenika st = new RasporedjivacStaklenika();
st.raspored(st.new Gasi(), 5000);
// Ranije klase "Restart" nisu potrebne:
st.ponavljaj(st.new Zvono(), 0, 1000);
st.ponavljaj(st.new TermostatNoc(), 0, 2000);
st.ponavljaj(st.new UkljuciSvetlo(), 0, 200);
st.ponavljaj(st.new IskljuciSvetlo(), 0, 400);
st.ponavljaj(st.new UkljuciVodu(), 0, 600);
st.ponavljaj(st.new IskljuciVodu(), 0, 800);
st.ponavljaj(st.new TermostatDan(), 0, 1400);
st.ponavljaj(st.new PrikupiPodatke(), 500, 500);
}
} /* (Pokrenite da biste videli rezultat) *///:-
Ova verzija reorganizuje kod i dodaje jednu novinu: prikupljanje m erenja tem pera-
ture i vlažnosti u stakieniku. RadnaTacka sađrži i prikazuje jedan skup merenja, dok je
PrikupiPodatke planiran zadatak koji generiše sim ulirane podatke i dodaje ih listi
List<RadnaTacka> u Stakleniku nakon svakog svog izvršavanja.
O bratite pažnju na to da se m odifikatori volatile i synchronized upotrebljavaju na
odgovarajućim m estim a kako bi se sprečilo da zadaci sm etaju jedan drugom . Prilikoin
pravljenja liste koja sadrži RadneTačke, sve m etode bivaju sinhronizovane uslužnom me-
todom synchronizeđL ist() iz biblioteke java.util.Collections.
Vežba 33: (7) Izm enite RasporedjivacStaklenika.java tako da koristi DeIayQueue um e-
sto ScheduledExecutora.
Semafor
O bična brava (eksplicitna iz concurrent.locks ili ugrađena synchronized) u svakom tre-
nutku dozvoljava sam o jed no m zadatku da pristupi svakom resursu. Setnafor brojač om o-
gućuje da n zadataka istovrem eno pristupa jednom resursu. Možete sm atrati da semafor
izdaje dozvole za korišćenje odredenog resursa, m ada se zapravo ne upotrebljavaju nikak-
vi objekti dozvola.
Kao prim er, razm otrićem o pojam grupe objekata koja upravlja ograničenim brojem
objekata tako što dozvoljava da b ud u pojedinačno izdati na u p otrebu i p otom vraćeni
kada korisnik završi svoj posao s njima. Te funkcije ćem o kapsulirati u generičkoj klasi:
//: paralelno/Grupa.java
// Semafor u grupi objekata koji ograničava broj zadataka
// koji istovremeno mogu koristiti resurs.
import java.uti1 .concurrent.*;
import java.uti1.*;
998 Misliti na Javi
Niz izdat tipa boolean prati objekte koji su izdati na korišćenje, a njim e upravljaju me-
tode uzm iStavku() i pustiStavku(). N jih pak čuva sem afor (objekat tipa Semaphore)
dostupan, tako da u m etodi izdajObjekatNaKoriscenje(), dostupan blokira poziv uko-
liko više nem a dostup nih dozvola za korišćenje objekata iz grupe (što znači da u grupi
više nem a objekata). M etoda prim iObjekatNazadUGrupu() vraća dozvolu sem aforu
ukoliko je objekat koji se vraća validan.
Kao p rim er upotrebićem o Debeli, tip objekta koji je skupo praviti zato što njegovom
konstruktoru treba m nogo vrem ena da obavi svoj posao:
O kupićem o ove objekte u grupu da bism o ograničili uticaj tog konstruktora. Klasu
Grupa ćem o testirati pravljenjem zadatka koji objekte tipa Debeli uzima na korišćenje,
drži ih neko vrem e i zatim vraća nazad:
//: paralelno/PrimerSemafora.java
// Testiranje klase Grupa
import java.uti1.concurrent.*;
import java.util.*;
import static net.mindview.util.Print.*;
Exchanger
Exchanger (razmenjivač) jeste barijera koja om ogućava da se m eđusobno zam ene objekti
dvaju zadataka. Kada zadaci uđu u barijeru, im aju svaki po jedan objekat, a kada izađu,
im aju objekat koji je prethodno im ao onaj drugi zadatak. Ovakvi razmenjivači se obično
koriste kada jedan zađatak pravi objekte čija je proizvodnja skupa, a drugi zadatak ih
troši; na taj način, može se napraviti više objekata koji se istovrem eno troše.
Da bism o upotrebili klasu Exchanger, napravićem o zadatke proizvođača i potrošača
koji preko generičkih tipova i Generatora rade sa svim vrstam a objekata, i zatim ćem o ih
prim eniti na klasu Debeli. Klase RazmenjivacProizvodjac i RazmenjivacPotrosac kori-
ste objekat tipa List<T> za razm enu; svaka od njih sadrži Exchanger za ovu listu
List<T>. Kada pozovete m etodu Exchanger.exchangc( ), ona blokira zadatak dok part-
nerski zadatak ne pozove svoju m etodu exch an ge(). Kada obe m etode exchan ge() završe
svoj posao, lista tipa List<T> biće zamenjena:
//: paralelno/ExchangerPrimer.java
import java.uti1 .concurrent.*;
import java.uti1.*;
import net.mindview.util.*;
U m etodi m a in (), pravi se jedan Exchanger koji koriste oba zadatka, i dve liste tipa
CopyOnWriteArrayList za razm enu. Ova varijanta objekta tipa List toleriše pozivanje
m etode rem ove() tokom prolaska kroz listu (ne baca ConcurrentModificationExcepti-
on). RazmenjivacProizvodjac popunjava jednu listu, zatim zamenjuje p u n u listu praz-
nom koju m u prosleduje RazmenjivacPotrosac. Klasa Exchanger čini da popunjavanje
jedne liste i trošenje druge m ogu da se odvijaju istovremeno.
Vežba 34: (1) Izm enite ExchangerPrimer.java tako da umesto klase Debeli upotrebite
neku svoju.
Simulacija
Jedna od najzanimljivijih i najuzbudljivijih prim ena paralelnog izvršavanja jeste pra-
vljenje simulacija. Zbog paralelnosti, svaka kom ponenta simulacije može biti zaseban
zadatak, a sim ulaciju je zato m nogo lakše program irati. Mnoge video igrice i CGI anim a-
cije u film ovim a su simulacije, a i ranije prikazani program i TrkaKonja.java i Raspored-
jivacStaklenika.java mogu se sm atrati sim ulacijama.
try {
while(!Thread.interrupted{)) {
Klijent klijent = klijenti.take();
TimeUnit.MILLISECONDS.sleep(
kli jent.dajVremeUsluzivanjaO);
synchronized(this) {
usluzenihKlijenata++;
while(!redKlijenataSeUsluzuje)
wait();
}
}
} catch(InterruptedException e) {
System.out.println(this + "prekinut");
}
System.out.println(this + "završava");
}
public synchronized void radiNestoDrugo() {
usluzenihKlijenata = 0;
redKlijenataSeUsluzuje = false;
}
public synchronized void usluzujRedKlijenata() {
assert !redKlijenataSeUsluzuje:"već uslužujem: " + this;
redKlijenataSeUsluzuje = true;
notifyAl1 ();
}
public String toString() { return "Službenik " + id + " "; }
public String shortStringO { return "T" + id; }
// Upotrebljava prioritetni red za čekanje:
public synchronized int compareTo(Sluzbenik drugi) {
return usluzenihKlijenata < drugi.usluzenihKlijenata ? -1 :
(usluzenihKlijenata == drugi.usluzenihKlijenata ? 0 : 1);
}
}
exec.execute(sluzbeni k);
radiSluzbenika.add(sluzbenik);
}
public void prilagodiBrojSluzbenika() {
// Ovo je zapravo upravljački sistem. Menjanjem brojeva otkrićete
// nestabilne radne tačke upravljačkog mehanizma.
// Ako je red predugačak, dodaj još jednog službenika:
if(klijenti.size() / radiSluzbenika.size() > 2 ) {
// Ako su službenici na pauzi ili rade nešto drugo,
// vrati jednog nazad:
if(sluzbenikaKojiRadeNestoDrugo.size() > 0) {
Sluzbenik sluzbenik = sluzbenikaKojiRadeNestoDrugo.remove();
sluzbenik.usluzujRedKl ijenata();
radiSluzbenika.offer(sluzbenik);
return;
}
// U protivnom napravi (zaposli) novog službenika
Sluzbenik sluzbenik = new Sluzbenik(klijenti);
exec.execute(sluzbenik);
radiSluzbenika.add(sluzbenik);
return;
}
// Ako je red dovoljno kratak, ukloni jednog službenika:
if(radiSluzbenika.size() > 1 &&
klijenti.size() / radiSluzbenika.size() < 2)
premestiJednogSluzbeni ka();
// Ako reda uopšte nema, treba nam samo jedan službenik:
if (kl ijenti .size() == 0)
whi1e(radiS1uzbenika.size() > 1)
premestiJednogSluzbenika();
}
// Daj službeniku drugi posao ili ga pošalji na pauzu:
private void premestiJednogSluzbenikaf) {
Sluzbenik sluzbenik = radiSluzbenika.pol1();
sluzbeni k .radi NestoDrugo();
sluzbeni kaKojiRadeNestoDrugo.offer(sluzbeni k);
}
public void run() {
try {
while(!Thread.interrupted()) {
TimeUni t.MILLISECONDS.sleep(periodPri1agodjavanja);
prilagodiBrojSluzbenika();
System.out.print(klijenti + " { ");
for(Sluzbenik sluzbenik : radiSluzbenika)
System.out.print(sluzbenik.shortString() + " ");
System.out.pri ntln("}");
}
} catch(InterruptedException e) {
System.out.println(this + "prekinut");
Poglavlje 21: Paralelno izvršavanje 1007
System.out.println(this + "završava")-
}
public String toStringO { return "RukovodilacSluzbenika }
}
O bjekti tipa Klijent veoma su jednostavni, pošto sađrže sam o jedno final int polje.
Pošto se ti objekti nikada ne menjaju, oni su samo za čitanje i ne zahtevaju sinhronizaciju
niti m odifikator volatile. Sein toga, svaki zadatak Službenik u svakom tren u tk u uklanja
1008 Misliti na Javi
sam o po jedan objekat tipa Klijent iz ulaznog reda i uslužuje ga dok ne završi, pa svakom
objektu tipa Klijent u svakom tren u tk u ionako p ristu p a sam o po jedan zadatak.
RedKlijenata, kako m u im e kaže, čine klijenti koji čekaju da ih usluži neki objekat tipa
Službenik. To je sam o objekat tipa ArrayBlockingQueue s m etodom to S trin g () koja is-
pisuje rezultate u željenom obliku.
O bjektu tipa RedKlijenata p ridru žen je jedan GeneratorKlijenata koji u nasum ičnim
vrem enskim intervalim a dodaje klijente u red.
Službenik uzim a klijente iz objekta tipa RedKIijenata i obrađuje ih jednog po jednog,
pam teći broj klijenata koje je uslužio tok om određene sm ene. Može m u se poručiti da
radiN estoD rugo() kada nem a dovoljno klijenata i usluzujRedKlijenata( ) kada dođe
m nogo klijenata. Službenika koji će biti vraćen na opsluživanje ulaznog reda bira m etoda
com pareTo() koja poredi broj usluženih klijenata da bi p rio ritetn i red za čekanje (Prio-
rityQueue) m ogao autom atski da stavi najm anje opterećenog službenika napred.
RukovodilacSluzbenika upravlja svim aktivnostima. On prati sve službcnike i nadgleda
šta rade klijenti. Jedna od zanimljivosti ove simulacije jeste pokušaj otkrivanja optim alnog
broja službenika za dati dotok klijenata. To možete videti u metodi prilagodiBrojSluzbe-
n ik a() koja je upravljački sistem za dodavanje i uklanjanje službenika na stabilan način. Svi
upravljačld sistemi m oraju da paze na stabilnost; ako na prom enu reaguju prebrzo, izgubiće
stabilnost, a ako reaguju presporo, sistem će preći u jedan od ekstrema (red klijenata
maksimalne dužine, a ne rade svi dostupni službenici ili su zaposleni svi službenici iako kli-
jenata nem a).
Vežba 35: (8) Izm enite program SimuIacijaSalterskogSluzbenika.java tako da predsta-
vlja Web klijente koji šalju zahteve o dređenom broju servera. Treba utvrditi opterećenje
koje ta grupa servera može da savlada.
Simulacija restorana
Jednostavan prim er Restoran.java prikazan u prethodnom delu poglavlja obogatiću u
ovoj simulaciji dodavanjem više kom ponenata kao što su objekti tipa Narudzba i
DeoObroka; pored toga, ponovo ću upotrebiti klase jelovnik iz poglavlja Nnbrojani tipovi.
Predstaviću i Java SE5 klasu SynchronousQueue što je blokirajući red za čekanje nultog
internog kapaciteta, pa svaki poziv m etode p u t() m ora da čeka na svoj poziv m etode take()
i obrnuto. Kao da nekome treba da predate neki predm et, a nema stola na koji biste ga mo-
gli staviti - uspećete jedino u slučaju da ta osoba pruži ka vama ruku da uzme predmet.
U ovom prim eru, SynchronousQueue predstavlja prostor neposredno ispred restoranskog
gosta, kako bi se naglasilo da u svakom trenutku može biti posluženo sam o jedno jelo.
Ostale klase i funkcije u ovom prim eru slede iz strukture program a R estoran.java ili
predstavljaju prilično neposredno preslikavanje operacija pravog restorana:
exec.execute(kuvar);
}
}
public void run() {
try {
while(!Thread.interrupted()) {
// Došao je novi gost; dodeli rnu Konobara:
Konobar knbr = konobari.get(
slucajan.nextInt(konobari,size()));
Gost k = new Gost(knbr);
exec.execute(k);
TimeUnit.MILLISECONDS.sleep(100);
}
} catch(InterruptedException e) {
print("Restoran prekinut");
}
print("Restoran se zatvara");
}
}
*///:-
Poglavlje 21: Paralelno izvršavanje 1013
Jedna od veom a važnih stvari na koje u ovom prim eru treba obratiti pažnju jeste upra-
vljanje složenošću tako što su za kom unikaciju izm eđu zadataka upotrebljeni redovi za
čekanje. Već sama ta tehnika uveliko pojednostavljuje postupak paralelnog program iranja
tako što invertuje kontrolu: zadaci se ne obraćaju jedan drugom neposredno, nego jedni
drugim a šalju objekte preko redova za čekanje. Zadatak koji je prim io objekat obrađuje ga
i tretira ga kao poruku, um esto da ga podvrgava poruci. Ukoliko se budete pridržavali tog
načina rada, imaćete m nogo više šanse da pravite robusne paralelne sisteme.
Vežba 36: (10) Izm enite RestoranSRedovima.java tako da za svaki sto postoji po jedan
objekat tip a Narudzbenica. Prepravite klasu narudzba u narudzbenica i dodajte klasu
Sto, s više G ostiju za stolom.
Raspodela posla
Naredni prim er je sim ulacija koja obuhvata više koncepata iz ovog poglavlja. Zamislite
robotizovanu m ontažnu liniju za autom obile. Svaka Kola se proizvode u nekoliko faza,
počev o d pravljenja šasije, preko m ontaže m otora, pogonskog sistema i točkova.
//: paralelno/ProizvodnjaKola.java
// Složen primer saradnje zadataka.
import java.uti1 .concurrent
import java.util.*;
import static net.mindview.uti 1.Print.*;
class Kola {
private final int id;
private boolean
motor = false, pogonskiSistem = false, tockovi = false;
public Kola(int idn) { id = idn; }
// Prazan objekat tipa Kola:
public Kola() { id = -1; }
public synchronized int dajId() { return id; }
public synchronized void dodajMotor() { motor = true; }
public synchronized void dodajPogonskiSistem() {
pogonskiSistem = true;
}
public synchronized void dodajTockove() { tockovi = true; }
public synchronized String toStringO {
return "Kola " + id + " [" + " motor: " + motor
+ " pogonskiSistem: " + pogonskiSistem
+ " tockovi: " + tockovi + " ]";
print("Monter isključen");
}
class GrupaRobota {
// (Nečujno) sprečava identične stavke:
private Set<Robot> grupa = new HashSet<Robot>();
public synchronized void add(Robot r) {
grupa.add(r);
noti fyAl1 ();
}
public synchronized void
unajmi(Class<? extends Robot> tipRobota, Monter d)
throws InterruptedException {
for(Robot r : grupa)
if (r.getClassO .equals(tipRobota)) {
Poglavlje 21: Paralelno izvršavanje 1017
grupa.remove(r);
r.dodeliMontera(d);
r.angazuj(); // Ukljući ga da obavi posao
return;
}
wait(); // Nema dostupnih
unajmi(tipRobota, d); // Rekurzivno pokušaj ponovo
}
public synchronized void release(Robot r) { add(r); )
}
Kola se prenose s m esta na mesto pom oću objekta tipa RedKola, što je tip reda Lin-
kedBlockingQueue. PravljenjeSasije pravi kostur Kola i sm ešta ga u RedKola. Monter
skida Kola i/. RedaKola i unajm ljuje Robote da na njim a rade. Barijera tipa CyclicBarrier
om ogućuje Monteru da čeka dok svi unajm ljeni Roboti ne b u d u gotovi, a potom stavlja
Kola u izlazni RedKoIa koji ih prenosi za sledeću operaciju. Potrošač završnog objekta
tipa RedKola jeste Izvestilac koji sam o ispisuje sastav Kola da bi pokazao da su svi zadaci
pravilno izvršeni.
Robotima se upravlja u grupi, i kada nešto treba da se uradi, odgovarajući Robot biva
unajmljen iz grupe. Nakon dovršetka posla, Robot se vraća u grupu.
U m etodi m a in () prave se svi potrebni objekti i inicijalizuju svi zadaci; poslednje se
pokreće PravljenjeSasije da bi se pokrenuo proces. (M eđutim , ponašanje reda Linked-
BIockingQueue je takvo da sam m ogao njega prvog da pokrenem .) O bratite pažnju na to
da ovaj pm gram sledi sve sm ernice u vezi sa životuim ciklusom objekata i zadataka koje
su spom enute u ovom poglavlju, pa je postupak gašenja bezbedan.
Uočili ste da su sve m etode Kola sinhronizovane. Ispostavlja se da je to u ovom primeru
redundantno, pošto su Kola unutar proizvodnje uvek u nekom od redova za čekanje i na
svakim kolima u svakom trenutku može raditi sam o jedan zadatak. U suštini, redovi za če-
kanje nam eću serijsko pristupanje objektima tipa Kola. Aii upravo to i jeste zamka - mogli
1018 Misliti na Javi
biste reći: „Hajde da optimizujemo perform anse tako što klasu Kola nećem o sinhronizovati,
pošto izgleda da to ovde nije neophodno". Ali kasnije, kada ovaj sistem bude povezan s ne-
kim drugim koji zahteva da Kola bud u sinhronizovana, sve će se raspasti.
Brian Goetz komentariše:
M nogo je lakše reći„Kola m ogu biti upotrebljena iz više niti, pa hajde da ih napravim o
očigledno bezbednim za višenitno izvršavanje". Evo kako ja to posm atram : na strm im
m estim a u parkovim a postoje zaštitne ograde i natpisi „Z abranjeno naslanjanje na
zaštitnu ogradu“. Naravno, prava svrha ovog pravila nije da spreči naslanjanje na ogradu,
nego da spreči padanje niza strm inu. Ali je m nogo lakše pridržavati se pravila „Zabranje-
no naslanjanje na ogradu“ nego pravila „Zabranjeno padanje niza strminu".
Vežba 37: (2) Izmenite program ProizvodnjaKola.java dodavanjem još jedne faze u
postupak pravljenja kola, u kojoj se dodaju izduvni sistem, karoserija i branici. Kao i u
drugoj fazi, pretpostavite da te procese roboti m ogu da obavljaju istovremeno.
Vežba 38: (3) Koristeći pristup iz program a ProizvodnjaKola.java, m odelujte priču o
gradnji kuće koja je data u ovom poglavlju.
Optimizacija performansi
Z natan broj klasa u biblioteci Jave SE5 java.util.concurrent postoji radi poboljšanja
perform ansi. Tokom prelistavanja biblioteke concurrent, teško ćete razlučiti klase na-
m enjene za redovnu upotrebu (kao što su redovi BlockingQueue) od klasa koje su sam o
za poboljšavanje perform ansi. U ovom odeljku razm otrićem o neka od tih pitanja i klasa
za optim izaciju performansi.
’ ’ Brian G oetz m i je fflnogo pom ogao da to shvatim . Više info rm acija o m eren ju p erfo rm an si pročitajte
u njegovom ćlanku na ađresi w w w -t28.ibm .com /devdoperw orks/librarY /j-jtpl2214.
1020 Misliti na Javi
da shvati kako se brojac povećava fiksan broj p u ta pa da unapred izračuna rezultat. Ima
različitih prevodilaca i sistema za izvršavanje, pa je teško reći šta će se tačno dogoditi, ali
m oram o sprečiti m ogućnost da prevodilac predvidi ishod.
D a bi testiranje bilo validno, program m ora biti složeniji. Prvo m oram o napraviti više
zadataka, i to ne sam o onih koji m enjaju interne vrednosti, već i onih koji te vrednosti či-
taju (inače b i optim izator m ogao prepoznati da se te vrednosti nikada ne koriste). Pored
toga, izračunavanje m ora biti toliko složeno i nepredvidljivo da prevodilac nem a šanse da
obavi agresivnu optim izađju. To ćem o postići prethodnim učitavanjem velikog niza
pseudoslučajnih celih brojeva (p reth od no učitavanje sm anjuje uticaj poziva m etode
R andom .nextInt() na glavne petlje) i sabiranjem tih brojeva:
//: para'le'lno/PoredjenjeSinhronizacija.java
// Poređenje performansi pri upotrebi eksplicitnih objekata tipa Lock
// i Atomic i performansi pri korišćenju rezervisane reči synchronized.
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.*;
import static net.mindvievv.uti 1 .Print .*;
} /* Ispis: (primer)
Zagrevanje
OsnZ aP or : 34237033
Ciklusa : 50000
OsnZ aP or : 20966632
synchronized : 24326555
Lock : 53669950
Atomi c : 30552487
synchroni zed/OsnZaPc r 1.16
Lock/OsnZaPor 2.56
Atomi c/OsnZaPor 1.46
synchroni zed/Lock 0.45
synchronized/Atomic 0.79
Lock/Atomi c 1.76
Ciklusa : 100000
OsnZaPor : 41512818
synchronized : 43843003
1024 Misliti na Javi
Lock 87430386
Atomi c 51892350
synchroni zed/OsnZaPor : 1.06
Lock/OsnZaPor : 2.11
Atomic/OsnZaPor : 1.25
synchronized/Lock : 0.50
synchronized/Atom 'c : 0.84
Lock/Atomic : 1.68
Ciklusa 200000
OsnZaPor 80176670
synchronized 5455046661
Lock 177686829
Atomic 101789194
synchroni zed/OsnZaPor 68.04
Lock/OsnZaPor 2.22
Atomic/OsnZaPor 1.27
synchronized/Lock 30.70
synchroni zed/Atomi c 53.59
Lock/Atomi c 1.75
Ci klusa 400000
OsnZaPor 160383513
synchronized 780052493
Lock 362187652
Atomi c 202030984
synchronized/OsnZaPor 4.86
Lock/OsnZaPor 2.26
Atomi c/OsnZaPor 1.26
synchroni zed/Lock 2.15
synchroni zed/Atomi c 3.86
Lock/Atomic 1.79
Ci klusa 800000
OsnZaPor 322064955
synchroni zed 336155014
Lock 704615531
Atomi c 393231542
synchronized/OsnZaPor : 1.04
Lock/OsnZaPor 2.19
Atomi c/OsnZaPor 1.22
synchronized/Lock 0.47
synchronized/Atomic : 0.85
L o c k /Atomic 1.79
Ci klusa 1600000
OsnZaPor 650004120
synchroni zed 52235762925
Lock 1419602771
Atomi c 796950171
Poglavlje 2 1: Paralelno izvršavanje 1025
sy nchronized/OsnZaPor : 80.36
Lock/OsnZaPor : 2.18
A tomic/OsnZaPor : 1.23
synchronized/Lock : 36.80
synchronized/Atomic : 65.54
Lock/Atomic : 1.78
Ciklusa : 3200000
OsnZaPor : 1285664519
synchronized : 96336767661
Lock : 2846988654
Atomic : 1590545726
synchronized/OsnZaPor : 74.93
Lock/OsnZaPor : 2.21
Atomic/OsnZaPor : 1.24
synchronized/Lock : 33.84
synchronized/Atomic : 60.57
Lock/Atomic : 1.79
*///:-
U metodi m a in (), testiranje se ponavlja i možete odlučiti da zatražite više od pet (po-
drazumevanih) ponavljanja. U svakom ponavljanju, broj ciklusa testiranja se udvo-
stručuje, pa možete videti kako se razne uzajamno isključive brave (m utexi) ponašaju
kada se izvršavaju sve duže i duže. Kao što vidite iz izlaza, rezultati su prilično iznenađu-
ju ći. U prve četiri iteracije izgleda da je rezervisana reč synchronized efikasnija od klasa
Lock i Atomic. Ali iznenada neka granica biva prekoračena pa deluje da synchronized
postaje veoma neefikasna, dok Lock i Atom ic kao da približno održavaju svoju srazmeru
prema testu OsnZaPor (osnova za poređenje) i stoga postaju mnogo efikasniji od syn-
chronized.
Im ajte u vidu da ovaj program daje tek naznaku razlike između raznih realizacija uza-
jam no isključivih brava i da gornji izlaz pokazuje te razlike samo na m om konkretnom
računaru pod m ojim konkretnim okolnostima. Kao što ćete videti kada budete eksperi-
mentisali s ponašanjem, ono se znatno menja u zavisnosti od broja niti i trajanja izvrša-
vanja programa. Neke optimizacije vrućih tačaka pozivaju se tek nekoliko minuta nakon
početka izvršavanja programa, a u slučaju serverskih programa, nakon nekoliko sati.
Uza sve to, prilično je jasno da je klasa Lock najčešće znatno efikasnija od rezervisane
reči synchronized, a izgleda i da se režijski troškovi upotrebe rezervisane reči synchroni-
zed znatno meniaju, dok oni klase Lock ostaju relativno ujednačeni.
Da li to znači da rezervisanu reč synchronizeđ uopšte ne treba upotrebljavati? U obzir
morate uzeti dva činioca: prvo, u primeru PoredjenjeSinhronizacija.java tela mutex
metoda su izuzetno mala. Uopšte uzev, to je dobro - uzajamno isključite samo one delove
programa koje apsolutno morate. Međutim, uzajamno isključeni delovi u praksi mogu
biti veći nego u gornjem primeru, te će i procentualni deo vremena koji se provodi u telu
metoda verovatno biti znatno veći nego režijski troškovi od ulaska i izlaska iz uzajamno
isključive brave, a to bi moglo da poništi eventualno poboljšanje koje prouzrokuje
ubrzanje uzajamno isključive brave. Naravno, istinu ćete saznati tek kada isprobate
različite pristupe i vidite njihov uticaj, a to bi trebalo da radite tek prilikom optimizacije
performansi.
Drugo, ko pročita kod u ovom poglavlju videće da rezervisana reč synchronized
proizvodi mnogo čitljiviji kod od idioma zaključaj-try/finally-otključaj, za koji su po-
trebne brave tipa Lock, i zato sam u ovom poglavlju uglavnom koristio synchronized. Na
više mesta u ovoj knjizi rekao sam da se kod mnogo češće čita nego piše — u programi-
ranju je važnije da kod razumeju drugi ljudi nego računar - te je čitljivost koda ključna.
Zato ima smisla početi s rezervisanom reči synchronized i preći na objekte tipa Lock tek
prilikom optim izacije performansi.
Najzad, lepo je kada možete da upotrebite klase Atomic u paralelnom programu, ali
morate voditi računa o tome da su, kao što smo videli u programu PoređenjeSinhroni-
zacija.java, Atomic objekti korisni samo u veoma jednostavnim slučajevima, po pravilu
samo kada imate jedan Atom ic objekat koji menjate i kada je taj objekat nezavisan od svih
o s ta lih o b je k a ta . B e z b e d n ij e je k r e n u ti s tr a d i c i o n a l n ij im u z a ja m n o is k lju č iv im bra\ 'ain a
i pokušati prelazak na Atoinic objekte kasnije, ukoliko to budete morali da učinite zbog
performansi.
Poglavlje 2 1: Paralelno izvršavanje 1027
O performansama
Ukoliko iz kontejnera bez zaključavanja uglavnom čitate, biće to mnogo brže nego da či-
tate iz njegove sinhronizovane verzije, zato što izbegavate režijske troškove od zaključa-
vanja i otključavanja. To važi i za mali broj upisivanja u kontejner bez zaključavanja, ali bi
bilo zanimljivo steći neku predstavu koliko je to ,,malo“. U ovom odeljku daćemo grubu
slik u o r a z lik a m a u p e r io r m a n s a m a tili k o n te jn e r a p o d r a z lič itim u s lo v im a .
1028 Misliti na Javi
//: paralelno/Tester.java
// Osnovna struktura za te stiranje performansi paralelnih kontejnera.
import java.util.concurrent.*;
import net.mindview.util.*;
Prethodnu strukturu (u kojoj ste prepoznali projektni obrazac Template Methođ) ko-
ristite tako što iz klase Tester izvedete konkretni tip kontejnera koji želite da testirate i na-
pravite odgovarajuće klase Citalac i Upisivac:
} /* Ispis: (primer)
Tip Vreme čitanja Vreme upis.
Sinhro. ArrayList 10c Ou 232158294700 0
Sinhro. ArrayList 9c lu 198947618203 24918613399
vremeCit + vremeUpis = 223866231602
Sinhro. ArrayList 5c 5u 117367305062 132176613508
vremeCit + vremeUpis = 249543918570
CopyOnWriteArrayList 10c Ou 758386889 0
CopyOnWriteArrayList 9c lu 741305671 136145237
vremeCit + vremeUpis = 877450908
CopyOnWriteArrayList 5c 5u 212763075 67967464300
vremeCit + vremeUpis = 68180227375
*/// =-
vremeCit += trajanje;
}
}
class Upisivac extends ZadatakTestiranja {
void test() {
for(long i = 0; i < ciklusaTestiranja; i++)
for(int indeks = 0; indeks < velicinaKontejnera; indeks++)
ko nt ej nerZaTestiranje.put(indeks, up is iP odatke[indeks]) ;
}
void upisiRezultate() {
vremeUpis += trajanje;
}
}
void po kr en i C i t a o c e l U p i s i v a c e O {
for(int i = 0; i < nCitalaca; i++)
exec.execute(new C i t a l a c O ) ;
for(int i = 0; i < nUpisivaca; i++)
exec.execute(new U p i s i v a c O ) ;
}
Optimističko zaključavanje
Pored toga što objekti tipa Atomic izvršavaju atomske operacije kao što je decrement-
AndGet( ), neke Atomic klase omogućuju i tzv. optimističko zaključavanje. To znači da
prilikom izračunavanja ne koristite uzajamno isključivanje, ali da nakon završetka izraču-
navanja za ažuriranje Atomic objekta koristite metodu com pareA ndSet(). Prosleđujete
jo j staru i novu vrednost, i ako se stara vrednost ne podudara s vrednošću pronađenom u
Atomic objektu, operacija se završava neuspehom - to znači da je neki drugi zadatak u
međuvremenu izmenio objekat. Ne zaboravite da inače uvek upotrebljavamo mutex (re-
zervisanu reč synchronized ili objekat tipa Lock) i tako sprečavamo da više zadataka isto-
vremeno modifikuje objekat, ali ovde se ponašamo optimistički tako što podatke
ostavljamo nezaključane i nadamo se da drugi zađaci neće uleteti i modifikovati ih. Narav-
no, sve to radimo samo zbog performansi - korišćenjem objekta tipa Atomic umesto re-
zervisane reči synchronized ili objekta tipa Lock, verovatno ćete poboljšati performanse.
Šta se dešava ako operacija com pareA ndSet() zakaže? Stvari postaju čupave, pa ovu
tehniku možete da prim enjujete samo na projekte koje možete krojiti kako god potrebe
nalažu. Ako com pareA ndSet() zakaže, morate da odlučite šta da radite; to je veoma važ-
n o , p o š t o u s lu ć a ju d a je o p o r a v a k n e m o g u ć , u m e s to o v e te h n ik e m o r a t e d a u p o tie b U e
neku od konvencionalnih brava za uzajamno isključivanje (m utexa). Možda možete po-
kušati da ponovite operaciju i neće smetati ako ona (tek) drugi put uspe. Ili možda može-
te da zanemarite neuspeh - u nekim simulacijama, gubitak jedne tačke ne znači ništa za
celinu, jer je broj tačaka ogroman. (Naravno, model morate poznavati toliko dobro da
znate je li prethodna hipoteza istinita.)
Poglav[je 2 !: Paralelno izvršavanje 1035
Zamislimo fiktivnu simulaciju koja se sastoji od 100 000 ,,gena“ dužine 30; recimo da je
to početak nekakvog genetičkog algoritma. Pretpostavimo da je za svaku ,,evoluciju“ gene-
tičkog algoritma potreban veoma skup proračun, pa ste odlučili da upotrebite višeproce-
sorski računar, rasporedite zadatke među procesorima i tako poboljšate performanse. Sem
toga, umesto objekata tipa Lock koristite Atomic objekte da biste izbegli režijske troškove
uzajamnog isključivanja. (Naravno, program ste najpre napisali na najjednostavniji mo-
gući način, pomoću rezervisane reči synchronized. Tek kada je takav program proradio,
otkrili ste da je prespor i počeli da primenjujete tehnike za poboljšavanje performansi!)
Zbog prirode ovog modela, zadatak koji otkrije sudar tokom proručuna, može da ga zane-
mari i da ne ažurira svoju vrednost. Evo kako to izgleda:
//: pa ralelno/BrzaSimulacija.java
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import j a v a . u t i l .*;
import static n e t . mi nd vi ew .u ti l.Print.*;
Sve elemente smo stavili u niz, zato što sm atram o da će to poboljšati performanse. (Tu
pretpostavku ćemo testirati u jed noj od vežbi.) Svaki objekat tipa Evolver uprosečava
svoju vrednost s prethodnom i sa sledećom, i ako ažuriranje ne uspe, on ispisuje tu vred-
nost i ide dalje. Imajte u vidu da u programu nema uzajamnog isključivanja.
Vežba 39: (6) Da li su pretpostavke u programu BrzaSimulacija.java razumne? Izmenite
niz tako da sadrži proste cele brojeve umesto objekata tipa Atom iclnteger i upotrebite
Lock brave za uzajamno isključivanje (mutexe). Uporedite performanse te dve verzije
programa.
ReadWriteLock
ReadVVriteLock optimizuje situaciju kada u strukturu podataka upisujete relativno ret-
ko, ali iz nje više zadataka često čita. Klasa ReadVVriteLock om ogućuje da do prvog
pokušaja upisivanja istovremeno radi više čitalaca. Nakon zaključavanja brave za pisanje,
čitaoci ne mogu da rade sve dok se brava za pisanje ponovo ne otključa.
Nemoguće je unapred reći da li će ReadVVriteLock poboljšati performanse vašeg pro-
grama; to zavisi od činilaca kao što su srazmera broja čitanja i broja modifikovanja poda-
taka, trajanje operacije čitanja i upisivanja (brava je složenija, pa kratke operacije ne daju
da se vidi poboljšanje), količina takm ičenja među nitima i da li se program izvršava na
višeprocesorskom računaru. Na kraju krajeva, jedini način da saznate da li ReadWri-
teLock poboljšava vaš program jeste da ga isprobate.
U narednom primeru prikazana je samo najjednostavnija upotreba klase ReadVVri-
teLock, tj. zasebnih brava za čitanje i upisivanje:
//: paralelno/ListaCitalacalUpisivaca.java
import ja v a . u t i 1 .concurrent.*;
import java.util.concurrent.locks.*;
import ja v a . u t i l .*;
import static n e t . mi nd vi ew .u ti l.Print.*;
new R e en tr an tR ea dW ri te Loc k( tr ue );
public ListaCitalacaIUpisivaca(int velicina, T pocetnaVrednost) {
zakljucanaLista = new ArrayList<T>(
Collections.nCopies(velicina, p o c e tn aV re dn os t) );
}
public T set(int indeks, T element) {
Lock bravaZaUpisivanje = b r av a. wr it eL oc k( );
br av aZ aU pi si va nj e. loc k( );
try {
return zakljucanaLista.set(indeks, element);
} finally {
b r a v a Z a U pi si va nj e. unl oc k( );
}
}
public T get(int indeks) {
Lock bravaZaCitanje = b r a v a . r e a d L o c k ( ) ;
br a v aZ aC it an je .l oc k();
try {
// Dokaz da više čitalaca može zaključati
// (pribaviti) bravu za čitanje:
if(brava.getReadLockCount() > 1)
p r i n t ( br av a. ge tR ea dLo ck Co un t());
return zakljucanaLista.get(indeks);
} finally {
br av aZ aC it an je .u nl ock ();
}
}
public static void main(String[] args) throws Exception {
new Te stListeCitalacaIUpisivaca(30, 1);
}
}
class TestListeCitalacalUpisivaca {
ExecutorService exec = E x e c u t o r s . ne wC ac he dTh re ad Po ol();
private final static int VELICINA = 100;
private static Random slucajan = new Random(47);
private Li staCitalacalUpisivaca<Integer> lista =
new ListaCitalacaIUpisivaca<Integer>(VELICINA, 0);
private class Upisivac implements Runnable {
public void run() {
try {
for(int i = 0; i < 20; i++) { // test od 2 sekunde
1 i s ta .s et (i, s l u c a j a n .n ex tl nt () );
TimeUni t. M I L L I S E C O N D S .s l e e p (1 0 0 ) ;
}
} catch(InterruptedException e) {
// Prihvatljiv način izlaska
}
1038 Misliti na Javi
Aktivni objekti
Kada proučite ovo poglavlje, možda ćete steći utisak da je u Javi veoma složeno i teško ko-
rektno sprovesti višenitni rad. Pored toga, izgleda i pomalo kontraproduktivno - iako
zadaci rade paralelno, morate uložiti mnogo truda da realizujete tehnike za sprečavanje
tih zadataka kako ne bi smetali jedan drugom.
Ako ste ikada pisali u asembleru, pisanje višenitnih programa izaziva isti osećaj: važna
je svaka sitnica, sve morate sami da uradite i nema zaštite u vidu provere koju sprovodi
prevodilac.
Da problem nije u samom modelu višenitnog rada? Na kraju krajeva, on je preuzet re-
Iativno nepromenjen iz sveta proceduralnog programiranja. Možda postoji drugačiji mo-
del paralelnog rada, prikladniji za objektno orijentisano programiranje.
Jedan od alternativnih pristupa nazvan je aktivni objekti ili aktori.26 Objekti su proglaše-
ni ,,aktivnim“ zato što svaki objekat održava sopstvenu radnu nit i red za poruke; svi za-
htevi koji se odnose na taj objekat ulaze u njegov red za čekanje i izvršavaju se jedan po
jedan. Dakle, kod aktivnih objekata serijalizujemoporuke, a ne tnetode, što znači da više ne
moramo da se štitimo od problema koji nastaju kada se zadatak prekine usred petlje.
Kada aktivnom objektu pošaljete poruku, ona se transformiše u zadatak koji ulazi u ob-
jektov red za čekanje na kasnije izvršavanje. Za realizaciju te šeme podesna je klasa Future
Jave SE5. Evo jeđnostavnog primera u kojem dve metode stavljaju pozive u red za čekanje:
//: paralelno/PrimerAktivnogObjekta.java
// Može da prosledi samo konstante, nepromenljive, "nevezane
// objekte" ili druge aktivne objekte kao argumente
// asinhronim metodama.
import j a v a .ut i 1 .co nc u r r e n t .
import j a v a . u t i l .*;
import static net.mindvievv.uti 1 .P r i n t .*;
public Future<Integer>
calculatelnt(final int x, final int y) {
return izvr si1ac.submit(new C a l1able<Integer>() {
public Integer c a l l () {
print("pokrećem " + x + 11 + " + y ) ;
s a c e k a j (500);
return x + y;
}
});
}
public Future<Float>
calculateFloat(final float x, final float y) {
return izvrsilac.submit(new Callable<Float>() {
public Float call () {
print("pokrećem " + x + " + " + y);
s a c e k a j (2000);
return x + y;
}
});
}
public void shutdown() { izvrsi la c. sh ut do wn (); }
public static void main(String[] args) {
Primer Ak tivnogObjekta pl = new Prim er Ak ti vn og Ob je kta ();
// Sprečava izuzetak ConcurrentModificationException:
L i s t < F u t u r e < ? » rezultati =
new C o p y O n W r i t e A r r a y L i s t < F u t u r e < ? » ( ) ;
for(float f = O.Of; f < l.Of; f += 0.2f)
r e z u l t a t i ,add(pl.calculateFloat(f, f ) ) ;
forfint i = 0 ; i <5; i++)
r e zu lt at i. ad d( pl .c alc ul at el nt (i, i ) ) ;
print("Završeni pozivi svih asinhronih metoda");
while(rezultati.size() > 0) {
fo r( Future<?> f : rezultati)
if ( f .is D on e( )) {
try {
print(f.get());
} catch(Exception e) {
throw new R u n t im eE xc ep ti on (e );
}
re zu lt at i. re mo ve (f );
}
}
pl.shutdown();
}
} /* Ispis: (85% podudaranja)
Završeni pozivi svih asinhronih metoda
pokr eć em 0.0 + 0.0
pokrećem 0.2 + 0.2
0.0
pokrećem 0.4 + 0.4
0.4
pokrećem 0.6 + 0.6
0.8
Poglavlje 2 1: Paralelno izvršavanje 1041
p o kr eć em 0.8 + 0.8
1.2
p o kr eć em 0 + 0
1.6
p o kr eć em 1 + 1
0
p o kr eć em 2 + 2
2
p o kr eć em 3 + 3
4
pokr eć em 4 + 4
6
8
*///:-
(najgore što se može desiti je kratko odlaganje). Pošto sistem aktivnih objekata kom uni-
cira samo preko poruka, dva objekta ne mogu biti blokirana dok se nadmeću da pozovu
metodu nekog trećeg objekta, a to znači da ne može nastati uzajamna blokada, što je veliki
korak napred. Pošto radna nit aktivnog objekta u svakom trenutku izvršava samo po jed-
nu poruku, nema takm ičenja za resurse i ne moram o da sinhronizujemo metode. Sinhro-
nizađja i dalje postoji, ali na nivou poruka, zato što se pozivi metoda ubacuju u red za
čekanje, pa se u svakom trenutku može izvršavati samo jedan od njih.
Nažalost, prevodilac ne daje neposrednu podršku za aktivne objekte, a ručno pro-
gramiranje po gornjem obrascu previše je zametno. Međutim, oblast aktivnih objekata i
aktora se razvija, kao i oblast agentski orijentisanogprogram iranja koja je još zanimljivija.
Agenti su zapravo aktivni objekti, ali agentski sistemi se ne m enjaju u zavisnosti od raču-
nara niti mreže. Ne bi me iznenadilo da agentski orijentisano programiranje nasledi
objektno orijentisano program iranje, zato što se u njemu kombinuju objekti i relativno
lako rešenje za paralelno izvršavanje.
Više inform acija o aktivnim objektim a, aktorima i agentima možete naći na Webu;
neke ideje realizovane u aktivnim objektim a možete naći naročito u C.A.R. Hoareovoj
teoriji komunicirajućih sekvencijalnih procesa (Communicating Seciuential Processes, CSP).
Vežba 41: (6) Programu PrimerAktivnogObjekta.java dodajte deo za obradu poruka
koji nema povratnu vrednost, a poziva se iz metode m a in ().
Vežba 42: (7) Izmenite VoskOMatik.java tako da realizuje aktivne objekte.
Projekat:27 Pomoću anotacija i Javassista napravite anotaciju klase @Active koja odre-
dišnu klasu transformiše u aktivan objekat.
Sažetak
Trebalo je da iz ovog poglavlja naučite osnove paralelnog programiranja pomoću Javinih
niti, kako biste shvatili sledeće:
1. Možete izvršavati više nezavisnih zadataka.
2 . Morate uzeti u obzir sve moguće probleme prilikom gašenja tih zadataka.
3 . Zadaci se međusobno om etaju prilikom korišćenja deljenih resursa. Osnovna alat-
ka za sprečavanje takvih sudara je uzajamno isključiva brava (mutex).
4 . Ako zadatke ne projektujete pažljivo, oni se mogu uzajamno blokirati.
Neophodno je naučiti kada se koristi paralelno izvršavanje, a lcada da ga izbegavati.
Osnovni razlozi za upotrebu jesu:
• Da biste posao podelili na više zadataka i tako efikasnije koristili računar. Time se
postiže i (za programera nevidljiva) raspodela zadataka na više procesora.
• Radi bolje organizacije koda.
• Radi vece pogodnosti za korisnika.
P rojekti su predlozi koji se m ogu koristiti (recim o ) za sem inarske radove. Vodič s rešenjim a ne sadrži
rešenja projckata.
Poglavjje 21: Paralelno izvršavanje 1043
V arijanta ovoga se zove „princip n ajm anjeg iznenađenja“, što znači da korisnika ne bi trebalo iznena-
đivati.
IBM j e za svoj program za uređivanje teksta Eclipse ( www.Eclipse.org) napravio novu G U I biblioteku
O t v o r e n o g iz v o r n o g koda koja je alternativa Svvingu; b o lje je predstavljena u nastavku poglavlja.
1046 Misliti na Javi
javax.swing u toj dokumentaciji kako biste saznali sve detalje o biblioteci Swing i metode
iz nje. Pomoć možete potražiti i na Webu, ali najbolje je da krenete od Sunovog udžbenika
za Swing na adresi http://java.sun.com/docs/books/tutorial/uiswing.
Postoji veliki broj (prilično debelih) knjiga posvećenih isključivo biblioteđ Swing i
njih ćete sigurno proučavati ako vam zatreba šire znanje, ili ako poželite da promenite
podrazumevano ponašanje biblioteke Swing.
Kako budete savladavali biblioteku Swing, otkrivaćete sledeće:
1. Biblioteka Swing je mnogo bolji model za programiranje od onih koje ste verovatno
sretali u drugim jezicim a i razvojnim okruženjima. Osnovna struktura za tu bibli-
oteku jesu zrna Jave (koja će biti objašnjena pri kraju ovog poglavlja).
2. Alatke za pravljenje grafićkih okruženja (vizuelna okruženja za programiranje)
obavezan su vid kompletnog razvojnog okruženja za Javu. Zrna Jave i Swing om o-
gućuju razvojnom okruženju da piše kod umesto vas, dok vi razmeštate kompo-
nente na obrasce pomoću grafičkih alatki. To u velikoj meri ubrzava projektovanje
grafičkog okruženja i om ogućuje slobodnije eksperimentisanje i isprobavanje di-
zajna, što posledično dovodi do izbora boljeg okruženja.
3. Jednostavnost i dobar projekat grafičke biblioteke Swing znači da će čak i ako kori-
stite vizuelnu alatku, umesto da ručno pišete kod, dobijeni kod biti razumljiv. To
rešava veliki problem s korišćenjem ranijih vizuelnih alatki kojima se često generi-
sao nerazumljiv kod.
Grafička biblioteka Swing sadrži sve komponente koje se očekuju u modernom korisnič-
kom okruženju, od dugmadi sa slikaina, preko stabala do tabela. To je velika biblioteka, ali
složenost odgovara zadatku koji se obavlja: ako je nešto jednostavno, ne morate da pišete
mnogo koda, ali ukoliko pokušate da uradite nešto teže, i kod postaje srazmerno složeniji.
Dobar deo privlačnosti biblioteke Swing potiče od nečega što bi se moglo nazvati ,,or-
togonalnost korišćenja". Odnosno, kada jednom steknete opšte ideje o ovoj biblioteci,
m oći ćete da ih prim enjujete svuda. Dok sam pisao primere iz ovog poglavlja, najčešće
sam već na osnovu imena metode mogao da zaključim šta ona radi, i to zahvaljujući stan-
dardu za imenovanje. To je, sasvim sigurno, obeležje dobre biblioteke. Pored toga, kom-
ponente se obično mogu dodavati drugim komponentama i sve će raditi ispravno.
Navigacija pom oću tastature je automatska: aplikaciju napisanu pomoću Svvinga ino-
žete da pokrenete i bez miša, što ne zahteva nikakvo dodatno programiranje. Podrška za
pom eranje sadržaja je izvanredna - treba samo da om otate komponentu u objekat klase
JScrollPane dok je dodajete u obrazac. Za dodavanje komponenata poput priručnih sa-
veta (engl. tooltips) obično je dovoljan samo jedan red koda.
Cela biblioteka Swing napisana je na Javi radi prenosivosti.
Swing podržava i prilično radikalnu mogućnost nazvanu „prilagodljiv izgled i ponaša-
nje“ (engl. pluggable look & feel), što znači da izgled grafičkog okruženja može dinamički
d a sc p r o m e n i d a b i se p r il a g o d io o ć e k i v a n j im a k o ris n ik a n a r a z lić itim p l a t t o r m a m a
i operativnim sistemima. Moguće je (mada teško) napraviti čak i sopstveni izgled okru-
ženja. Neka takva okruženja možete naći i na Webu. '
M eni se najviše sviđa izgled i ponašanje Salveta (engl. nnpkin) autora Kena A rnolda, u kojem prozori
izgledaju kao da su skicirani na salveti. Posetite http://mipkinlaJ.sourceforge.iiet.
Poglavlje 22: Grafička korisnička okruženja 1047
Uprkos svim svojim pozitivnim aspektima, Swing nije za svakoga, niti je sve probleme
s korisničkim okruženjima rešio onako kako su njihovi projektanti zamislili. Na kraju
poglavlja razmotrićemo dve alternative za Swing: IBM -ov SWT, razvijen za editor teksta
Eclipse, ali besplatno dostupan kao samostalna GUI biblioteka otvorenog izvornog koda,
i Flex kom panije Adobe, alatku za razvoj klijentskih okruženja Web aplikacija u Flashu.
Apleti
Kada se Java tek pojavila, dobar deo halabuke oko tog jezika izazvao je a p le t - program
koji se preuzima sa Interneta da bi se izvršavao u čitaču Weba (unutar tzv. bazena s pe-
skom, da ne bi naškodio klijentskom računaru). Predviđalo se da će Java aplet postati sle-
deća faza u evoluciji Interneta i u mnogima od prvih knjiga o Javi pretpostavljalo se da je
čitalac zainteresovan za taj jezik prvenstveno zato što želi da piše aplete.
Iz raznih razloga, ta revolucija se nije odigrala. Dobar deo problema činilo je to što
većina računara nema potreban Java softver za izvršavanje apleta, a većina korisnika ne
želi da preuzme i instalira paket od 10 M B da bi prikazali nešto što su slučajno pokupili
s Weba. Već i spominjanje nečeg sličnog dovoljno je da uplaši mnoge korisnike. Java apleti
nikada nisu dostigli kritičnu masu kao sistem za distribuciju aplikacija klijentima, i
premda se apleti i đalje povremeno sreću, po pravilu pripadaju prošlosti računarstva.
To ne znači da apleti nisu zanimljiva i vredna tehnologija. Ukoliko možete obezbediti
da svi korisniđ imaju instaliran JRE (recim o u jednoj kom paniji), onda bi apleti (ili
JNLP/Java Web Start koje ćemo opisati u nastavku poglavlja) mogli biti savršeni za đistri-
buciju klijentskih programa i automatsko ažuriranje svih računara i pri tom bi se izbegli
troškovi i trud koji obično prate instalaciju i distribuciju novog softvera.
Uvod u tehnologiju apleta naći ćete u mrežnim dodacima ove knjige, na adresi
www.MindView.net.
Osnove Svvinga
Većina Svving aplikacija, tj. grafičkih korisničkih okruženja, gradi se unutar jednostavnog
objekta tipa JFram e, koji pravi prazan prozor (ili okvir, engl . fram e) u operativnim si-
stemima svih korisnika. Naslov prozora se ovako zadaje pomoću konstruktora objekta
tipa JFram e:
//: gui/ZdravoSwing.java
import javax.swing.*;
Metoda setD efaultC loseO peration() kazuje objektu tipa JFram e šta da radi kada ko-
risnik izvede manevar gašenja. Konstanta EXIT_O N_CLO SE saopštava mu da izađe iz
programa. Podrazumevano se u tom slučaju ne radi ništa, pa ako ne pozovete metodu
setD efaultC loseO peration() ili ne napišete ekvivalentan kod za vaš prozor, aplikađja se
neće završiti.
setS ize() zadaje veličinu prozora u pikselima.
O bratite pažnju na poslednji red:
p r oz or .s et Vi si bl e( tru e);
//: gu i/ZdravoNatpis.java
import javax.swing.*;
import java.util.concurrent.*;
Tekst JLabel natpisa menja se nakon jedne sekunde. Iako je to zabavno i u tako jedno-
stavnom programu bezbedno, baš i nije dobro da glavna nit m a in () piše neposredno u
GUI komponente. Swing ima sopstvenu nit za prim anje UI dogadaja i ažuriranje ekrana.
Ako sadržaj ekrana počnete da menjate pomoću drugih niti, možete izazvati sudare i uza-
jam nu blokadu opisane u poglavlju Paralelno izvršavanje.
Umesto toga, druge niti - kao prethodno spomenuta nit m a in () - treba Svvingovoj niti
za otpremu događaja (engl. event dispatch thread) da šalju (engl. submit) zadatke na
izvršavanje.4 To se postiže predajom zadatka metodi SwingU tilities.invokeLater( ) koja
zadatak smešta u redza čekanje dogadaja (engl. event queue), iz koga ih nit za otpremu do-
gađaja (u nekom trenutku) vadi i izvršava. U prethodnom primeru to bi izgledalo ovako:
Ova praksa jc novina u Javi SE5, pa je gom ila starih program a ne podržava. To ne znači da su njihovi
autori bili neznalice, nego da se preporučena praksa stalno m enja.
1050 Misliti na Javi
Obratite pažnju na to da poziv metode sleep () rtije unutar konstruktora. Ako ga sta-
vite tarno, prvobitni tekst natpisa („Jedan natpis“) uopšte neće biti prikazan, pošto u tom
slučaju konstruktor neće završiti dok se ne završi metoda sleep () i novi natpis ne bude
umetnut. Ali ako je poziv metode slee p () unutar konstruktora ili bilo koje UI operacije,
to znači da tokom spavanja koje prouzrokuje metoda sle e p () zadržavate izvršavanje niti
za otpremu događaja, što je po pravilu loše.
Vežba 1: (1) Izmenite ZdravoSwing.java tako da dokažete da se aplikacija ne gasi bez po-
ziva metode setD efauItCIoseO peration().
Vežba 2: (2) Izmenite ZdravoNatpis.java tako da pokažete da je dođavanje natpisa di-
namičko - stavite u prozor pseudoslučajan broj natpisa.
Alatka za prikazivanje
Objedinićem o navedene ideje i smanjiti količinu redundantnog koda sledećom alatkom
za prikazivanje koju ćem o koristiti u ostalim primerima biblioteke Svving u ostatku
poglavlja:
Ovu alatku ćete možda poželeti da koristite na više mesta, pa je smeštena u biblioteku
net.mindview.util. Da bi mogla da je koristi, aplikacija mora biti u objektu tipa JFram e
(kao svi primeri u ovoj knjizi). Statična metoda r u n () zadaje naslov prozora jednak Class
imenu objekta tipa JFrame.
Vežba 3: (3) Izmenite SlanjeSwingPrograma.java tako da upotrebljava program Swing-
Konzola.
PravIjenje dugmeta
Pravljenje dugmeta je prilično jednostavno: samo pozovite konstruktor klase JButton i
prosledite mu natpis koji treba da se pojavi na dugmetu. Kasnije ćete videti da možete
uraditi i nešto naprednije, npr. staviti slike na dugmad.
Referenca na dugme se obično čuva unutar klase da bi kasnije moglo ponovo da jo j se
pristupi.
JButton je Swing kom ponenta sa sopstvenim prozorčićem koja se automatski iscrtava
tokom ažuriranja prikaza. To znači da dugme ili neki drugi kontrolni objekat ne crtate
ručno, već ih samo postavljate na obrazac i om ogućujete im da se automatski iscrtavaju.
Dugme se na obrazac postavlja obično unutar konstruktora:
//: gui/Dugmel.java
// Stavljanje dugmadi u Swing aplikaciju.
import javax.swing.*;
import java.awt.*;
import static ne t. m i n d v i e w . u t i l .SwingKonzola.*;
Ovde je dodato nešto novo: pre nego što se element postavi u okno sadržaja, dodeljuje
mu se nov„raspoređivač“ tipa FlowLayout. Rasporedivač (engl. Layout manager) opisuje
n a č in n a koji o k n o im p l ic itn o o d lu č u je o t o m e g d e ć e p o s ta v iti k o n tr o ln i o b je k a t na
obrascu. Aplet obično koristi rasporedivač tipa BorderLayout, ali to nam u ovom slučaju
ne odgovara zato što se na taj način (kao što ćete saznati u nastavku poglavlja) svaki kon-
trolni objekat potpuno prekriva novim objektima u istoj oblasti. Međutim, raspoređivač
1052 Misliti na Javi
Hvatanje događaja
Ako prevedete i izvršite prethodni program, primetićete da se neće dešavati ništa kada
pritiskate dugmad. U tu svrhu morate sami da napišete kod. Suština programiranja upra-
vljanog događajima, što čini veliki deo programiranja grafičkih korisničkih okruženja, je-
ste povezivanje događaja s kodom koji odgovara na njih.
Ovo se u grafičkoj biblioteci Swing postiže jasnim razdvajanjem okruženja (grafičkih
kom ponenata) i realizacije (koda koji želite da izvršavate kada se desi događaj). Svaka
komponenta može da prijavljuje događaje, i svaki od njih može da prijavljuje zasebno.
Dakle, ako niste zainteresovani, na primer, za to da li je pokazivač miša prešao preko dug-
meta, nećete osluškivati taj događaj. To je veoma jednostavan i elegantan način za pisanje
programa vođenih događajima. Kada shvatite osnovne pojmove, lako ćete koristiti Swing
komponente koje nikada niste videli. Zapravo, ovaj model se prim enjuje na sve što se
može okarakterisati kao zrno Jave (o čemu ćete saznati više u nastavku poglavlja).
Za početak, usredsredićemo se na glavne đogađaje kom ponenata. Za dugme tipa JBut-
ton, „glavni događaj“ je njegovo pritiskanje. Da biste objavili svoje zanim anje za dogadaj
pritiskanja dugmeta, pozivate metodu addActionListener( ) klase JButton. Ta metoda
očekuje argument koji realizuje interfejs ActionListener što sadrži samo jednu metodu
action P erform ed (). Dakle, da biste povezali kod s dugmetom tipa JButton, treba samo
da realizujete interfejs ActionListener u klasi i da prosledite objekat te klase dugmetu me-
todom addA ctionListener(). Metoda prosledenog objekta pozivaće se kad god se priti-
sne dugme (to se obično naziva povratni poziv , engl. callback).
Šta bi trebalo da bude rezultat pritiskanja tog dugmeta? Želeli bismo da vidimo da se
nešto menja na ekranu, pa će biti predstavljena nova Swing komponenta: JTextFieId. To
je jednoredno polje za unos teksta, ili, u ovom slučaju, polje u kome program može me-
njati tekst. Iako postoji više načina da se napravi objekat klase JTextFieId, najjednostav-
niji način je da se samo saopšti konstruktoru koliko treba da bude široko to polje. Kada se
polje za tekst postavi na obrazac, njegov sadržaj možete da menjate metodom setT ext()
(klasa JTextFieldsadrži i mnoge druge metode, ali njih morate da potražite sami u HTM L
dokumentaciji na adresi http://java.sun.com). Evo kako to izgleđa:
//: gui/Dugme2.java
// Reagovanje na pritiskanja dugmeta.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindvie w . u t i 1 .S wi ngKonzola.*;
Poglavlje 22: Grafička korisnička okruženja 1053
Jeđnoredno polje za tekst pravi se i smešta na obrazac na isti način kao dugme ili neka
druga Swing komponenta. Gornji program se razlikuje po pravljenju pomenute klase
PrijemnikDugmeta koja realizuje interfejs ActionListener. Argument metode action-
Performed( ) tipa je ActionEvent i sadrži sve inform acije o događaju i mestu gde je na-
stao dogadaj. U ovom slučaju, želeo sam da opišem dugme koje je pritisnuto: metoda
g etS ou rce() vrača objekat gde se đesio događaj, a ja sam (eksplicitnom konverzijom) uči-
nio da njegov tip bude JButton. Metoda g etT ext() vraća tekst koji se nalazi na dugmetu,
i ja sam ga stavio u polje tipa JTextField kako bih dokazao da se kod zaista poziva kada se
dugme pritisne.
U konstruktoru se metoda addA ctionListener() koristi za registrovanje objekata klase
PrijemnikDugmeta kao prijemnika događaja oba dugmeta.
Ćesto je zgodnije da interfejs ActionListener bude realizovan kao anonimna unutra-
šnja klasa, naročito zato što se obično koristi samo jedna instanca svake klase ovog tipa.
Program DugmeŽ.java može se promeniti tako da koristi anonim nu unutrašnju klasu na
sledeći način:
//: gui/Duame2b.java
// Korišćenje anonimnih unutrašnjih klasa.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static ne t. mi nd vi ew .u ti l.SwingKonzola.*;
1054 Mis/iti na Javi
//: gui/TextArea.java
// Korišćenje kontrole JTextArea.
import j a v a x . s w i n g .*;
import java.awt.*;
lmport java.awt.event.*;
import j a v a . u t i 1 .*;
import net.mindview.util.*;
import static ne t. mi nd vi ew .u ti1 .SwingKonzola.*;
Poglavlje 22: Grafićka korisnička okruženja 1055
Vežba 7: (5) Napravite aplikaciju pom oću klase SwingKonzoIa i dodajte sve Swing kom-
ponente koje imaju metodu ad dA ctionListener(). (Potražite ih u HTM L dokumentaciji
na adresi http://java.siin.com. Savet: upotebite indeks.) Uhvatite njihove događaje i za
svaki prikažite odgovarajuću poruku u polju za tekst.
Vežba 8: (6) Gotovo sve kom ponente biblioteke Swing izvedene su iz klase Component
koja ima metodu se tC u rso r(). Potražite je u H TM L dokumentaciji za Javu. Napišite ap-
likaciju i promenite pokazivač miša u neki iz klase Cursor.
Raspoređivanje elemenata
Način na koji se komponente raspoređuju u Javi verovatno je drugačiji nego u svim osta-
lim sistemima za grafička okruženja koje ste koristili. Prvo, sve se obavlja programski: ne
postoje ,,resursi“ koji upravljaju postavljanjem komponenata. Drugo, načinom na koji se
komponente postavljaju na obrazac ne upravlja apsolutno pozicioniranje, već raspoređivač
koji odlučuje o položaju komponenata na osnovu redosleda kojim ih dodajete. Veličina, ob-
lik i položaj komponenata znatno se razlikuju od jednog raspoređivača do drugog. Pored
toga, raspoređivači se prilagodavaju dimenzijama apleta ili prozora aplikacije, pa ako se
promeni dimenzija prozora, u saglasnosti s tim mogu da se promene veličina, oblik i po-
ložaj komponenata.
Klase JApplet, JFram e, JWindow, JDialog JPanel itd. mogu da sadrže i prikazuju
komponente. Klasa Container sadrži metodu setLayout( ) koja omogućuje zadavanje
rasporedivača. U ovom odeljku proučićem o raziičite raspoređivače tako što ćemo posta-
vljati dugmad (jer je to najjednostavnije). Nećemo obrađivati nikakve događaje, pošto ovi
primeri samo pokazuju kako se kom ponente raspoređuju.
Raspoređivač BorderLayout
Prozor podrazumevano koristi raspoređivač tipa BorderLayout. Ako ne zadate drugačije,
on uzima sve objekte koje mu dodajete i smešta ih u centar, i pri tom ih proširuje sve do
ivica.
Ovaj raspoređivač koristi četiri granične oblasti i centralnu oblast. Kada na pano koji
koristi raspoređivač BorderLayout dodate nešto, možete da upotrebite preklopljenu me-
todu a d d () čiji je prvi argument konstantna vrednost. Ta vrednost može da bude nešto od
sledećeg:
Borderl_ayout.NORTH fvrh)
BorderLayout. SOUTH |dno)
BorderLayout. EAST (desno)
BorderLayout.WEST (ievoj
BorderLayout.CENTER Ipopunjavanje sredine, do ostaiih komponenata ili ivica)
Poglavlje 22: Grafička korisnička okruženja 1057
//: gu i/ Bo rderLayoutl.java
// Prikazuje raspoređivač B o r d e r L a y o u t .
import javax.swing.*;
import java.awt.*;
import static n e t. mi nd vi ew .u ti l.SwingKonzola.*;
Ako se element postavlja u bilo koju oblast osim CENTER, sažima se da bi se uklopio
u najm anji moguči prostor duž jedne dimenzije, a maksimalno se produžava duž druge
dimenzije. Oblast CENTER, međutim, proširuje objekat u obe dimenzije da bi zauzeo
sredinu.
Raspoređivač FlowLayout
Ovaj raspoređivač samo ,,reda“ komponente na obrazac, sleva udesno, sve dok se ne po-
puni jedan red, a zatim prelazi u sledeći red i nastavlja redanje.
Evo primera koji bira raspoređivač tipa FlowLayout i zatim postavlja dugmad na
obrazac. Prim etićete da u ovakvom načinu raspoređivanja komponente zadržavaju svoju
,,prirodnu“ veličinu. Dugme je, na primer, taman toliko da se može prikazati znakovni niz
koji sadrži.
//: g u i / F l o w L a y o u t l .java
// Prikazuje raspoređivač FlowLayout.
import javax.swing.*;
import java.awt.*;
import static net. mi nd vi ew .u ti1 .SwingK on zo la .*;
Raspoređivač GridLayout
GridLayout omogućuje izradu tabele kom ponenata, a kako ih dodajete, one se smeštaju
sleva udesno i odozgo naniže u tabeli. U konstruktoru se zađaje potreban broj redova i
kolona i one se prikazuju u jednakim proporcijam a.
//: gui/GridLayoutl.java
// Prikazuje raspoređivač GridLayout.
import javax.swing.*;
import java.awt.*;
import static ne t. mi nd vi ew .u ti l.SwingKonzola.*;
Raspoređivač GridBagLayout
GridBagLayout obezbeduje potpunu kontrolu nad odlučivanjem o rasporedu i formati-
ranju oblasti u prozoru kada mu se promeni veličina. Međutim, to je istovremeno i naj-
složeniji raspoređivač koji je veoma teško razumeti. Namenjen je prvenstveno
a u t o m a ts k o m g e n e ris a n ju k o d a p o m o ć u a la tk e za p r a v lje n je g r a fić k ih a p lik a c ija (d o b r i
vizuelni alati koriste GridBagLayout umesto apsolutnogpozicioniranja). Ako je program
toliko složen da mislite kako treba koristiti raspoređivač tipa GridBagLayout, trebalo bi
da upotrebite vizuelnu alatku. Ako baš želite da saznate sve detalje o ovome, potražite ih
u nekoj knjizi potpuno posvećenoj grafičkoj biblioteci Svving.
Poglavlje 22: Grafička korisnička okruženja 1059
Apsolutno pozicioniranje
Apsolutan položaj grafičkih kom ponenata moguće je zadati i na sledeći način:
1. Uklonite raspoređivač metodom Container.setLayout(null).
2 . U zavisnosti od verzije jezika, za sve komponente pozovite metodu setB ou nd s() ili
resh ap e (), prosleđujući jo j okvirni pravougaonik čije su koordinate izražene pik-
selima. To možete da uradite u konstruktoru ili u metodi p a in t(), u zavisnosti od
toga šta želite da postignete.
Neke alatke za pravljenje grafičkih okruženja često koriste ovakav pristup, ali to obič-
no nije najbolji način za generisanje koda.
Raspoređivač BoxLayout
Pošto korisnici nisu razumevali klasu GridBagLayout i teško su je primenjivali, u biblio-
teku Swing dodata je i klasa BoxLayout koja pruža mnoge prednosti rasporedivača Grid-
BagLayout, a nije toliko složena. Ovakvo raspoređivanje često možete da koristite pri
ručnom razmeštanju elemenata (i ovde, ako program postane previše složen, upotrebite
vizuelnu alatku). Rasporedivač BoxLayout om ogućuje i vertikalno i horizontalno kon-
trolisanje položaja komponenata, a za kontrolisanje prostora izmedu komponenata kori-
ste se ,,podupirači“ i ,,lepak“. Elementarne primere upotrebe rasporedivača BoxLayout
potražite u mrežnim dodacima ove knjige, na adresi www.MindView.net.
Vidi se da svaki tip komponente podržava samo odredene tipove događaja. Ispostavlja
se da je prilično teško tražiti sve događaje koje podržava svaka komponenta. Lakši pristup
je da se prilagodi program PrikaziMetode.java iz poglavlja Podaci o tipu tako da prika-
zuje sve prijem nike đogađaja koje podržava bilo koja uneta Swing komponenta.
U p o g la v lju l\nlnci o tipu o b ja š n je n a je rcjlcksija a p o t o m je k o r i š ć e n a i za lista n je m e -
toda odredene klase (ceo spisak nietoda ili podskup metoda u čijem se imenu nalazi za-
data rezervisana reč). Ovaj postupak je močan zato što automatski prikazuje svemetode
D ogađaj M o u seM o tio n E v en t ne postoji m ada izgleda kao da bi m o rao postojati. Pom eran je m iša uz
držanje pritisnu tog tastera ob jed in jen o je u događaj M ou seE v ent, pa drugo pojavljivanje klase M o-
u seE v en t u tabeli n ije greška.
1062 Misliti na Javi
}
}
}
public PrikaziMetodeAddListener() {
ImeL imeListener = new ImeL();
ime.addActionListener(imeLi st en er );
JPanel gornji = new JPanel();
gornji .add(new JLabelC'Ime Swing klase(pritisnite Enter):'1));
go rn ji . a d d ( i m e ) ;
add(BorderLayout.NORTH, gornji);
add(new JScrollPane(rezultati));
// Početni podaci i test:
ime.setText("JTextArea'');
imeLi stener.actionPerformed(
new Ac ti o n E v e n t ( " “ , 0 ,""));
}
public static void main(String[] args) {
run(new Pr i kaziMetodeAddLis t en er (), 500,400);
}
} ///:-
Glavni prozor ovog programa sadrži polje ime tipa JTextFielđ; u njega treba da une-
sete ime Swing klase koju tražite. Rezultati se vade pomoću regularnog izraza i prikazuju
u polju tipa JTextArea.
Prim etićete da nema dugmađi niti drugih komponenata kojima bi se naznačilo kako
želite da poćnete pretraživanje. Razlog je to što događaje polja tipa JTextField nadgleda
prijem nik tipa ActionListener. Kad god nešto izmenite i pritisnete taster Enter, spisak se
trenutno ažurira. Ako polje nije prazno, njegov sadržaj se koristi za pokušaj pronalaženja
klase metodom C lass.forN am e(). Ukoliko je ime nepravilno, metoda C lass.forN am e()
neće naći klasu, pa generiše izuzetak koji se hvata, a u objektu klase JTextArea prikazuje
se znakovni niz ,,Ne postoji". Medutim, ako otkucate ispravno ime (važan je i raspored ve-
likih i malih slova), metoda Class.forN am e() nalazi klasu i getM ethods() vraća niz obje-
kata klase Method.
Ovde se koriste dva regularna izraza. Prvi, addListener, traži reč add iza koje slede slova
pa reč Listener i spisak argumenata u zagradi. Ceo regularan izraz je u običnim zagradama
(koje nisu pretvorene u izlazne sekvence), što znači da će biti pristupačan kao ,,grupa“
regularnog izraza kada bude pronaden. Unutar metode Im eL.A ctionPerform ed(), svaki
objekat Method prosleđuje se metodi Uzorak.matcher( ), čime se pravi objekat tipa
Matcher. Kada se za taj objekat tipa Matcher pozove metoda fin d (), ona vraća true ako
pronađe podudaran niz znakova i u tom slučaju pozivom metode g ro u p (l) možete oda-
brati prvu podudarnu grupu unutar zagrada. Taj znakovni niz još uvek sadrži kvalifikato-
re, pa se za n iih o v o o đ b a c iv a n ie u p o tr e b lia v a o b je k a t kvalifikator Uzorak, k a o u
PrikažiMetode.java.
Na kraju konstruktora, u ime se smešta početna vrednost i izvršava se reakcija na do-
gadaj (ActionEvent) koja ispitivanje snabdeva početnim podacima.
1064 Misliti na Javi
Ovaj program je zgodan za ispitivanje mogućnosti neke Swing kom ponente. Kada
znate koje događaje podržava odredena Swing kom ponenta, ne morate da tražite ništa
posebno da bi ona reagovala na taj događaj; samo učinite sledeće:
1. Iz imena klase događaja uklonite reč Event. Dodajte reč Listener onom e što preo-
stane. To je interfejs prijemnika koji morate da realizujete u unutrašnjoj klasi.
2. Realizujete pomenuti interfejs i napišete metode za obradu dogadaja. Na primer,
možda želite da pratite pokrete miša; u tom slučaju treba da napišete kod za meto-
du m ouseM oved() interfejsa M ouseM otionListener. (Naravno, morate da reali-
zujete i druge metode tog interfejsa, ali za to često postoji prečica, što ćete uskoro
saznati.)
3. Napravite objekat prijemničke klase iz drugog koraka. Prijavite ga svojoj kompo-
nenti metodom čije ćete ime dobiti ako ispred imena prijem nika dodate add, npr.
addMouseMotionListener.
Evo nekih prijemničkih interfejsa:
Ovo nije sveobuhvatan spisak, đelimično i zbog toga što vam model dogadaja dozvo-
ljava da pravite sopstvene tipove dogadaja i prijemnike za njih. Zato ćete često viđati bi-
blioteke u kojim a postoje novi događaji, a znanje koje ste stekli čitajući ovo poglavlje
om ogućiće vam da smislite kako ćete koristiti te događaje.
Ovo ne radi, a vi ćete izludeti pokušavajući da otkrijete razlog. Sve se lepo prevodi i iz-
vršava, osim što nakon pritiska na taster miša vaša metoda neće biti pozivana. Vidite li šta
je problem? Ime metode: navedeno je M ouseClicked() umesto m ouseC licked(). Malo
zanemarivanje veličine slova prouzrokuje dodavanje potpuno nove metode. Međutim, to
nije metoda koja se poziva kada se prozor zatvara, pa nećete dobiti želiene rezultate. I po-
red n e p o g o d n o s ti, in te r ie js ć e g a r a n t o v a ti d a su m e to d e p r a v i ln o re a liz o v a n e .
Izvesno ćete ređefinisati odredenu metodu ukoliko u gornjem kodu upotrebite ugra-
đenu anotaciju @Override.
Vežba 9: (5) Na osnovu programa PrikaziMetodeAddListener.java napišite program sa
svim funkcijama programa podaciotipu.PrikaziM etode.java.
1066 Misliti na Javi
//: gu i/ Pr at iD og ad ja j.java
// Prikazuje događaje koji se dešavaju.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import ja va .u ti l.*;
import static net.mindview.util .SwingKonzola.*;
U Javi 1.0/1.1 ra)'ebilo m oguće izvesti korisne klase iz dugm eta. To je bio sam o jedan od m nogih ve-
likih propusta u projektovanju.
Poglavlje 22: Grafička korisnička okruženja 1067
h.put(dgd, t ) ;
1
add(dl);
add(d2);
}
public static void main(String[] args) {
run(new P r at iD og ad ja j(), 700, 500);
}
1 ///=-
3 . Zbog pravila za imenovanje koja se prim enjuju na Swing događaje, prilično se lako
pogađa kako se piše i registruje prijem nik za odredeni tip dogadaja. Upotrebite
program PrikaziMetodeAddListener.java iz ovog poglavlja da biste sebi olakšali
traženje određene komponente.
4 . Kada stvari postanu komplikovanije - vreme je da koristite vizuelne alatke.
Dugmad
Biblioteka Swing sadrži veliki broj dugmadi različitih tipova. Sva dugmad, polja za potvr-
du, radio-dugmad, čak i stavke menija, izvedeni su iz klase A bstractButton (kojoj bi, po-
što obuhvata i stavke menija, bolje odgovaralo ime „AbstractSelector" ili nešto opštije).
Uskoro ćete saznati kako se koriste stavke menija, a sledeći primer prikazuje različite do-
stupne tipove dugmadi:
Grupe dugmadi
Ako želite da radio-dugmad isključuju jedno drugo, morate ih smestiti u „grupu dugma-
di“. Međutim, kao što prikazuje sledeći primer, svako dugme tipa AbstractButton može
se dodati grupi tipa ButtonGroup.
Da bi se izbeglo često ponavljanje koda, u ovom primeru koristi se refleksija za pra
vljenje grupa dugmadi različitih tipova. To se vidi u metodi n ap rav iP an o() koja pravi
grupu dugmadi i objekat klase JPanel. Drugi argument metode n apraviP ano() jeste niz
objekata tipa String. Za svaki element tog niza na pano se dodaje dugme, a tip dugmadi
određuje prvi argument metode:
//: giri/GrupeDugmadi.java
// Primenjuje refleksiju za pravljenje grupa
// različitih tipova dugmadi AbstractButton.
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.lang.reflect.*;
import static n e t .m in dview.uti1 .SwingKonzola.*;
bg.add(ab);
jp.add(ab);
}
return jp;
}
public G r u p e D u g m a d i () {
setLayout(new Flow La yo ut () );
add(makeBPanel(JButton.class, ids));
add(makeBPanel(JToggleButton.class, ids));
add(makeBPanel(JCheckBox.class, ids));
add(makeBPanel(JRadioButton.class, i d s));
}
public static void main(String[] args) {
run(new G r u p e D u g m a d i (), 500, 350);
}
} ///:-
Ikonice
Klasu Icon možete da koristite unutar natpisa tipa JLabel ili bilo koje klase koja se izvođi
iz klase AbstractButton (uključujući JButton, JCheckBox, JRadioButton i razne vrste
JM enuItem ). Korišćenje ikonica s klasom JLabel sasvim je jednostavno (kasnije ćete vi-
deti prim er). U sledećem primeru istražuju se drugi načini korišćenja ikonica s dugma-
dima i objektim a koji su iz njih izveđeni.
Možete da koristite proizvoljne datoteke u formatu GIF, a one koje se upotrebljavaju u
ovom primeru naći ćete u paketu sa izvornim kodom, dostupnom na adresi www.Min-
dVicw.net. Da biste otvorili datoteku i prikazali sliku, napravite objekat klase Im agelcon
i prosledite mu ime datoteke. Nakon toga, moći ćete u programu da koristite dobijeni
objekat tipa Icon.
//: g u i / L i k o v i .java
j j Ponašanje lkonica na duyraadima tipa JButton.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net. mi nd vi ew .u ti1 .Sw in gK o n z o l a . * ;
1072 Misliti na Javi
Priručni saveti
U prethodnom primeru dugmetu je dodat i „priručni savet“ (engl. tool tip). Gotovo sve
klase grafičkih elemenata izvedene su iz klase JCom ponent koja sadrži metodu setTool-
TipText(String). Zbog toga za doslovno sve što stavite na obrazac, treba samo da napišete
(za objekat jc bilo koje klase izvedene iz klase JC om ponent):
jc.setToolTipText("Moj sa v e t " ) ;
i kada se pokazivač miša zadrži iznad objekta klase JCom ponent zadato vreme, pored po-
kazivača će se pojaviti malo polje s vašim tekstom.
//: g u i/ Po lj aZ aT ek st.java
// Polja za tekst i Javini događaji.
import javax. sw in g.*;
import j a v a x . s w i n g .e v e n t .*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.uti1 .SwingKonzola.*;
tl.addActionListener(new T I A ());
setLayout(new FlowLa yo ut () );
add(dl);
add(d2);
add(tl);
add(t2);
add(t3);
)
class T1 implements DocumentListener {
public void changedllpdate(DocumentEvent e){)
public void insertUpdate(DocumentEvent e){
t 2 .s et Te xt(tl.getText());
t3.setText("Tekst: "+ tl .g e t T e x t ( ) ) ;
}
public void removeUpdate(DocumentEvent e){
t 2 .s et Te xt(tl.getText());
}
}
class TIA implements Ac ti on Li st en er {
private int broj = 0;
public void actionPerformed(ActionEvent e) {
t3.setText("tl dogadjaj " + broj++);
}
}
class B1 implements Ac tionListener {
public void actionPerformed(ActionEvent e) {
if(tl.getSelectedText() == null)
s = tl .g et Te xt ();
el se
s = tl .g et Se le ct ed Te xt ();
tl.set Ed it ab le (t ru e);
}
}
class B2 implements Ac tionListener {
public void actionPerformed(ActionEvent e) {
dvs.zadajP re tv ar an je( fa ls e);
tl.setText("Ubaceno dugmetom 2: " + s ) ;
dvs.zada jP re tv ar an je( tr ue );
tl.s et Ed it ab le (f al se);
}
}
public static void main(String[] args) {
run(new Po lj aZ aT ek st (), 375, 200);
}
)
Polje t3 tipa JTextFieId služi za izveštavanje o događajima polja t l tipa JTextFieId. Vi-
dećete da se prijemnik događaja za polje JTextField pokreće samo kada pritisnete taster
Enter.
S poljem t l povezano je nekoliko prijemnika. T1 je tipa Docum entListener
i odgovara na sve promene ,,dokumenta“ (u ovom slučaju, sadržaja polja). On automatski
kopira sav tekst iz t l u t2. Pored toga, dokument polja t l je tipa DokumentSVelikimSIo-
vima koji je izveden iz klase PlainDocument, a pretvara sva slova u velika. Automatski se
otkrivaju znakovi za brisanje, briše sadržaj i pomera pokazivač, pa sve radi kao što se i
očekuje.
Vežba 13: (3) Promenite program PoljaZaTekst.java tako da znakovi u t2 zadržavaju pr-
vobitni raspored malih i velikih slova kako su bila unesena, umesto da se automatski pre-
tvaraju u velika slova.
Ivice
Klasa JComponent sadrži metodu se tB o rd e r() koja om ogućuje postavljanje zanimljivih
ivica oko svih vidljivih komponenata. Sledeći primer prikazuje razne ivice - metodom
p rik azilvicu () pravi se objekat klase JPanel i na njega postavlja ivica. Prim enjuje se i teh-
nika prepoznavanja tipa tokom izvršavanja da bi se ustanovilo ime ivice koja se koristi (uz
izbacivanje svih informacija o putanji), a zatim se to ime stavlja u objekat klase JLabel, u
sredinu panoa.
//: gui/Ivice.java
// Razne ivice iz biblioteke Swing.
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import static ne t. mindview.uti1 .SwingKonzola.*;
jp.setBorder(b);
return jp;
}
public Ivice() {
setLayout(new G r i d L a y o u t ( 2 , 4 ) ) ;
add(prikazilvicu(new Ti t l e d B o r d e r ( " N a s l o v " ) ) ) ;
add(prikazilvicu(new E t c h e d B o r d e r ( ) ) ) ;
add(pri kaziIvi cu(new Li n e B o r d e r ( C o l o r .B L U E ) ));
add(prikaziIvicu(
new M a t t e B o r de r( 5, 5, 30 ,30 ,C ol or .G RE EN )) );
add(prikazilvicu(
new Bevel B o r d e r ( B ev el Bo rd er .RA IS ED )));
add(prikazilvicu(
new SoftBevel Border(Bevel B o r d e r .L 0W ER ED )));
add(pri kaziIvicu(new CompoundBorder(
new Et ch ed Bo rd er (),
new L i n e Bo rd er (C ol or .R ED) )) );
}
public static void main(String[] args) {
run(new Ivice(), 500, 300);
}
} ///:-
Možete da napravite i sopstvene ivice koje ćete staviti oko dugmadi, natpisa ili bilo
čega što je izvedeno iz klase JCom ponent.
//: gui/TekstualniPano.java
// Klasa JTextPane kao mali program za uređivanje teksta.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import net.mindview.util.*;
import static ne t. m i n d v i e w . u t i l .SwingKonzola.*;
public class TekstualniPano extends JFra me {
private JButton b = new JButton("Dodaj tekst");
private JTextPane tp = new JTextPane();
private static Ge nerator sg =
new R a n d o m Ge ne ra to r.S t r i n g (7);
public Teks tu aln i P a n o () {
b.addActionListener(new ActionListener() {
public void ac ti on Pe rf or me d( Ac tio nE ve nt e ) {
for(int i = 1; i < 10; i++)
tp.setText(tp.getText() + sg.next() + "\n");
}
Poglavjje 22: Grafička korisnička okruženja 1077
});
add(new JScrollPane(tp));
add(BorderLayout.SOUTH, b ) ;
}
public static void main(String[] args) {
run(new Te ks t u a l n i P a n o ( ) , 475, 425);
}
} ///:-
Polja za potvrdu
Polje za potvrdu om ogućuje pravljenje opcije koja može da bude potvrdena ili nepotvr-
đena; sastoji se od malog polja i oznake. Polje obično sadrži malo x (ili nešto drugo što po-
kazuje da je opcija potvrđena) ili je prazno, u zavisnosti od toga da li je stavka koju
označava potvrđena ili nije.
O bjekat klase JCheckBox obično se pravi pom oću konstruktora čiji je argument nat-
pis. Stanje polja možete da čitate i zadajete, ali možete i da čitate i menjate njegov natpis
nakon pravljenja.
Kad god se polje tipa JCheckBox potvrdi ili se potvrda ukloni, nastaje događaj koji
možete da uhvatite na isti način kao događaj dugmeta, pomoću objekta klase ActionLi-
stener. U sledećem primeru koristi se tekstualno polje za brojanje potvrđenih polja:
//: gui/PoljaZaPotvrdu.java
// Korišćenje klase JCheckBoxes.
import javax.swing.*;
import java.awt.*;
iinport j a v a , a w t .e v e n t . * ;
import static ne t. mi nd vi ew .u ti1 .SwingKonzola.*;
Metoda o b ra d i() dodaje ime polja i njegovo tekuće stanje u sadržaj višerednog polja
za tekst metodom ap p en d (), pa će se prikazati zbirni spisak odabranih polja i njihovih
stanja.
Vežba 15: (5) Dodajte polje za potvrdu u aplikađju napravljenu u vežbi 5, uhvatite doga-
đaj i umetnite drugačiji tekst u polje.
Radio-dugmad
Ime radio-đugme u programiranju grahčkih korisnićkih okruženja izvedeno je aso-
cijacijom na radija iz starinskih autom obila koji su imali mehaničku dugmad. Kada bi se
jedno dugme pritisnulo, iskakalo bi dugme koje je ranije bilo pritisnuto. Na taj naćin mo-
gla je da se odabere samo jedna mogućnost.
Poglav[je 22: Grafička korisnička okruženja 1079
Da biste napravili povezanu grupu dugmadi tipa JRadioButton, treba samo da ih do-
date u grupu tipa ButtonGroup. (U obrascu može da postoji proizvoljan broj grupa dug-
madi.) Jednom dugmetu može da se (drugim argumentom konstruktora) zada početno
stanje tako da bude potvrđeno. Ako više radio-dugmadi u grupi uključite konstruktorom,
poslednje dugme će iskJjučiti sva prethodna.
Evo jednostavnog prim era korišćenja radio-dugmadi. O bratite pažnju na to da se do-
gađaji radio-dugmadi obrađuju isto kao i svi drugi događaji:
//: gui/RadioĐugmad.java
// Korišćenje klase JRadioButtons.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import static ne t. m i n d v i e w . u t i l .SwingKonzola.*;
//: gui/PadajuceListe.java
// Korišćenje padajućih lista.
import javax.swing.*;
import java.avvt.*;
import java.awt.event.*;
import static net.mi nd vi ew .u ti l.SwingKonzola.*;
add(b);
}
public static void main(String[] args) {
run(new Padaju ce Li st e( ), 200, 175);
}
} III--
Tekstualno polje prikazuje „odabrani indeks“ (redni broj elementa koji je trenutno iza-
bran u nizu), kao i tekst stavke koja je odabrana u kom binovanoj listi.
Grafičke liste
Polja tipa JList prilično se razlikuju od polja tipa JCom boBox, i to ne samo po izgledu. Za
razliku od polja tipa JCom boBox koje pada kada ga aktivirate, JList uvek zauzima tačno
određen broj redova na ekranu i ne m enja se. Ako želite da vidite elemente liste, samo po-
zovite metodu getSelectedValues() koja vraća niz izabranih stavki.
Lista JList omogućuje višestruki izbor: ako uz pritisnut taster Ctrl mišem pritisnete
više od jedne stavke, stavka koj u ste prvu izabrali ostaće istaknuta, a nakon toga možete da
izaberete još proizvoljan broj elemenata. Ukoliko izaberete neki element, a zatim uz pri-
tisnut taster Shift mišem pritisnete još jedan element, biće izabrani svi elementi između
njih. Da biste uklonili stavku iz izabrane grupe, pritisnite je mišem, istovremeno držeći
pritisnut taster Ctrl.
b. se tE nab1ed(false);
}
}
};
private ListSelectionListener 11 =
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e .g et Va lu el sA dj ust in gO ) return;
t.se tT ex t( "" );
for (Object stavke : Is t. ge t S e l e c t e d V a l u e s O )
t.append(stavke[i] + "\n");
}
};
private int broj = 0;
public Lista() {
t.setEditable(false);
setLayout(new FlowLayout());
// Pravi ivice za komponente:
Border brd = BorderFactory.createMatteBorder(
1, 1, 2, 2, Co lo r. BL AC K);
1 s t .se tB or de r( br d);
t.se tB or de r( br d);
// Dodavanje prve četiri stavke u listu
for(int i = 0; i < 4; i++)
elementi Liste.addElem en t( uk us i[ br oj ++ ]);
// Dodavanje stavki u pano sadržaja radi prikaza
add(t);
ad d ( l s t ) ;
add(b);
// Registrovanje slušalaca događaja
1 st.addLi stSelectionLi st en er (1 1);
b.ad dA ct io nL is te ne r(b l);
}
public static void m a i n ( S t r i n g [] args) {
run(new Lista(), 250, 375);
}
} ///:-
Okno s jezičcima
Klasa JTabbedPane om ogućuje pravljenje okna u kom e se duž gornje ivice prikazuju je-
zičci. Pritiskom na odredeni jezičak prikazaćete njegov poddijalog.
Kada izvršite program, videćete da JTabbedPane autom atski prenosi jezičke u sledeći
red ako ih ima previše da bi stali u isti red. To ćete vidieti kada u toku rada prom enite ve-
ličinu prozora.
Okviri za poruke
G r a f ić k o o k r u ž e n j e o b i ć n o s adrži s t a n d a r d a n s k u p o k v i r a za p o r u k e koji o m o g u ć u j u b r z o
dostavljanje informacija korisniku ili uzim anje inform acija od njega. U grafičkoj biblio-
teci Svving, polja za poruke realizuje klasa JOptionPane. Postoje brojne m ogućnosti (od
kojih su neke vrlo napredne), ali najčešće ćete koristiti dijalog poruke i dijalog za potvrdu
koji se dobijaju pozivom statičkih m etoda JO ptionPane.showM essageDialog( ) i JOpti-
onPane.show C onfirm D ialog( ). Sledeći primer prikazuje neke od okvira za poruke koji
postoje u klasi JOptionPane:
1084 Misliti na Javi
//: gui/OkviriZaPoruke.java
// Prikazuje klasu JOptionPane.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.util.SwingKonzola.*;
};
public OkviriZaPoruke() {
setl_ayout(new FlowLayout());
for(int i = 0; i < b.length; i++) {
b[i] . a d d A c t i o n L i s t e n e r ( a O ;
a d d( b[ i]);
}
add(txt);
}
public static void main(String[] args) {
run(new O k v i r i Z a P o r u k e O , 200, 200);
}
} ///= -
Da bih napisao sam o jedan prijem nik đogađaja, koristio sam donekle rizičnu proveru
natpisa na dugm adim a. Pri takvoj proveri često se pogreši u pisanju, obično u rasporedu
velikih i m alih slova, a takva greška se teško uočava.
Obratite pažnju na to da su rezultati m etoda sh ow O ption D ialog( ) i showInputDia-
lo g ( ) objekti s vrednošću koju je uneo korisnik.
Vežba 17: (5) Napravite aplikaciju p o m oću klase SwingKonzola. U HTML dokum enta-
ciji na lokaciji java.sun .com pronađite klasu JPasswordField i dodajte je u program. Ako
korisnik otkuca tačnu lozinku, prikažite poruku o uspehu p om oću objekta klase JOpti-
onPane.
Vežba 18: (4) Prom enite program PoljaZaPotvrdu.java tako da ima zaseban prijemnik
tipa ActionListener za svako dugm e (um esto da traži tekst na dugm etu).
Meniji
Svaka kom ponenta koja m ože da sadrži m eni, uključujući JApplet, JFrame, JDialog i kla-
se koje su iz njih izvedene, im a m etodu setJMenuBar( ). Toj m etodi se prosleđuje linija
menija - objekat klase JMenuBar (m ože da postoji sam o jedan objekat klase JMenuBar
za određenu k om p on entu ). M eniji tipa JMenu dodaju se na liniju menija, a objekti klase
JMenuItem su stavke menija i oni se dodaju u m enije. Sa svakim elem entom tipa JMenu-
Item m ože da bude povezan poseban prijem nik događaja, i on se aktivira kada se izabere
ta stavka menija.
Kada koristite Javu i biblioteku Swing, m orate ručno da definišete sve m enije u izvor-
n om kodu. Evo vrlo jednostavnog primera menija:
//: gui/JednostavniMeniji.java
import javax.swing.*;
impnrt java.awt.*;
import java.awt.event.*;
import static net.mindview.uti1.SwingKonzola.*;
public class JednostavniMeniji extends JFrame {
private JTextField t = new JTextField(15);
private ActionListener al = new ActionListener() {
1086 Misliti na Javi
//: gui/Meniji.java
// Podmeniji, polja za potvrdu u meniju, kaskadni
// meniji, prečice i komande za pokretanje.
import javax.swing.*;
Pog!av[je 22: Grafička korisnička okruženja 1087
import java.avvt.*;
import java.awt.event.*;
import static net.mindview.util.SwingKonzola.*;
public Meniji() {
ML ml = new ML();
Poglavlje 22: Grafička korisnička okruženja 1089
Ovaj program ne pravi jednu, već dve linije menija tipa JMenuBar da bi pokazao kako
se linije m enija m ogu aktivno menjati dok se program izvršava. Vidite da je linija sasta-
vljena od objekta klase JMenu, o d n o sn o da je svaki objekat tipa JMenu napravljen od
stavki tipa JMenuItem, JCheckBoxItem ili čak od drugih menija tipa JMenu (p od m en i-
ja). Kada se linija menija sastavi, m ože se prikazati u tekućem prozoru m etodom setJMe-
nuBar( ). Obratite pažnju na to da se - nakon pritiska dugm eta - m etodom
getJM enuBar( ) proverava koji m eni je trenutno prikazan, a zatim se on zamenjuje dru-
gom linijom menija.
Pravopis i raspored m alih i velikih slova presudno su važni prilikom ispitivanja natpisa
O tvori, ali Java ne ukazuje na grešku ako ne nađe odgovarajuću stavku. Ovakva vrsta po-
ređenja znakovnih nizova čest je izvor grešaka u programiranju.
Potvrđivanje i odjavljivanje stavki m enija obavlja se autom atski. Kod kojim se obra-
đuju stavke tipa JCheckBoxMenuItem prikazuje dva načina za određivanje onoga što je
potvrđeno: pronalaženje istih znakovnih nizova (kao što je rečeno, to nije naročito bez-
bedan pristup iako se s vrem ena na vrem e koristi) i pronalaženje istih ciljnih objekata do-
gađaja. Kao što je pokazano, m etoda getState( ) m ože se koristiti za ustanovljavanje
stanja. Stanje stavke JCheckBoxMenuItem m ožete da prom enite i m etodom setS tate().
Događaji za m enije su donekle nedosledni i m ogu da izazovu zabunu: stavke tipa JMe-
nultem koriste prijem nike tipa ActionListener, dok stavke tipa JCheckboxMenuItem
koriste prijem nike tipa ItemListener. Objekti tipa JMenu m ogu da podrže i prijemnik
tipa ActionListener, ali to ob ičn o nije korisno. Po pravilu, prijem nici se povezuju sa svim
stavkama JMenuItem, JCheckBoxMenuItem ili RadioButtonM enuItem, ali je u prim e-
ru pokazano kako se objekti klasa ItemListener i ActionListener povezuju s raznim kom -
ponentam a menija.
Biblioteka Swing podržava prečice s tastature, što znači da m ožete aktivirati bilo koji
objekat klase izvedene iz klase AbstractButton (dugm e, stavku menija itd.) korišćenjem
tastature um esto miša. To je prilično jednostavno: za stavku JMenuItem m ožete da upo-
trebite preklopljeni konstruktor čiji je drugi argum ent identifikator tastera. M eđutim , ve-
ćina dugm adi tipa AbstractButton nem a takve konstruktore, pa je opštiji način za
rešavanje problem a da se koristi m etoda setM nem on ic( ). U prethodnom primeru doda-
te su prečice u neke stavke menija; indikatori prečica autom atski se pojavljuju na kom po-
nentam a.
Vidi se i korišćenje m etode setA ctionC om m and( ) kojom se zadaje kom anda akcije. To
je m alo neobično zato što je u svim slučajevim a „komanda akcije" ista kao natpis na kom -
p onenti m enija. Zašto se ne upotrebi postojeći natpis um esto novog znakovnog niza?
Zbog internacionalizacije. Ako ovaj program prilagodite drugom jeziku, menjaćete sam o
natpise u m eniju, ali ne i kod (to bi bez sum nje prouzrokovalo nove greške). Da bi se kod
koji traži znakovne nizove povezane s kom ponentam a menija lakše izvršavao, „komanda
akcije“ je neprom enljiva, dok se natpis menija m ože menjati. Ceo kod radi s„kom andom
akcije", pa na njega ne utiću prom ene natpisa menija. Obratite pažnju na to da se u ovom
programu ne ispituju sve kom pon en te m enija, pa se onim a koje se ne ispituju ne dode-
ljuje kom anda akcije.
Poglavlje 22: Grafička korisnička okruženja 1091
Najveći deo posla odvija se u prijem nicim a. Klasa BL zamenjuje linije m enija (objeka-
ta tipa JMenuBar). Klasa ML prim enjuje pristup „otkrij ko je zvonio" tako što ispituje iz-
vor događaja (ActionEventa) i konvertuje ga u JMenuItem, a p otom ispituje znakovni
niz kom ande akcije kroz nadovezane naredbe if.
Prijem nik FL je jednostavan, m ada obrađuje sve različite ukuse u m eniju ukusa. Ovaj
pristup je koristan ako je logika dovoljno prosta, ali ćete po pravilu prim enjivati pristup
upotrebljen za FooL, BarL i BazL u kojem se prijem nici povezuju sam o s jedn om stav-
kom m enija, pa nije potrebna nikakva dodatna logika za otkrivanje i zna se tačno ko je
pozvao prijem nik. Čak i ako se broj klasa koje su napravljene na ovaj način poveća, kod
unutar njih nije dugačak pa je ovakav postupak bezbedniji.
V idite da kod menija brzo narasta i postaje neuredan. I u ovom slučaju treba koristiti
vizuelnu razvojnu alatku. Dobra alatka te vrste istovrem eno će i održavati menije.
Vežba 19: (3) Prom enite program Meniji.java tako da se u m eniju koriste radio-dugm ad
um esto polja za potvrdu.
Vežba 20: (6) N apišite program koji tekstualnu datoteku deli na reči. Stavite te reči u m e-
nije i podm enije kao stavke.
Iskačući meniji
Najjednostavniji način za prim enu iskačućeg menija tipa JPopupMenu jeste da se napra-
vi unutrašnja klasa koja proširuje klasu MouseAdapter, i da se zatim po jedan objekat te
unutrašnje klase pridruži svakoj kom ponenti koja treba da izaziva iskakanje menija:
//: gui/Iskakanje.java
// Pravljenje iskačućih menija pomoću biblioteke Swing.
import javax.swin g .
import java.awt.*;
import java.awt.event.*;
import static net.mindview.uti1 .SwingKonzola.*;
m.addActionListener(al);
meni.add(m);
m = new JMenuItem("Sutra");
m.addActionl_istener(al);
meni.add(m);
meni,addSeparator();
m = new JMenuItem("Nikad");
m.addActionListener(al) ;
meni.add(m);
PrijemnikMenija pl = new PrijemnikMenija();
addMouseLi stener(pl);
t.addMouseListener(pl);
}
class PrijemnikMenija extends MouseAdapter {
public void mousePressed(MouseEvent e) {
iskociAkoTreba(e);
}
public void mouseReleased(MouseEvent e) {
iskociAkoTreba(e);
}
private void iskociAkoTreba(MouseEvent e) {
if(e.isPopupTrigger()) {
meni.show(e.getComponent(), e.getX(), e.getY());
}
}
}
public static void main(String[] args) {
run(new Iskakanjef), 300, 200);
}
} ///:-
Isti prijemnik tipa A ction L isten er povezuje se sa svim stavkama menija (objektim a
tipa JM enuItem ). Prijemnik uzim a tekst natpisa m enija i um eće ga u tekstualno polje.
Crtanje
U dobroj biblioteci za projektovanje grafičkih okruženja crtanje bi trebalo da bude prilič-
no lako, a u biblioteci Swing tako i jeste. Problem sa svakim prim erom crtanja jeste to što
su izračunavanja kojima se zadaje gde če se šta crtati ob ično m n ogo složenija od poziva
procedura za crtanje. Izračunavanja su često pom ešana s p ozivim a procedura, pa pristup
izgleda složeniji nego što stvarno jeste.
Radi jednostavnosti, razm otrim o problem s predstavljanjem podataka na ekranu;
ovde će podatke obezbeđivati ugrađena m etoda M a th .s in f), ti. matematička funkcija si-
nus. Ua bi sve biio zammljivije, i da bi se još jed n om pokazalo koliko se kom ponente bi-
blioteke Swing lako koriste, na dno obrasca biće postavljen klizač za dinam ičko
kontrolisanje broja prikazanih perioda sinusoide. Pored toga, ako prom enite veličinu
prozora, videćete da se sinusoida prilagođava toj novoj veličini.
Poglav[je 22: Grafička korisnička okruženja 1093
Iako se bilo koja kom ponenta tipa JComponent m ože koristiti kao platno za crtanje,
ako želite jednostavnu površinu (pano) za crtanje, obično treba izvesti klasu iz klase JPa-
nel. Jedino je potrebno redefinisati m etodu paintC om ponent( ) koja se poziva kad god se
kom ponenta iscrtava na ekranu. (O bično ne m orate da brinete o tom e kada će ova m etoda
biti pozvana, pošto o tom e odlučuje biblioteka Swing.) Swing prosleđuje ovoj m etodi obje-
kat tipa Graphics, a taj objekat zatim m ožete da koristite za crtanje ili slikanje p o površini.
U sledećem prim eru se sva logika povezana s crtanjem nalazi u klasi CrtajSinusoidu;
klasa Sinusoida sam o podešava program i klizač. M etoda zadajCikluse( ) om ogućuje
drugom objektu, u ovom slučaju klizaču, da kontroliše broj ciklusa.
//: gui/Sinusoida.java
// Crtanje u biblioteci Swing, korišćenjem klizača JSlider.
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import static net.mindview.uti 1 .SwingKonzola.*;
interfejsa je stateC hanged( ), što se m oglo zaključiti po im enu prijemnika. Njen argu-
m ent je objekat tipa ChangeEvent koji om ogućuje da se pregleda izvor prom ene i pročita
nova vrednost. Pozivanjem m etode zadajCikluse( ) objekta sinusi p on ovo se iscrtava
pano s novom sinusoidom .
V idećete da se većina problem a u biblioteci Swing m ože rešiti sličnim postupkom , što
je ob ičn o dosta lako, čak i ako neku k om p on en tu nikada ranije niste koristili.
Ako su vaši problem i složeniji, postoje i naprednije m etod e za crtanje, uključujući i
zrna Jave nezavisnih proizvođača i interfejs za programiranje Java 2D. Ta rešenja prevazi-
laze okvire ove knjige, ali bi trebalo da ih potražite ako vaš kod za crtanje postane previše
problem atičan.
Vežba 21: (5) Izm enite program Sinusoida.java tako što ćete objekat klase S in u soid a
pretvoriti u zrno, i to tako što ćete m u dodeliti m etode za čitanje i zadavanje svojstava.
Vežba 22: (7) Napravite aplikaciju p o m o ću klase SwingKonzoIa. Trebalo bi da im a tri
klizača, po jedan za crvenu, zelenu i plavu boju u java.awt.Color. Ostatak obrasca bi tre-
balo da bude objekat tipa JPanel koji prikazuje boju zadatu s tri klizača. Postavite i polja
za tekst čiji se sadržaj ne m ože menjati i koja prikazuju tekuće RGB vrednosti.
Vežba 23: (8) Na osnovu aplikacije Sinusoida. java napravite program koji na ekranu
prikazuje rotirajući kvadrat. Neka jedan klizač upravlja brzinom rotacije, a drugi veliči-
n om prozora.
Vežba 24: (7) Sećate li se igračke s dva dugm eta - jednim koje kontroliše vertikalno kre-
tanje nacrtane tačke i drugim koje kontroliše horizontalno kretanje? Napravite takvu
igračku koristeći program Sinusoida. java. U m esto dugm adi upotrebite klizače. Dodajte
dugm e kojim se briše cela skica.
Vežba 25: (8) Na osnovu aplikacije Sinusoida. java napravite program (aplikaciju koja
upotrebljava klasu SwingKonzoIa) koji na ekranu crta anim iranu sinusoidu što se p o-
mera u prozoru za prikazivanje kao da je u pitanju osciloskop. Anim aciju neka pokreće
klasa java.util.Timer, a njenu brzinu neka određuje kontrola javax.swing.JSlider.
Vežba 26: (3) Izm enite prethodnu vežbu tako da aplikacija im a više panela sa sinusoida-
ma. Broj panela neka se zadaje parametrom na kom andnoj liniji.
Vežba 27: (5) Izm enite vežbu 25 tako da anim aciju pokreće klasa javax.swing.Timer.
Obratite pažnju na razliku izm eđu ove anim acije i o n e u kojoj se koristi klasa ja-
va.util.Timer.
Vežba 28: (7) Napravite klasu za kockanje (bez grafičkog korisničkog okruženja). Neka se
baca pet kocaka i to više puta. Nacrtajte krivu koja prikazuje zbir palih brojeva i dinam ič-
ki se ažurira nakon svakog bacanja.
Okviri za dijalog
Okvir za dijalog je prozor koji iskače iz drugog prozora. N jegova svrha je da se izbori sa
određenom tem om a da ne zatrpa detaljima početni prozor. Dijalozi se često koriste u
grafičkim program skim okruženjima.
1096 Misliti na Javi
Da biste napravili okvir za dijalog, treba da nasledite klasu JDialog, što je sam o jedna
vrsta prozora, p op ut klase JFrame. Klasa JDialog im a svoj raspoređivač (podrazum evani
je BorderLayout), a za obradu događaja treba da dodate prijemnike. Evo vrlo jednostav-
n o g primera:
//: gui/Dijalozi.java
// Pravljenje i korišćenje okvira za dijalog.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.util.SwingKonzola.*;
Sledeći prim er je složeniji. Dijalog sadrži tabelu dugm adi tipa Ik sO k sD u gm e (napra-
vljena je p o m oću klase GridLayout). Ovo dugm e oko sebe iscrtava okvir i, u zavisnosti od
svog stanja, u sredini ne prikazuje ništa, prikazuje x ili o. D u gm e je u početku prazno, a
zatim se, u zavisnosti od toga ko je na redu da igra, pretvara u x ili o. M eđutim , on o na-
izm en ično menja stanja izm eđu x i o i kada ga pritiskate. (N a taj način igra iks-oks se
m alo kom plikuje.) Pored toga, okvir za dijalog m ože da sadrži proizvoljan broj redova i
kolona, što se zadaje prom enom brojeva u glavnom prozoru aplikacije.
//: gui/IksOks.java
// Prikaz okvira za dijalog
// i izrade komponenata.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.util.SwingKonzola.*;
Pošto sam o spoljašnji nivo klase m ože imati statičke ćlanove, unutrašnje klase ne
m ogu da sadrže statičke podatke ili ugnežđene klase.
Metoda p a in tC o m p o n e n t() crta kvadrat oko panoa, kao i oznake x ili o. Taj postupak
je prepun dosadnih izračunavanja, ali je jednostavan.
Poglavlje 22: Grafička korisnička okruženja 1099
Operaciju pritiskanja miša hvata objekat klase MouseListener koji prvo proverava da
li je na p an ou nešto ispisano. Ako nije, ispituje se roditeljski prozor da bi se ustanovilo ko
je na potezu i to se koristi za zadavanje stanja objekta IksOksDugme. P om oću m ehani -
zma unutrašnje klase, IksOksDugm e zatim pristupa svom roditelju i menja igrača koji je
na potezu. Ako dugm e već prikazuje x ili o, stanje m u se menja. U izračunavanjima se vidi
zgodno korišćenje ternarnog operatora uslovljavanja koji je opisan u poglavlju Operatori.
Nakon p rom en e stanja, objektu Iks-OksDugme menja se boja.
Konstruktor klase IksOksDijalog je prilično jednostavan: u tabelu tipa GridLayout
dodaje dugm ad po potrebi, a zatim povećava stranu svakog dugm eta za 50 piksela.
Objekat klase IksOks pokreće celu aplikaciju tako što pravi tekstualna polja za unošenje
broja redova i kolona tabele dugm adi, dugm e ,,potez“ i prijemnik tipa ActionListener. Kada
se pritisne dugm e, uzimaju se podaci iz tekstualnih polja, a pošto su to znakovni nizovi, m o-
raju se konvertovati u tip int p om oću Integer konstruktora koji prima String argument.
//: gui/TestBiranjaDatoteke.java
// Prikaz dijaloga za rad s datotekama.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.uti1 .SwingKonzola.*;
imeDatoteke.setEditable(fa1se);
p = new JPanel();
p.setLayout(new GridLayout(2,l));
p.add(imeDatoteke);
p.add(di r);
add(p, BorderLayout.NORTH);
}
class OtvoriL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser c = new JFileChooser();
// Prikazuje dijalog "Otvori":
int rVrednost = c.showOpenDialog(TestBiranjaDatoteke.this);
if(rVrednost == JFi1eChooser.APPR0VE_0PTI0N) {
imeDatoteke.setText(c.getSelectedFile(),getName());
dir.setText(c.getCurrentDirectory() .toSt r i n g O ) ;
}
if(rVrednost == JFi1eChooser.CANCEL_OPTION) {
imeDatoteke.setText("Pritisnu!i ste dugme Cancel");
dir.setText("");
}
}
}
class SnimiL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser c = new JFileChooser();
// Prikazuje dijalog "Snimi":
int rVrednost = c.showSaveDialog(TestBiranjaDatoteke.this);
if(rVrednost == JFileChooser.APPROVE_OPTION) {
imeDatoteke.setText(c.getSelectedFile().getName());
di r.setText(c.getCurrentDi rectory().toStri n g ()) ;
}
if(rVrednost == JFileChooser.CANCEL_OPTION) {
imeDatoteke.setText("Pritisnuli ste dugme Cancel");
di r.setText("");
}
}
}
public static void main(String[] args) {
run(new TestBiranjaDatoteke(), 250, 150);
}
1 ///:-
Zapam tite da postoje razne verzije dijaloga JFileChooser, uključujući i one koje kori-
ste filtre za sužavanje spiska dozvoljenih im ena datoteka.
M etodom sh o w O p en D ia lo g ( ) poziva se dijalog za otvaranje datoteke, a m etodom
show SaveD ialog( ) dijalog za snim anje datoteke. Iz tih m etoda se izlazi tek kada se dijalog
zatvori. Objekat tipa JFileChooser posle toga i dalje postoji, pa m ožete da čitate podatke
iz njega. M etode getSelectedF ile( ) i getC urrentD irectory( ) dva su načina za ispitivanje
rezultata operacije. Ako te m etode vrate null, korisnik je u dijalogu pritisnuo dugm e za
odustajanje (C ancel).
Poglavlje 22: Grafička korisnička okruženja 1101
Vežba 29: (3) U HTML dokum entaciji za biblioteku java.sw in g potražite klasu JCoIor-
C hooser. N apišite program koji pravi dugm e što, kada se pritisne, prikazuje kom andu za
biranje boja u obliku dijaloga.
//: gui/HTMLDugme.java
// Stavljanje HTML teksta na Swing komponente.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.util.SwingKonzola.*;
Tekst mora da započne oznakom < h tm l> , a potom m ožete da upotrebljavate uobiča-
jene HTML oznake. Obratite pažnju na to da nije obavezno korišćenje uobičajenih ozna-
ka za zatvaranje.
Prijemnik tipa A ction L isten er u obrazac dodaje nov natpis tipa JLabel koji sa A ži i
1 rn vil. tekst. M eđutim , taj nalpis se ne đođaje tokom konstruisanja objekta, pa m orateda
pozovete kontejnersku m etodu validate( ) da biste prouzrokovali p on ovn o iscrtavanje
kom ponenata (od n osn o, prikazivanje novog natpisa).
I ITML tekst m ožete da koristite i u objektim a klasa JTabbedPane, JM enuItem , JTooI-
Tip, JR adioB utton i JCheckBox.
Vežba 30: (3) Napišite program u kojem se HTML tekst ispisuje na svim objektima
nabrojanim u prethodnom pasusu.
1102 Misliti na Javi
//: gui/Napredak.java
// Korišćenje traka napredovanja, klizača i monitora napredovanja.
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.awt.*;
import static net.mindview.util.SwingKonzola.*;
} ///:-
Poglavlje 22. Grafička korlsnička okruženja 1103
pb.setModel(sb.getModel());
Ove dve kom p onente m ogli biste da kontrolišete i p om oću prijemnika, ali je ovo reše-
nje prostije. ProgressM onitor nem a m odel, pa sam m orao da upotrebim prijemnik. Ve-
rovatno ste prim etili da se ovaj ProgressMonitor kreće sam o unapred i da se zatvara kada
d ođe do kraja.
Klasa JProgressBar je prilično očigledna, ali klasa JSlider im a m noštvo opcija, npr.
orijentisanje, veće i manje zareze na klizaču itd. Obratite pažnju na to kako se lako dodaje
ivica sa natpisom .
V ežba31: (8) Napravite „indikator asim ptotskog napredovanja“ koji se usporava s pribli-
žavanjem završnoj tački. Dodajte mu nasum ično pogrešno ponašanje da bi s vremena na
vrem e izgledalo kao da počinje da se ubrzava.
Vežba 32: (6) Izm enite program Napredak.java tako da za povezivanje klizača i trake na-
predovanja ne koristi zajedničke m odele, već prijemnik.
try {
UIManager.setLookAndFeel (
UIManaqer.getSystemLookAndFee1ClassNamef));
/ cdtcn ( c x c e p i 1on ej \
throw new RuntimException(e);
Blok catch m ože da bude prazan zato što će U IM anager učitati podrazum evan izgled
i ponašanje ako pokušaj da zadate drugačiji izgled ne uspe. M eđutim , inform acija o izu-
zetku biva veom a korisna pri traženju grešaka, pa u bloku catch m ožete staviti barem na-
redbu za ispisivanje.
Evo programa koji na osnovu argum enta s kom andne linije zadaje izgled i ponašanje,
i pokazuje kako neke kom ponente u tom slučaju izgledaju:
//: gui/IzgledlPonasanje.java
// Biranje različitih izgleda i ponašanja.
// {Args: motif}
import javax.swing.*;
irrport java.awt.*;
import static net.mindview.util.SwingKonzola.*;
UIManager.setLookAndFeel(UIManager.
getSystemLookAndFeelClassName());
} catch(Exception e) {
e.printStackTrace();
}
} else if(args[0] .equals("motif")) {
try {
UIManager.setLookAndFeel("com.sun.java.“+
"swing.plaf.motif.MotifLookAndFeel");
} catch(Exception e) {
e.printStackTrace();
}
} else greskaKoriscenja();
// Obratite pažnju na to da se izgled i ponašanje moraju
// zadati pre pravljenja bilo kakvih komponenata.
run(new IzgledIPonasanje()» 300, 300);
}
} ///:-
Za eksplicitno zađavanje izgleda i ponašanja koristi se M otifL ookA ndF eel. Sam o se
taj i podrazum evani ,,m etalni“ izgled sigurno m ogu koristiti na svim platformam a; iako
postoje kom ponente koje oponašaju izgled i ponašanje W indow sa i M acOS-a, one se
m ogu koristiti sam o na odgovarajućim platform am a (dobijaju se kada pozovete m etodu
getSystem L ookA ndF eeIC lassN am e() dok radite na određenoj platform i).
M oguće je napraviti i sopstveni paket elem enata koji opisuju izgled i ponašanje, na
prim er ako pravite strukturu aplikacije za kom paniju koja želi da ta aplikacija posebno iz-
gleda. To je velik posao i uveliko premašuje tem u ove knjige (zapravo, otkrićete da se
njom e ne bave ni m noge knjige isključivo posvećene biblioteci Swing).
je, pom oću ikonice na radnoj površini ili programa za upravljanje aplikacijama koji je deo
realizacije JNLP-a. Aplikacija se m ože pokretati čak i s Web lokacije s koje je bila preuzeta.
U vrem e izvršavanja, JNLP aplikacija m ože dinam ički da preuzim a resurse sa Interne-
ta i da autom atski proverava verziju ako je korisnik povezan sa Internetom . To znači da
im a sve prednosti apleta, a i prednosti sam ostalnih aplikacija.
Korisnikov računar mora oprezno da tretira JNLP aplikacije, kao i aplete. Zato se
JNLP aplikacije izvršavaju sam o u „bazenu s peskom “, kao apleti. U koliko su u potpisa-
nim JAR datotekama, korisnik m ože odlučiti da im ukaže poverenje i dopusti korišćenje
resursa njegovog računara. (Isto važi i za aplete.) Za razliku od apleta, p o m o ću usluga
JNLP interfejsa za programiranje JNLP aplikacije m ogu zatražiti pristup određenim re-
sursima korisnikovog računara i kada su u nepotpisanim JAR datotekam a. Tokom izvrša-
vanja programa, od korisnika se traži da odobri te zahteve.
JNLP je specifikacija protokola, a ne njegova realizacija. Zato je za korišćenje neop-
h odna i neka realizacija. Java Web Start (JAWS) Sunova je besplatna zvanična referentna
realizacija koja se isporučuje u sklopu Jave SE5. U koliko želite da je koristite za razvoj,
njena JAR datoteka (javaws.jar) mora biti u putanji klasa vašeg računara; najjednostav-
nije rešenje je dodati datoteku javaw s.jar putanji klasa —prem estiti je iz njene uobičajene
putanje Java instalacije na putanju jre/lib. Ako JNLP aplikaciju nameravate da instalirate
s Web servera, on bi trebalo da prepoznaje MIME tip ap p lication /x-java-jn lp -file. To je
unapred konfigurisano u novijim verzijama servera Tomcat {http://jakarta.apache.org/
tom cat). Odgovarajuće pojedinosti pročitajte u korisničkim uputstvim a za svoj server.
Nije teško napraviti JNLP aplikaciju. Napravite običnu aplikaciju, kom prim ujte je u
JAR arhivu i zatim obezbedite datoteku za pokretanje - jednostavnu XML datoteku koja
korisnikovom računaru daje sve informacije potrebne za preuzim anje i instaliranje te ap-
likacije. U koliko ne potpišete JAR arhivu, morate da koristite usluge koje interfejs za pro-
gramiranje JNLP aplikacija pruža za sve tipove resursa kojima aplikacija treba da pristupi
na računaru korisnika.
Sledi varijanta programa TestBiranjaD atoteka.java koja za otvaranje birača datoteka
upotrebljava JNI.P usluge, čim e om ogućuje da klasa bude distribuirana u obliku JNLP
aplikacije u nepotpisanoj JAR arhivi.
//: gui/jnlp/JnlpBiranjeDatoteka.java
// Otvaranje datoteka na lokalnom računaru pomoću JNLP-a.
// {Requires: javax.jnlp.Fi1eOpenService;
// javaws.jar mora biti u putanji klasa}
// Ovako ćete napraviti datoteku jnlpbiranjedatoteka.jar:
// cd ..
// cd ..
// jar cvf gui/jnlp/jnlpbiranjedatoteka.jar gui/jnlp/*.class
package g ui .jnlp;
import javax.jnlp.*:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
Poglavjje 22: Grafička korisnička okruženja 1107
snimi.setEnabled(true);
}
1 108 Misliti na Javi
}
class SnimiL implements ActionListener {
public void actionPerformed(ActionEvent e) {
FileSaveService fs = null;
try {
fs = (FileSaveService)ServiceHanager.lookup(
"javax.jnlp.FileSaveService");
} catch(Unavailab1eServiceException use) {
throw new RuntimeException(use);
}
if(fs != null) {
try {
sadrzajDatoteke = fs.saveFileDialog(".",
new String[]{"txt"},
new ByteArrayInputStream(
ep.getText().getBytes()),
sadrzajDatoteke.getName());
if(sadrzajDatoteke == null)
return;
imeDatoteke.setText(sadrzajDatoteke.getName());
} catch(Exception exc) {
throw new RuntimeException(exc);
}
}
}
}
public static void main(String[] args) {
JnlpBiranjeDatoteka bd = new JnlpBiranjeDatoteka();
bd.setSize(400, 300);
bd.setVisible(true);
}
} ///■■-
/ / :! gui/jnlp/biranjedatoteka.jnlp
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec = "1.0+"
codebase="fi1e:C:/AAA-TIJ4/code/gui/jnlp"
href="biranjedatoteka.jnlp">
<information>
<title>Primer FileChooser aplikacije</title>
<vendor>Mindview Inc.</vendor>
<description>
Jnlp aplikacija za biranje datoteka
</description>
<description kind="short">
Pokazuje otvaranje, čitanje i upisivanje u tekstualnu datoteku
</description>
<icon href="inindview.gif"/>
<off1ine-all owed/>
</information>
<resources>
<j2se version="1.3+"
href="http://java.sun.com/products/autodl/j2se"/>
<jar href="jnlpbiranjedatoteka.jar" download="eager"/>
</resources>
<application-desc
main-class = "gui.jnlp.JnlpBi ranjeDatoteka"/>
</jnlp>
///:-
N avedenu datoteku za pokretanje naći ćete u datoteci izvornog koda ove knjige (m ože
se preuzeti na adresi w w w .M in d V iew .n et) pod im en om biranjedatoteka.jnlp (bez prvog
i poslednjeg reda), u istom direktorijum u kao JAR arhiva. Kao što vidite, radi se o XML
datoteci s jed n om oznakom <jnlp>. Ona im a nekoliko podelem enata, m anje-više jasnih
po sebi.
Atribut spec elem enta jnlp saopštava klijentskom sistem u koja verzija JNLP-a m ože da
pokrene aplikaciju. Atribut codebase pokazuje na URL adresu na kojoj su resursi i dato-
teka za pokretanje. Ovde pokazuje na direktorijum lokalnog računara, što je dobar način
za testiranje ove aplikacije. N a d a m se da shvatate kako tu p u ta n ju n io ra tep ro m en iti tako da
pokazuje na odgovarajući direktorijum vašeg rac'unara —tek tada će seprogram učitati. Atri-
but href specificira im e datoteke koju treba učitati.
Oznaka inform ation im a razne p od elem ente koji pružaju inform acije o aplikaciji.
Njih čita adm inistrativna konzola Java Web Start ili druga njoj ekvivalentna realizacija
JNLP-a koja instalira JNLP aplikaciju i om ogu ću je korisniku da je pokrene s kom andne
liniie, pravi prečice itd.
Svrha oznake resources slićna je svrsi oznake <applet> u U'i iVft. datoteci. Podelem ent
j2se specificira verziju J2SE potrebnu za pokretanje aplikacije, a podelem ent jar JAR da-
toteku u kojoj je klasa arhivirana. Elem ent jar im a atribut download čije vrednosti eager
od n osn o lazv kazuju realizaciji JNLP-a da li pre pokretanja aplikacije m ora da učita celu
arhivu ili ne mora.
1110 Misliti na Javi
Atribut application-desc saopštava realizaciji JNLP-a koja klasa je izvršna klasa (ulaz-
na tačka) JAR arhive.
D rugi koristan podelem ent oznake jnlp jeste oznaka security koje u gornjoj datoteci
nem a. Evo kako izgleda oznaka security:
<security>
<a11-permissions/>
< secu rity/>
//:! gui/jnlp/biranjedatoteka.html
<html>
Sledite uputstva iz JnlpBiranjeDatoteka.java za pravljenje
jnlpfi1echooser.jar, a zatim:
<a href="biranjedatoteka.jnlp">pritisnite ovde</a>
</html>
///: -
Dugotrajni zadaci
Jedna od osnovnih grešaka pri programiranju grafičkih korisničkih okruženja jeste upo-
treba rasporedivača za izvršavanje dugotrajnog zadatka. Sledi jednostavan primer:
//: gui/DugotrajanZadatak.java
// Loše projektovan program.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.*;
import static net.mindview.util.SwingKonzoia.*;
/ / : gui/DugotrZadKojiSeMozePrekinuti.java
// Dugotrajni zadaci u nitima.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.*;
import static net.mindview.util.SwingKonzola.*;
setLayout(new FlowLayout());
add(dl);
add(d2);
}
public static void main(String[] args) {
run(new DugotrZadKojiSeMozePrekinuti(), 200, 150);
}
} ///:-
//: net/mindview/util/StavkaZadatka.java
// Objekat tipa Future i Callable koji ga je napravio.
package net.mindview.util;
import j a v a . u t i l .concurrent.*;
//: net/mindview/util/MenadzerZadataka.java
// Upravljanje redom zadataka i njegovo izvršavanje.
package net.mindview.util;
import j a v a .ut i1 .co nc u r r e n t .41;
import j a v a . u t i1.*;
// : gui/DugotrCallableKoj1SeMozePrekinuti.java
// Korišćenje Callable objekata za dugotrajne zadatke.
import javax.swing.*;
import java.awt.*;
Poglav[je 22: Grafička korisnička okruženja 1115
import java.awt.event.*;
import java.uti1.concurrent.*;
import net.mindview.util.*;
import static net.mindview.util.SwingKonzola.*;
public class
DugotrCallableKojiSeMozePrekinuti extends JFrame {
private JButton
dl = new JButtonC'Pokreni dugotrajan zadatak"),
d2 = new JButton("Ugasi dugotrajan zadatak"),
d3 = new JButton("Daj rezultate");
private MenadzerZadataka<String,CallableZadatak> menadzer =
new MenadzerZadataka<String,CallableZadatak>();
public DugotrCallableKojiSeMozePrekinuti() {
dl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Cal 1ableZadatak zadatak = new CallableZadatak();
menadzer.add(zadatak);
System.out.println(zadatak + " dodat u red za čekanje");
}
});
d2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for(String rezultat : menadzer.purge())
System.out.pri ntln (rezul tat);
}
});
d 3 .addActionListener(new ActionListener() {
public void actionPerformed(A*tionEvent e) {
// Primer poziva metode klase Zadatak:
for(StavkaZadatka<String,CallableZadatak> tt :
menadzer)
tt.zadatak.id(); // Nije potrebna eksplicitna konverzija tipa
for(String rezultat : menadzer.getResults())
System.out.println(rezultat);
}
/);
setLayout(new FlowLayout());
add(dl);
add (d2);
add (d3);
1116 Misliti na Javi
}
public static void main(String[] args) {
run(new DugotrCallableKojiSeMozePrekinuti(), 200, 150);
}
} ///: -
Kao što vidite, CallableZadatak radi isto što i Zadatak, sem što vraća i rezultat - u
ovom slučaju String identifikator zadatka.
Za rešavanje istog problem a napisane su i n e-Sw ing uslužne klase (koje se ne isporuču-
ju u standardnoj distribuciji Jave). To su SwingWorker (naći ćete je na Sunovoj Web lo-
kaciji) i F oxtrot (na adresi http://foxtrot.sourceforge.net). D ok sam pisao knjigu, te klase
nisu bile m odifikovane tako da iskoriste m ehanizam Jave SE5 Callable/Future.
Č esto krajnjem korisniku valja dati nekakav vizuelni znak da se zadatak izvršava i oba-
vestiti ga dokle je odm aklo izvršavanje. To se ob ičn o radi p om o ću klasa JProgressBar ili
ProgressMonitor. U narednom prim eru upotrebiću ProgressMonitor:
}
}
);
}
} catch(InterruptedException e) {
monitor.close();
System.out.println(this + " prekinut");
return "Rezultat: " + this + " prekinut";
}
System.out.println(this + " završen");
return "Rezultat: " + this + " završen";
}
public String toString() { return "Zadatak " + id; }
};
public class NadziranDugotrCallable extends JFrame {
private JButton
dl = new JButton("Pokreni dugotrajan zadatak"),
d2 = new JButton("Ugasi dugotrajan zadatak"),
d3 = new JButton("Daj rezultate1');
private MenadzerZadataka<String,NadziranCallable> menadzer =
new MenadzerZadataka<String,NadziranCallable>();
public NadziranDugotrCallableO {
dl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
NadziranCal1able zadatak = new NadziranCallable(
new ProgressMonitor(
Nadzi ranDugotrCal1able.thi s ,
"Dugotrajan Zadatak", 0, 0)
);
menadzer.add(zadatak);
System.out.println(zadatak + " dodat u red za čekanje");
}
});
d2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for(String rezultat : menadzer.purge())
System.out.println(rezultat);
}
});
d3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for(String rezultat : menadzer.getResults())
System.out.println(rezultat);
)
});
setLayout(new F1owLayout());
add(dl);
add(d2);
1118 Misliti na Javi
add(d3);
}
public static void main(String[] args) {
run(rew NadziranDugotrCallable(), 200, 500);
}
} ///:-
Konstruktor klase NadziranCallable prim a objekat tipa ProgressM onitor kao argu-
m ent, a njegova m etoda c a ll( ) ažurira ProgressM onitor svako pola sekunde. Imajte u
vidu da je NadziranCallable zaseban zadatak i da ne bi trebalo da neposredno kontroliše
ulazno/izlazne operacije, pa sam m onitoru m etod om SwingUtilities.invokeLater( )
podneo informacije o prom eni stepena napredovanja. U Sunovom udžbeniku za Swing
(„Swing Tutorial" na adresi http://java.sun.com ) prikazan je drugačiji pristup; tam o se
upotrebljava Swing Timer, koji proverava status zadatka i ažurira m onitor.
Ako se na m onitoru pritisne dugm e cancel, m etoda m onitor.isC anceled( ) vraća true.
Ovde zadatak sam o poziva m etodu in terru p t( ) za sopstvenu nit, što će ga dovesti u od-
redbu catch gde se m onitor gasi m etod om cIo se( ).
Ostatak koda je uglavnom isti kao pre, sem što se pravljenje objekta tipa ProgressMo-
nitor odvija unutar konstruktora zadatka NadziranDugotrCallable.
Vežba 33; (6) Izmenite DugotrCallableKojiSeMozePrekinuti.java tako da sve zadatke
izvršava paralelno, a ne sekvencijalno.
//: gui/ObojeneKutije.java
// Vizuelni prikaz višenitnog izvršavanja.
// {Args: 12 50}
import javax.swing.*;
import java.awt.*;
import java.util.concurrent.*;
import java.util.*;
import static net.mindview.uti 1 .SwingKonzola.*;
ObojeneKutije konfiguriše objekat tipa GridLayout tako da u svakoj dim enziji ima
brojcelija. Zatim dodaje odgovarajući broj ObKut objekata da popuni taj broj ćelija i sva-
kom prosleđuje broj pauza. U m etodi m a in ( ) vidite da pauza i brojcelija imaju podrazu-
m evane vrednosti koje se m ogu menjati prosleđivanjem argumenata na komandnoj liniji.
bve se radi u kiasi ObKut. Ona je izvedena iz klase JPanel i realizuje interfejs Runna-
ble, tako da svaki objekat tipa JPanel m ože biti nezavisan zadatak. Tim zadacim a upravlja
grupa niti ExecutorService.
1120 Misliti na Javi
Tekuća boja ćelije je boja. Boje pravi konstruktor klase Color čiji ulazni argum ent tre-
ba da bude 24-bitni broj koji u ovom slučaju pravim o nasum ično.
M etoda paintC om ponent( ) sasvim je jednostavna; ona sam o menja boju kako joj
kaže boja i ceo pano (objekat tipa JPanel) popunjava njom .
U m etodi r u n ( ) postoji beskonačna petlja koja objektu boja zadaje novu nasum ično
napravljenu boju i zatim poziva rep a in t( ) da bi je pokazala. Potom ta nit - prim enom
m etode sle e p () - odlazi na spavanje, za vrem e zadato na kom andnoj liniji.
Poziv m etode repaint( ) unutar m etod e r u n ( ) zaslužuje pažnju. Na prvi pogled izgle-
da da pravim o m nogo niti i da se sve m oraju prebrojiti. M ožda vam izgleda da tim e krši-
m o princip na osnovu koga sve zadatke treba poslati raspoređivaču, od n osn o redu za
čekanje. M eđutim , te niti zapravo ne m odifikuju deljeni resurs. Kada one p ozovu m etodu
rep ain t( ), ona ne preduzim a prebojavanje nego sam o postavlja određeni indikator
,,zamrljano“, kojim pokazuje da je ta oblast kandidat za prebojavanje kada raspoređivač
sledeći put bude sprem an za tu akciju. Zato program ne prouzrokuje problem e sa Swin-
govim nitima.
Kada nit raspoređivača događaja pozove m etodu p a in t( ), ona prvo pozove
p aintC om ponent( ), zatim paintB order( ) i onda paintC hildren( ). Ako u izvedenoj
kom ponenti želite da redefinišete m etodu p a in t( ), ne zaboravite da pozovete verziju
p a in t( ) iz osnovn e klase, tako da se uradi šta treba.
Upravo zato što je ovaj dizajn prilagodljiv i niti su vezane za svaki elem ent panoa,
m ožete da eksperim entišete i pravite proizvoljan broj niti. (U praksi, ograničenje nam eće
broj niti s kojima vaša JVM m ože da izađe na kraj.)
Ovaj program om ogućuje zanim ljivo poređenje perform ansi, pošto m ože da pokaže
velike razlike u ponašanju i perform ansam a raznih realizacija JVM niti i raznih platformi.
Vežba 34: (4) Izm enite program ObojeneKutije.java tako da prvo nasum ično prska tač-
ke (,,zvezde“) po platnu, a zatim nasum ično menja boje tih ,,zvezda“.
vizuelna predstava je često izgled kom ponente u program u koji se izvršava. Zbog toga
deo postupka vizuelnog programiranja podrazum eva prevlačenje kom ponente s palete i
spuštanje na obrazac. Alatka za pravljenje aplikacija piše kod dok vi prevlačite kom p o-
nente, a taj kod prouzrokuje pravljenje kom ponente u izvršnom programu.
Sam o prevlačenje kom ponente u obrazac obično nije dovoljno za kom pletiranje pro-
grama. Č esto m orate da prom enite neka njena obeležja, npr. boju, tekst koji sadrži, bazu
podataka s kojom je povezana itd. Obeležja koja se m ogu prom eniti tokom projektovanja
označavaju se kao svojstva (engl. properties). Sa svojstvim a kom ponente m ožete da radite
p om o ću alatke za pravljenje aplikacija, a kada napravite program , svojstva kom p on en te
će biti snim ljena da bi se m ogla rekonstruisati kada se program pokrene.
D osad ste se sigurno navikli na ideju da objekat nem a sam o svojstva, već i ponašanje.
Tokom projektovanja, ponašanje vizuelne kom ponente je delim ično predstavljeno doga-
đajim a. Događaj znači: „Evo nečega što bi se m oglo dogod iti kom p on en ti“. O bično od lu -
čujete šta treba da se desi nakon događaja tako što povezujete kod sa određenim
događajem .
Evo izuzetno važnog dela: alatka za pravljenje aplikacija koristi refleksiju za dinam ičko
ispitivanje k om p on ente i otkrivanje svojstava i događaja koja kom ponenta podržava.
Kada to sazna, m ože da prikaže svojstva i om ogu ći đa ih m enjate (uz snim anje stanja), ali
m ože i da prikaže događaje. Po pravilu, vi dvaput pritiskate m išem , a alatka za pravljenje
aplikacija pravi kod i povezuje ga s tim događajem . U tom trenutku treba sam o da na-
pišete kod koji se izvršava kada se desi događaj.
Dakle, alatka za pravljenje aplikacija obavlja najveći deo posla um esto vas. Zbog toga
se m ožete usredsrediti na izgled programa i na to šta on treba da radi, a da se oslonite da
će ta alatka obaviti kom pletno povezivanje. Alatke za vizuelno programiranje izuzetno su
popularne zato što prim etno ubrzavaju pravljenje aplikacije. To posebno važi za korisnič-
ka okruženja, ali često i za druge delove aplikacije.
Šta je zrno?
Kada se prašina slegne, vidi se da je kom ponenta sam o blok koda koji je ob ičn o predsta-
vljen u obliku klase. Ključno pitanjeje m ogućnost alatke za pravljenje aplikacija da otkri-
va svojstva i događaje te kom ponente. Da bi napravio VB kom ponentu, programer je
m orao da piše prilično složen kod u kom e je poštovao određena pravila za otkrivanje
svojstava i događaja. Delphi je bila alatka za vizuelno programiranje druge generacije. Taj
jezik je aktivno koristio vizuelno programiranje pa se vizuelna kom ponenta m n ogo lakše
pravila. M edutim , izradu vizuelnih kom ponenata na najviši nivo dovela je Java, i to p o-
m oću zrna (engl. Java BcansJ, pošto je zrno sam o klasa. Ne m orate pisati nikakav dodatni
kod niti koristiti specijalna proširenja jezika da bi nešto postalo zrno. Zapravo, treba
sam o m alo dn prom enite način im enovanja metoda. Na osn ovu imena m etode, alatka za
pravljenje aplikacija zna da li se radi o svojstvu, dogadaju ili običnoj m etodi.
U dokum entaciji za Javu ova pravila im enovanja pogrešno su označena kao „projektni
obrazac“. To je nesporazum , pošto su projektni obrasci dovoljan izazov i bez ove zbrke
(naći ćete ih u knjizi T h in kin g in Patterns w ith Java koju m ožete preuzeti s lokacije
1 122 Misliti na Javi
w w w .M ind V iew .n et). Zrna Jave nisu opisana projektnim obrascem , već sam o pravilim a za
im enovanje, i to prilično jednostavnim :
1 . Da bi se neko svojstvo nazvalo xxx, ob ičn o treba napraviti dve metode: getX xx() i
setX x x ( ). Obratite pažnju na to da se prvo slovo iza get ili set autom atski pretvara
u m alo da bi se dobilo im e svojstva. Tip koji vraća m etoda get isti je kao tip argu-
m enta m etode set. Im e svojstva i tip ovi koje vraćaju m etode get i set ne moraju da
b udu povezani.
2. Za svojstvo tipa boolean m ožete da koristite prethodni pristup s m etodam a get i
set, ali um esto get m ožete da koristite is.
3. O bične m etode zrna ne poštuju prethodna pravila za dodeljivanje im ena, ali su jav-
ne.
4. Za događaje se koristi pristup ,,prijemnika“ iz biblioteke Swing. To je isto on o što
ste već videli: m etodam a addBounceListener(BounceListener) i removeBounce-
Listener(BounceListener) obrađuje se događaj BounceEvent. Najčešće će ugrađe-
ni događaji i prijem nici zadovoljavati vaše potrebe, ali m ožete da napravite i
sopstvene događaje i prijem ničke interfejse.
Ova saznanja m ožem o da iskoristim o za pravljenje jednostavnog zrna:
//: frogbean:Frog.java
// Obično zrno Jave.
package frogbean;
import java.awt.*;
import java.awt.event.*;
class Spots {}
Prvo uočavate da je ovo obična klasa. Sva njena polja ob ićn o će biti privatna i m oći će
da im se pristupa sam o preko m etoda. U skladu s pravilom za im enovanje, svojstva su
jum ps, color, spots i jum per (obratite pažnju na prom enu veličine prvog slova im ena
svojstva). Iako je im e lokalnog identifikatora isto kao im e svojstva u prva tri slučaja, svoj-
stvo jum per pokazuje da im e svojstva nije uslov za korišćenje tačno određenog identifi-
katora za lokalne prom enljive. (Lokalne prom enljive za to svojstvo čak ne moraju ni da
postoje.)
Događaji koje generiše ovo zrno su ActionEvent i KeyEvent, što zaključujemo po
im enim a m etoda add i rem ove za rad s prijem nicim a. Konačno, vidite da je obična m e-
toda cro a k ( ) i dalje deo zrna sam o zato što je javna, a ne zato što poštuje pravilo dodelji-
vanja im ena.
//; gui/IspitivanjeZrna.java
// Ispitivanje Zrna klasom Introspector.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.lang.reflect.*;
import static net.mindview.util.SwingKonzola.*;
print("Javne metode:");
for(MethodDescriptor m : bi .getMethodDescriptorsO)
print(m.getMethod() .toSt r i n g O ) ;
print("======================");
print("Podrška za događaje:");
for(EventSetDescriptor e : bi .getEventSetDescriptorsO) {
print("Tip pri jemnika:\n 11 + e.getListenerType() .getNameO);
for(Method lm : e.getListenerMethodsO)
print.("Metoda pri jemnika:\n " + lm.getName());
for(MethodDescriptor lmd : e.getListenerMethodDescriptorsO)
print("Opis metode:\n " + lmd.getMethod());
Method dodavanje = e.getAddListenerMethod();
print("Metoda za dodavanje:\n " + dodavanje);
Method uklanjanje = e.getRemoveListenerMethod();
printC'Metoda za uklanjanje:\n " + uklanjanje);
print("====================");
}
}
class Ispitivac implements ActionListener {
public void actionPerformed(ActionEvent e) {
String ime = upit.getText();
C1ass<?>c = nul1;
try {
c = Class.forName(ime);
} catch(ClassNotEoundException ex) {
rezultati.setText("Ne mogu da pronađem" + ime);
return;
}
ispitaj(c);
public IspitivanjeZrnaO {
JPanel p = new JPanel();
p.setLayout(new FlowLayout());
p.add(new JLabel("Puno ime zrna:"));
p.add(upit);
cp.add(BorderLayout.NORTH, p ) ;
cp.add(new JScrollPane(rezultati));
Ispitivac isp = new Ispitivac();
upit.addActionListener(isp);
upi t .setText("frogbean.Frog");
// Prinudna provera
isp.actionPerformed(
new ActionEvent(isp, 0, ""));
}
public static void main(String[] args) {
run(new IspitivanjeZrnaO, 600, 500);
}
} ///:-
1126 Misliti na Javi
Tip svojstva:
Color
Ime svojstva:
color
Metoda za čitanje:
public Color getColor()
Metoda za upisivanje:
public void setColor(Color)
Tip svojstva:
boolean
Ime svojstva:
jumper
Metoda za čitanje:
public boolean isJumper()
Metoda za upisivanje:
public void setJumper(boolean)
Tip svojstva:
int
Ime svojstva:
jumps
Metoda za čitanje:
public int getJumps()
Poglavlje 22: Grafička korisnička okruženja 1127
Metoda za upisivanje:
public void setJumps(int)
Tip svojstva:
frogbean.Spots
Ime svojstva:
spots
Metoda za čitanje:
public Spots getSpots()
Metoda za upisivanje:
public void setSpots(Spots)
Javne metode:
public void setSpots(frogbean.Spots)
public void setColor(Color)
public void setJumps(int)
public boolean isJumper()
public frogbean.Spots getSpotsO
public void croak()
public void addActionListener(ActionListener)
public void addKeyListener(KeyListener)
public Color getColor()
public void setJumper(boolean)
public int getJumps()
public void removeActionListener(ActionListener)
public void removeKeyListener(KeyListener)
Podrška za događaje:
Tip prijemnika:
KeyLi stener
Metoda prijemnika:
keyTyped
Metoda prijemnika:
keyPressed
Metoda prijemnika:
keyReleased
Opis metode:
public void keyTyped(KeyEvent)
Opis metode:
public void keyPressed(KeyEvent)
Opis metode:
public void keyReleased(KeyEvent)
Metoda za dodavanje:
public void addKeyListener(KeyListener)
Metoda za uklanjanje:
public void removeKeyListener(KeyListener)
Tip prijemnika:
Acti onLi stener
1128 Misliti na Javi
Metoda prijemnika:
actionPerformed
Opis metode:
public void actionPerformed(ActionEvent)
Metoda za dodavanje:
public void addActionListener(ActionListener)
Metoda za uklanjanje:
public void removeActionListener(ActionListener)
Ovaj program otkriva najveći d eo svojstava koje vidi klasa Introspector dok na osno -
vu zrna pravi objekat tipa Beanlnfo. Prim etićete da su tip svojstva i njegovo im e nezavi-
sni. Obratite pažnju na to da su sva slova u im enu svojstva mala. (Jedino se odstupa kada
im e svojstva poćinje s nekoliko uzastopnih velikih slova.) Zapamtite i to da se im ena m e-
toda kakva ovde vidite (npr. m etod e za čitanje i upisivanje) zapravo dobijaju iz objekta
tipa M ethod koji se m ože koristiti za pozivanje odgovarajuće m etode.
Spisak javnih m etoda sadrži m etod e koje nisu povezane s nekim svojstvom ili događa-
jem , kakva je m etoda croak ( ), ali i o n e koje jesu povezane sa svojstvima i događajima. To
su sve m etode zrna koje m ožete da pozovete iz programa, a alatka za pravljenje aplikacija
m ože ih sve prikazati dok pišete program da bi vam olakšala posao.
Konačno, prim ećuje se da su događaji p otp u n o analizirani i razdvojeni na interfejs
prijemnika, njegove m etod e i m etod e za dodavanje i uklanjanje prijemnika. U osnovi,
kada im ate objekat tipa Beanlnfo, m ožete da saznate sve što je važno o zrnu. M ožete i da
pozivate m etode zrna, čak i ako nem ate nikakve druge inform acije osim ovog objekta (to
je još jedna osobina refleksije).
Naprednije zrno
Sledeći prim er je sam o nešto m alo napredniji, mada je frivolan. To je objekat klase JPanel
koji iscrtava mali krug oko miša kad god se on pom eri. Kada pritisnete taster miša, reč
Bang! se pojavljuje u sredini ekrana i pokreće se prijem nik događaja.
M ožete da prom enite sledeća svojstva: veličinu kruga, boju, veličinu i reč koja se pri-
kazuje kada se pritisne taster miša. Objekat BangBean takode ima sopstvene m etode ad-
dActionListener( ) i rem oveA ctionListener( ) p om oću kojih m ožete da povežete zrno sa
svojim prijem nikom koji se pokreće kada korisnik pritisne m išem . Sada bi već trebalo da
um ete prepoznati podršku za svojstva i događaje:
//: bangbean:BangBean.java
// Grafičko zrno.
package bangbean;
import javax. swi ng
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.uti1.*;
Poglavlje 22: Grafička korisnička okruženja 1129
Prvo ćete prim etiti da klasa BangBean realizuje interfejs Serializable. To znači da alat-
ka za pravljenje aplikacija m ože da učita sve inform acije o zrnu p om oću serijalizacije, na-
kon što projektant programa podesi vrednosti svojstava. Kađa se zrno napravi kao deo
izvršne aplikacije, snim ljena svojstva se rekonstruišu pa se dobija tačno on o što ste pro-
jektovali.
U potpisu m etode addA ctionListener( ) vidite da ona m ože da generiše izuzetak To-
oManyListenersException. To znači da je događaj jcdnosm eran, tj. da se sam o jedan pri-
jem nik obaveštava o tom e da se desio. O b ičn o ćete koristiti višesm erne događaje tako da
o događaju bude obavešteno više prijem nika. M eđutim , ovde već uiazim o u tem e koje će
biti razm otrene u odeljku „Zrna Jave i sinhronizacija“. Jednosm ernim događajem zasad
ćem o zaobići ovaj problem .
Kada pritisnete taster miša, u zrnu se ispisuje tekst, a ako polje actionListener nije
null, pravi se nov objekat tipa ActionEvent i šalje se prijem niku. Kad god se pom eri miš,
pam te se njegove nove koordinate i pozadina menja boju (uz brisanje teksta koji se nalazi
na pozadini, kao što ćete videti).
Evo klase BangBeanTest koja om ogu ću je testiranje zrna kao apleta ili aplikacije:
//: gui/BangBeanTest.java
// {Timeout: 5} U testiranju, ugasi nakon 5 sekundi
package bangbean.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.uti1 .*;
import static net.mindview.util.SwingKonzola.*;
Poglavlje 22: Grafička korisnička okruženja 1131
Ova klasa se neće koristiti kada se zrno nalazi u razvojnom okruženju, ali je korisna
kao način za brzo testiranje svih zrna. BangBeanTest stavlja zrno BangBean u aplet, po-
vezujući s njini jednostavan prijem nik tipa ActionListener koji ispisuje broj đogađaja u
tekstualnom polju kad god se desi događaj. Alatka za izradu aplikacija obično pravi naj-
veći deo koda koji koristi zrno.
Kada ispitate ovo zrno Idasom IspitivanjeZrna ili ga stavite u razvojno okruženje koje
podržava zrna, prim etićete da ima m n ogo više svojstava i akcija nego što se vidi u gor-
njem kodu. Razlog je to što je klasa BangBean izvedena iz klase JPanel, a JPanel je takođe
zrno, pa se prikazuju i njegova svojstva i dogadaji.
Vežba 35: (6) Pronadite na Internetu i preuzm ite jednu ili više besplatnih vizuelnih alatki
za pravljenje grafičkih korisničkih okruženja, ili kupite neku. Saznajte šta je neophodno
da bi se zrno BangBean unelo i koristilo u tom okruženju.
//: gui/BangBean2.java
// Trebalo bi da svoja zrna ovako pišete,
// kako bi mogla da se izvršavaju u okruženju s više niti.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import net.mindview.uti1 .SwingKonzola.*;
}
public synchronized Color getTextColor() { return tColor;}
public synchronized void setTextColor(Color newColor) {
tColor = newColor;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.drawOval(xm - cSize/2, ym - cSize/2, cSize, cSize);
}
// Ovo zrno ima više prijemnika što se češče koristi
// od obaveštavanja samo jednog prijemnika,
// kako je bilo urađeno u programu BangBean.java:
public synchronized void
addActionListener(ActionListener 1) {
actionListeners.add(l);
}
public synchronized void
removeActionListener(ActionListener 1) {
actionLi steners.remove(l);
}
// Pazite, ovo nije sinhronizovano:
public void notifyListeners() {
ActionEvent a = new ActionEvent(BangBean2.this,
ActionEvent.ACTION_PERFORMED, null);
ArrayLi st 1v = nul 1 ;
// Napravi površnu kopiju spiska za slučaj
// da neko doda jedan prijemnik tokom
// pozivanja drugih prijemnika:
synchronized(this) {
lv = new ArrayList<ActionListener>(actionListeners);
}
// Obavesti sve metode prijemnike:
for(ActionListener al : 1v)
a l .actionPerformed(a);
}
class ML extends MouseAdapter {
public void mousePressed(MouseEvent e) {
Graphics g = getGraphics();
g.setColor(tColor);
g.setFont(
new FontC'TimesRoman", Font.BOLD, fontSize));
int width = g.getFontMetricsO .stringWidth(text);
g.drđwString(text, (getSize().width - width) / 2,
getSizeO .height/2);
g.disposeO;
noti fyListeners();
1134 Misliti na Javi
}
class MM extends MouseMotionAdapter {
public void mouseMoved(MouseEvent e) {
xm = e.getX();
ym = e.getY();
repaint();
}
}
public static void main(String[] args) {
BangBean2 bb2 = new BangBean2();
bb2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
System.out.println("ActionEvent'' + e ) ;
}
} );
bb2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
System.out.println("BangBean2 action");
}
});
bb2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
System.out.println("More action");
}
});
JFrame frame = new JFrameO;
frame.add(bb2);
run(bb, 300, 300);
}
} ///:-
Dodavanje rezervisane reči synchronized m etodam a jeste mala i laka izm ena. M eđu-
tim , uočite da se u m etodam a addActionListener( ) i rem oveActionListener( ) prijem-
nici sada dodaju na listu i uklanjaju s nje kako bi zrno m oglo da ima više prijemnika.
Vidi se da m etoda notifyL isteners( ) nije sinhronizovana. Nju m ože istovrem eno da
pozove više niti. M oguće je i da m etode addA ctionListener( ) i rem oveActionListener( )
budu pozvane usred poziva m etode notifyListeners( ), što predstavlja problem jer ta m e-
toda prolazi kroz listu prijemnika. Da bi se taj problem ublažio, spisak je unutar sinhro-
nizovanog dela koda kloniran p om oću konstruktora spiska prijem nika ArrayList koji
kopira elem ente svog argumenta, pa se prolazi kroz klon. Tako se m ože raditi sa original-
nim spiskom prijemnika, a da se pri tom ne utiče na tnetodu notifyListeners( ).
N i m etoda paintComponent( ) nije sinhronizovana. Odiučiti da li treba sinhro-
nizovati redefm isane m etode nije tako lako kao kad se sam o dodaju sopstvene m etode.
o\'oni prim eru ispostavlja se da m etoda p ain tC o m p o n en t( ) naizgled dobro radi, bila
sinhronizovana ili ne. Ali m orate uzeti u obzir i sledeća pitanja:
Poglavlje 22: Grafička korisnička okruženja 1135
T. D a li ta m etoda menja stanje kritičnih prom enljivih unutar objekta? D a biste otkrili
koje su prom enljive kritične, m orate utvrditi da li će ih druge niti u program u čitati
ili im zadavati vrednosti. (U ovom slučaju, čitanje i zadavanje gotovo se uvek oba-
vlja unutar sinhronizovanih m etoda, pa m ožete da ispitate sam o njih.) M etoda
paintCom ponentO nem a takvih izm ena stanja.
2 . Da li m etoda zavisi od stanja tih kritičnih promenljivih? Ako neka sinhronizovana
m etoda m odifikuje prom enljivu koju vaša m etoda koristi, trebalo bi da i nju
sinhronizujete. Na osnovu toga m ogli biste uočiti da prom enljivu cSize m odifikuju
sinhronizovane m etode, pa bi stoga m etodu paintC om ponent( ) trebalo sinhroni-
zovati. U ovom slučaju, m eđutim , m ožete da se zapitate: „Šta se najgore m ože d o-
goditi ako se sadržaj prom enljive cSize prom eni tokom izvršavanja m etode
p aintC om p onent( )?“ Ako zaključite da to što se m ože desiti nije tako strašno, a uz
to je i prolazno, m ožete da ostavite m etodu p aintC om ponent( ) nesinhronizovanu
kako biste izbegli dodatne režijske troškove koje izaziva pozivanje sinhronizovane
m etode.
3. Da li je verzija m etode u osnovnoj klasi sinhronizovana? M etoda paintC om po-
nent( ) nije sinhronizovana u osnovnoj klasi. To nije baš besprekoran argum ent
nego sam o povod za razmišljanje. U ovom slučaju, recim o, postoji jedno polje (cSi-
ze) koje m odifikuje sinhronizovana m etoda, a njem u pristupa m etoda paintCom-
p o n e n t( ), pa je to m oglo đa izm eni situaciju. Imajte u vidu, m eđutim , da se atribut
synchronized ne nasleđuje - znači, ukoliko je neka m etoda sinhronizovana u os-
novnoj klasi, ona nepostajc autom atski sinhronizovana u redefinisanoj verziji izve-
dene klase.
4. M etode paint( ) i paintComponent( ) moraju biti najbrže m oguće. Izuzetno se
preporučuje korišćenje svega što iz njih uklanja režijske troškove, pa ako smatrate
da te m etode treba da sinhronizujete, to m ože biti znak lošeg projekta.
Kod za testiranje unutar m etode m a in ( ) drugačiji je nego onaj u program u BangBe-
anTest, kako bi se pokazala sposobnost klase BangBean2 da o dogadajim a obaveštava
više prijemnika.
Pakovanje zrna
Pre nego što se zrno uveze u vizuelnu alatku, mora se staviti u standardan paket za zrna,
a to je JAR arhiva koja sadrži sve klase zrna i m anifest datoteku. M anifest datoteka je tek-
stualna datoteka određenog oblika. Za zrno BangBean, ta datoteka izgleda ovako:
Manifest-Version: 1.0
Name: bangbean/BangBean.class
Java-Bean: True
Prvi red prikazuje broj verzije manifest datoteke koji, do daljeg obaveštenja iz kom pa-
nije Sun, iznosi 1.0. Drugi red (prazni redovi se zanemaruju) navodi datoteku Bang-
Bean.class, a treći kaže ,,To je zrno“. Bez trećeg reda, alatka za pravljenje programa ne bi
prepoznavala klasu kao zrno.
1136 Misliti na Javi
O ovom e uglavnom ne m orate da vodite računa, a ako nešto menjate, m ožete sam o da
prom enite prvobitnu manifest datoteku i da p on ovo pozovete alatku jar da biste napra-
vili novu arhivu. U JAR arhivu m ožete da dodate i nova zrna tako što ćete informacije o
njima staviti u m anifest datoteku.
D obro je da se svako zrno stavi u sopstveni poddirektorijum , jer kada pravite arhivu
m ožete da arhivirate ceo direktorijum. Vidi se da i Frog i BangBean imaju sopstvene pod-
direktorijume.
Kada pravilno sm estite zrno u arhivu, m ožete da ga unesete u alatku za izradu progra-
ma koja podržava zrna. Način na koji se to radi razlikuje se od alatke do alatke, ali kom-
panija Sun obezbeđuje besplatno okruženje za testiranje Javinih zrna koje se zove Bean
Builder. (M ožete ga preuzeti s lokacije h ttp ://ia va .iu n .co m /b ea n s.) Da biste sm estili zrno
u Bean Builder, kopirajte njegovu JAR datoteku u odgovarajući poddirektorijum .
Vežba 36: (4) D odajte datoteku Frog.class u manifest datoteku kao što je prikazano u
ovom poglavlju i pokrenite program jar da biste napravili JAR arhivu koja sadrži zrna
Frog i BangBean. Zatim sa Interneta preuzm ite i instalirajte Bean Builder ili upotrebite
svoju alatku za pravljenje programa koja podržava zrna. Dodajte novu arhivu u to okru-
ženje kako biste m ogli da testirate ta dva zrna.
Poglav[je 22: Grafička korisnička okruženja 1137
Više o zrnima
M nogi su pisali o zrnim a Jave; na primer, Flliote Rusty Harold autor je knjige JavaBeans
(IDG , 1998).
Alternative za Svving
Iako je biblioteka Swing GKO alatka koju preporučuje Sun, to nikako ne znači da ne
postoie i d ru g e alatke za pravljenje gralićkih korisnićkih okruženja. Dve važne alternative
su Flash kom panije A dobe, u kojem se za pravljenje klijentskih GKO preko Weba koristi
Flashov sistem za programiranje Flex, i biblioteka otvorenog izvornog koda Eclipse Stan-
dard W idget Toolkit (SW T) za aplikacije nam enjene stonim računarima.
1 138 Misliti na Javi
Zašto biste koristili alternative? Za Web klijente se prilično uverljivo m ože tvrditi da
apleti nisu uspeli. Iako su postojali od početka i m ada se o njim a toliko pričalo i obećava-
lo, danas je iznenađenje kada čovek naiđe na Web aplikaciju koja koristi aplete. Čak ni
Sun ne koristi aplete svugde. Evo primera:
http://java.sun.com /developer/onlineT raining/new 2java/javam ap/intro.htm l
Iako je interaktivna mapa Javinih m ogu ćn osti na Sunovoj lokaciji veom a dobar kan-
didat za Java aplet, oni su je napravili u Flashu. Izgleda da je to prećutno priznanje da
apleti nisu uspeli. Još je važnije to što je Flash Player instaliran na preko 98 procenata
računarskih platformi, pa se m ože sm atrati prihvaćenim standardom . Kao što ćete videti,
sistem Flex obezbeđuje veom a m o ćn o okruženje za programiranje klijentskih aplikacija,
svakako m nogo jače od JavaScripta, a njegov izgled i ponašanje često su bolji od apleta.
U koliko ipak želite da koristite aplete, ostaje vam da ubedite klijenta da s Weba preuzme
JRE koji je m nogo veći i duže se preuzim a od Flash Playera.
Za stone aplikacije, jedan od problem a sa Sw ingom je to što korisnici opaze da koriste
drugu vrstu aplikacije, pošto se izgled i ponašanje Sw ing aplikacija razlikuje od onih uo-
bičajenih na stonim računarima. Korisnici po pravilu ne žele da svaka aplikacija drugačije
izgleda i ponaša se; oni se trude da što brže završe svoj posao i trude se da sve aplikacije
izgledaju i ponašaju se jednako. SWT pravi aplikacije koje izgledaju kao m atične, a pošto
ta biblioteka koristi m atične kom ponente koliko god je m oguće, te aplikacije se obično
izvršavaju brže od ekvivalentnih Swing aplikacija.
Zdravo, Flex
Pročitajte ovaj MXML kod, koji definiše korisničko okruženje (imajte u vidu da prvog i
poslednjeg reda nema u kodu koji preuzim ate u paketu izvornog koda ove knjige):
giii/flex/zdravoflexl.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xml n s :mx="http://www.mac romedi a .com/2003/mxml1111
backgroundCol or="#ffffff">
<mx:Natpis id="output" tekst="Zdravo, Flex!" />
</mx:Applicati on>
III--
M XML datoteke su XML dokum enti, te stoga počinju op isom XML verzije i kodi-
ranja; to su atributi version i encoding. Krajnji spoljašnji MXML elem ent je Application
koji se vizuelno nalazi na vrhu i logički je kontejner za Flex korisničko okruženje. Unutar
elem enta Application rnožete đeklarisati oznake koje predstavljaju vizuelne kontrole kao
što je gornji elem ent Natpis. Kontrole uvek sm eštam o u kontejnere, a kontejner pored
ostalih m ehanizam a kapsulira i rasporedivač (engl. layout tnanager) koji upravlja raspo-
redom kontrola u kontejneru. U najjednostavnijem slučaju, kao u gornjem prim eru, Ap-
plication deluje kao kontejner. Podrazum evani rasporedivač elem enta Application
smešta kontrole vertikalno niz prozor, redosledom kojim su bile deklarisane.
ActionScript je verzija ECMAScripta, ili JavaScripta, koja p otp u n o liči na Javu i p o-
država klase i strogu proveru tipova, kao i dinam ičko pravljenje skriptova. Kada prim eru
dodam o skript, uvešćem o ponašanje. Ovde MXML kontrola Script sm ešta ActionScript
neposredno u MXML datoteku:
//:! gui/flex/zdravoflex2.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.macromedia.com/2003/mxml"
backgroundColor="#ffffff">
< m x :Script>
< ! [CDATA[
function update0utput() {
output.tekst = "Zdravo! " + input.tekst;
}
]]>
</mx:Script>
< m x:TextInput id="input" width="200"
change="updateOutput()" />
<mx:Natpis id="output" tekst="Zdravo!" />
</mx:Appli cati on>
///: -
11 N aveđena adresa više nije do stu p n a . Više inform acija o M X M L-u naći ćete na adresi w\vw\adobe.cotn/
licvnc t/flex/n ixrnl_as.htm l
1140 Misliti na Javi
Textlnput kontrola prima korisnikov unos, a Natpis (već tokom unosa) prikazuje une-
sene podatke. Vodite računa o tom e da je atribut id svake kontrole u skriptu dostupan kao
im e promenljive, pa skript m ože da referencira instance M XM L oznaka. U polju Text-
Input vidite da je atribut change povezan s funkcijom updateO utput( ) - dakle, ta funk-
cija se poziva kad god se desi bilo kakva prom ena (engl. change).
Prevođenje MXML-a
Najbolje je da sa adrese w w w .adobe.com /products/flex/trial preuzm ete besplatnu probnu
verziju Flexa.12 Taj proizvod postoji u više izdanja - od besplatnih probnih do serverskih
za velika preduzeća — a za razvoj Flex aplikacija A dobe nudi još neke alatke. Pošto se
izdanja stalno menjaju, pročitajte pojedinosti na Web lokaciji proizvođača Adobe.
Takođe, imajte u vidu da ćete m ožda m orati da m odifikujete datoteku jvm.config u
direktorijum u bin Flexove instalacije.
Za prevođenje MXML koda u Flashov bajtkod im ate dve opcije:
1. M XML datoteku m ožete sm estiti u neku Java Web aplikaciju, pored JSP i HTML
stranica u WAR datoteci, i dati da se u vrem e izvršavanja zahtevi za .m x m l datote-
kom prevode kad god bilo koji čitač zatraži URL adresu tog MXML dokum enta.
2 . MXML datoteku m ožete prevesti p om o ću Flexovog prevodioca m xm lc koji se po-
kreće s kom andne linije.
Prva opcija, prevođenje na W ebu u vrem e izvršavanja, sem Flexa zahteva i neki konte-
jner servleta (kao što je A pacheovT om cat). WAR datotekekontejnera servleta moraju biti
ažurirane p o m o ću informacija iz Flexove konfiguracije, kao što su mapiranja servleta do-
data deskriptoru w eb.xm l, i m oraju obuhvatati Flexove JAR datoteke - sve se to obavlja
autom atski kada instalirate Flex. Nakon konfigurisanja WAR datoteke, MXML datoteke
m ožete sm estiti u Web aplikaciju i zatražiti URL adresu tog dokum enta bilo kojim či-
tačem . Flex će prevesti aplikaciju nakon prvog takvog zahteva, slično kao u m odelu Javi-
nih serverskih stranica (JSP), i p otom će isporučivati prevedeni i keširani SWF unutar
HTM L ljuske.
U drugoj opciji server nije potreban. SWF datoteke pravite pozivom Flexovog prevo-
dioca m x m lc na kom andnoj liniji. Njih m ožete upotrebiti kako god želite. Izvršna dato-
teka m xm lc je u direktorijum u b in FIexove instalacije, i kada je pozovete bez argumenata,
ispisaće spisak validnih opcija u kom andnoj liniji. O bično se lokacija Flexove biblioteke
klijentskih kom ponenata zadaje kao vrednost opcije -flexlib, ali u veom a jednostavnim
slučajevim a kao što su dva prethodna, Flexov prevodilac će pretpostaviti gde je biblioteka
kom ponenata. Zato prva dva prim era m ožete prevesti ovako:
mxmlc.exe zdravoflexl.mxml
mxmlc.exe zdravoflex2.mxml
Tim e se proizvodi datoteka z d rav oflex 2 .sw f koja se m ože pokrenuti u Flashu ili sm e-
stiti uz HTM L na neki HTTP server. (N akon učitavanja Flasha u čitač Weba, često je do-
voljno dvaput pritisnuti SWF datoteku da biste je pokrenuli u čitaču.)
U složenijim aplikacijama m ožete razdvojiti MXML i A ctionScript tako što ćete funk-
cije referencirati u spoljnim ActionScript datotekama. U M XM L-u upotrebite sledeću
sintaksu za kontrolu Script:
MXML i ActionScript
MXML je deklarativna stenografija za ActionScript klase. Za svaku MXML oznaku posto-
ji istoim ena ActionScript klasa. Kada Flexov prevodilac leksićki analizira (raščlanjuje i
razrešava) MXML, prvo transform iše XML u ActionScript i učitava referencirane Ac~
tionScript klase, a potom prevodi i povezuje taj ActionScript u SWF datoteku.
Celu Flex aplikaciju m ožete napisati u sam om A ctionScriptu, a da uopšte ne u po-
trebite MXML. Dakle, MXML nije neophodan, ali je pogodan. MXML se ob ičn o upotre-
bljava za deklarisanje kom ponenata korisničkog okruženja kao što su kontejneri i
kontrole, dok se ActionScript i Java koriste za obradu događaja i ostalu klijentsku logiku.
Imate m ogućnost da pravite sopstvene MXML kontrole i da ih referencirate u Ac-
tionScript klasama. Postojeće MXML kontejnere i kontrole m ožete da kom binujete u no-
vom MXML dokum entu koji m ože biti referenciran kao oznaka u drugim MXML
dokum entim a. Više informacija o tom e potražite na Web lokaciji kom panije Adobe.
Kontejneri i kontrole
V izuelno jezgro biblioteke Flex kom ponenata je skup kontejnera koji upravljaju raspore-
dom i niz kontrola koje idu 11 te kontejnere. M eđu kontejnerim a su panoi, vertikalni i ho-
rizontalni pravougaonici, pravougaonici koji se slažu kao pločice, pravougaonici koji se
šire poput harm onike, pravougaonici sa unutrašnjom m režom , tabele itd. Kontrole su
spravice (engl. widgets) korisničkog okruženja kao što su dugm ad, tekstualna polja, kli-
zaći, kalenđari, tabele podataka itd.
Sada ćem o napraviti Flex aplikaciju koja prikazuje i uređuje listu audio datoteka. Vi-
d e ć e t a p r i m e r e kontejnera, kontrola i otkrićete kako đa ih iz Flasha povežete s Javom.
Na početak MXML datoteke postavićem o kontrolu DataGrid (koja spada u sofistici-
ranije Flex kontrole) u kontejner tipa Panel:
//:! gui/f1ex/pesme.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
1142 Misliti na Javi
xmlns:mx="http://www.macromedi a .com/2003/mxml"
backgroundColor="#B9CAD2" pageTitle="Flex program za rukovanje pesmama"
initialize="dajPesme()">
<mx:Script source="skriptPesama.as" />
<mx:Style source="sti1ovi Pesama.css"/>
<mx:Panel id="panoListePesama"
ti tleStyleDeclarati on="headerText"
title="Flex MP3 biblioteka">
< m x :HBox verticalA1i gn="bottom">
<mx:DataGrid id="tabelaPesama"
c e n P r e s s = “izaberiPesmu(dogadjaj)" rowCount="8">
<mx:columns>
<mx:Array>
<mx:DataGri dColumn columnName="naslov"
headerText="Naslov pesme" width="120" />
< mx :DataGri dColumn columnName="i zvodjač"
headerText="Izvodjač" width="180" />
<mx:DataGridColumn columnName="album"
headerText="Album" width="160" />
</mx:Array>
</mx:columns>
</mx:DataGrid>
<mx:VBox>
<mx:HBox height="100" >
<mx:Slika id="sl ikaAlbuma'' source=""
height="80" width="100"
mouseOverEffect="promVelPovecaj"
mouseOutEffect="promVelSmanji" />
<mx:TextArea id="podaciOPesmi"
styleName="boldText" height="100%" width="120"
vScrol1Policy="off" borderStyle="none" />
</mx:HBox>
<mx:MediaPlayback id="plejerZaPesme"
contentPath=""
mediaType="MP3"
height="70"
width="230"
control 1erPolicy="on"
autoPlay="false"
visible="false" />
< / mx:VBox>
</mx:HBox>
<mx:ControlBar horizontalAlign="right">
<mx:Button id="dugme0svezi Pesme"
1 abel ="0sveži pesme" w M t h = "100"
toolTip=" Osveži listu pesama"
cl ick="uslugaPesama.dajPesmeO" />
</mx:ControlBar>
Poglavjje 22: Grafička korisnička okruženja 1143
</mx:Panel>
<mx:Effect>
<mx:PromVel name="promVelPovecaj" heightTo="100"
duration="500"/>
<mx:PromVel name="promVelSmanji" heightTo="80"
duration="500"/>
</mx:Effect>
<mx:RemoteObject id=''uslugaPesama"
source="gui.f1ex.UslugaPesama"
rezul tat="onPesme(dogadjaj.rezultat)"
fault="alert(dogadjaj.fault.faultstring, 'Greška')">
<mx:method name="dajPesme"/>
</mx:RemoteObject>
</mx:Application>
///:-
Kontejner tipa DataGrid sadrži ugnežđene oznake za svoj niz kolona. Svaki atribut ili
ugnežđeni elem ent na kontroli odgovara nekom svojstvu, dogadaju ili kapsuliranom ob-
jektu pripadne ActionScript klase. Taj DataGrid ima atribut id čija je vrednost tabelaPe-
sama, pa ActionScript i MXML oznake m ogu programski da referenciraju tu tabelu
koristeći tabelaPesama kao im e prom enljive. DataGrid eksponira m n ogo više svojstava
nego što sam ja ovde pokazao; celokupni API za MXML kontrole i kontejnere m ožete naći
na adresi http://livcdocs.adobe.eom /flex/l/asdocs.
Iza kontejnera DataGrid sledi VBox sa elem entom Slika koji pokazuje prednju stranu
album a i podatke o pesm i, kao i s kontrolom MediaPlayback za reprodukciju MP3 dato-
teka. U ovom primeru sadržaj se em ituje u realnom vrem enu, da bi se smanjila prevedena
SWF datoteka. Ukoiiko slike, audio i video datoteke ugradite u Flex aplikaciju um esto da
ih em itujete u realnom vrem enu, te datoteke postaju deo prevedenog SWF-a i isporučuju
se zajedno s korisničkim okruženjem; u tom slučaju, u vrem e izvršavanja nem a em ito-
vanja u realnom vrem enu na zahtev.
Flash plejer sadrži ugrađene kodeke za reprodukciju i em itovanje audio i video dato-
teka raznih formata u realnom vrem enu. Flash i FIex podržavaju najkorišćenije form ate
slika na Webu, a Flex um e da konvertuje datoteke skalabilne vektorske grafike (SVG) u
SWF resurse koji se m ogu ugraditi u Flex klijentc.
Efekti i stilovi
Flash plejer iscrtava grafiku vektorski, pa u vrem e izvršavanja m ože da obavi veom a iz-
ražajne transformacije. Flexovi efekti om ogućuju da steknem o uvid u takve animacije.
Efekti su transformacije koje p om oću MXML sintakse m ožete prim eniti na kontrole i
kontejnere.
Oznaka Ettect prikazana u prethodnom MXML kodu proizvodi dva rezultata: prva
ugneždena oznaka dinam ički povećava sliku kada se miš nalazi iznad nje, a druga je di-
nam ički sm anjuje kada miš ode s nje. Ti efekti se prim enjuju na dogadaje miša dostupne
za kontrolu Slika nazvanu slikaAlbuma.
1144 Misliti na Javi
Flex im a efekte i za uobičajene anim acije kao što su prelazi, prebrisavanja i m odulira-
jući alfa kanali. Pored ugrađenih efekata, Flex podržava i Flashov API za crtanje zaista in-
ovativnih animacija. Dublje istraživanje ove tem e obuhvata grafički dizajn i anim aciju, i
prevazilazi opseg ovog odeljka.
Flex podržava kaskadne opise stilova (Cascading Style Sheets, CSS), p a znači i stan-
dardne stilove. Ako MXML datoteci p ridružite CSS datoteku, Fiex kontrole će se pridrža-
vati tih stilova. U ovom prim eru, kaskadni opis stilova stiloviPesam a.css sadrži sledeću
CSS deklaraciju:
//:! gui/flex/stiloviPesama.css
.headerText {
font-family: Arial, "_sans";
font-size: 16;
font-weight: bold;
}
,boldText {
font-family: Arial, "_sans";
font-size: 11;
font-weight: bold;
}
III--
Aplikacija biblioteke pesama uvozi tu datoteku i upotrebljava je preko oznake Style u
MXML datoteci. Nakon uvoza opisa stilova, njegove deklaracije se m ogu prim eniti na
Flex kontrole u MXML datoteci. Na prim er, deklaraciju boldText iz opisa stilova upotre-
bljava kontrola TextArea čiji je identifikator (id) podaciOPesm i.
Događaji
Korisničko okruženje je mašina stanja; ono obavlja radnje kada se m enja stanje. U Flexu
se te prom ene prijavljuju pom oću događaja. Flexova biblioteka klasa sadrži m noštvo na-
jrazličitijih kontrola koje podržavaju m noštvo događaja za sve aspekte kretanja miša i ko-
rišćenja tastature.
Prim era radi, atribut click elem enta Button predstavlja jedan od događaja dostupnih
za tu kontrolu. Vrednost dodeljena atrib u tu click m ože biti neka funkcija ili um etnut
skript. Prim era radi, u gornjoj MXML datoteci, elem ent ControlBar sadrži dugm e-
OsveziPesme za osvežavanje liste pesama. Iz oznake vidite da događaj click izaziva poziv
m etode uslugaPesam a.dajPesm e(). U tom prim eru, događaj click dugm eta Button re-
ferencira RemoteObject (udaljeni objekat) koji odgovara toj Java metodi.
Povezivanje s Javom
Oznaka RemoteObject na kraju MXML datoteke uspostavlja vezu sa spoljnom Java kla-
som, gui.flex.UslugaPesama. Pom enuti Flex klijent - m etodom dajPesm e() te Java klase -
pribavlja podatke za DataGrid. Da bi to m ogao da radi, m ora izgledati kao usluga -k ra jn ja
Poglavlje 22: Grafička korisnička okruženja 1145
tačka s kojom klijent može da razmenjuje poruke. Usluga definisana u oznaci RemoteOb-
ject im a atribut source koji naznačuje Java klasu oznake RemoteObject, i specificira Ac-
tionScript povratnu funkciju, on P esm e(), koja če biti pozvana kada se Java m etoda vrati.
U gnežđena oznaka m ethod deklariše m etodu dajPesm e(), što tu Java m etodu čini dostup-
n om ostatku Flex aplikacije.
U Flexu se svi pozivi usluga vračaju asinhrono, preko događaja koji p rouzrokuju (iza-
zivaju) te povratne funkcije. U slučaju greške, isti RemoteObject prikazuje dijalog
upozorenja.
M etodu dajPesm e() sada m ožem o pozvati iz Flasha pom oću ActionScripta:
uslugaPesama.dajPesme();
Zbog konfiguracije MXML-a tim e će biti pozvana m etoda dajPesm e() u klasi Usluga-
Pesama:
//: gui/flex/UslugaPesama.java
package gui,flex;
import java.uti1.*;
//: gui/flex/Pesma.java
package gui.flex;
//: gui/flex/skriptPesama.as
function dajPesme() {
uslugaPesama.dajPesmeO;
}
function izaberiPesmu(dogadjaj) {
var pesma = tabelaPesama.getItemAt(dogadjaj.itemIndex);
pri kazi podatkeOPesmi(pesma);
}
function prikazipodatkeOPesmi(pesma) {
po da c i O P e s m i .tekst = pesma.naslov + nevvline;
podaciOPesmi.tekst += pesma.izvodjac + newline;
podaciOPesmi.tekst += pesma.album + newline;
slikaAlbuma.source = pesma.urlS1ikeNaAlbumu;
Poglavlje 22: Grafička korisnička okruženja 1147
plejerZaPesme.contentPath = pesma.urlNosacaZvuka;
plejerZaPesme.visible = true;
}
function onPesme(pesme) {
tabelaPesama.dataProvider = pesme;
) ///:-
Da bism o obradili izbor D ataG rid ćelija, atrib u t događaja cellPress dodajem o dekla-
raciji elem enta D ataG rid u MXML datoteci:
<mx:Slider id="mojKlizac"/>
<mx:Text tekst="{moj K1 i z a c . v a l ue}"/>
Podatke povezujete kada smestite reference u vitičaste zagrade. Sve u n u tar tih vitiča-
stih zagrada Flex sm atra izrazom koji treba izračunati.
Vrednost prve kontrole, elem enta Slider (klizača), prikazuje druga kontrola - jedno
tekstualno polje. S prom enom vrednosti klizača, svojstvo tekst tekstualnog polja auto-
matski se ažurira. Na taj način, program er ne m ora da obrađuje događaje prom ena vred-
nosti elem enta Slider da bi ažurirao tekstualno polje.
Ima i sofisticiranijih kontrola, kao što su Tree (stablo) i DataGrid u aplikaciji biblio-
teke pesama. Te kontrole imaju svojstvo dataprovider koje olakšava povezivanje s kolek-
cijama podataka. A ctionScript funkcija on P esm e() prikazuje kako je m etoda
UslugaPesam a.dajPesm ef) povezana sa svojstvom dataprovider Flexovog elementa Da-
taG rid. l\ao što je deklarisano u oznaci RemoteObject MXML datoteke, tu povratnu
funkciju poziva A ctionScript kada se Java m etoda vrati.
Sofisticiranija prim ena složenijeg m odela podataka, kao što je in tran et aplikacija koja
upotrebljava objekte za prenos podataka (Data Transfer Objects) ili dostavljače poruka
1148 Misliti na Javi
čiji su podaci usklađeni sa složenim šem am a, m ože p o d sta đ dalje razdvajanje izvora
podataka od kontrola. U razvoju Flex program a, to razdvajanje postižem o deklarisanjem
objekta ,,modela“, što je generički MXML kontejner podataka. Taj model ne sadrži logiku.
O n je ekvivalentan objektu za prenos podataka kakav se sreće p ri razvoju intranet pro-
gram a i odgovarajućim strukturam a u ostalim program skim jezicima. Po tom m odelu,
povezujemo podatke kontrola s m odelom i istovrem eno m odel povezuje svoja svojstva sa
ulazim a i izlazima usluga. Tim e se izvori podataka, usluge, razdvajaju od vizuelnih po-
trošaća podataka, čim e se olakšava korišćenje obrasca model-prikaz-kontroler (Model-
View-Controller, MVC). U većim, sofisticiranijim aplikacijama, početno povećanje slože-
nosti zbog um etanja m odela često se isplati je r se dobija MVC aplikacija s jasno razdvo-
jenim podacim a od kontrola.
Sem Java objekata, Flex pom oću kontrola WebService odnosno HttpService um e da
pristupa i Web uslugam a čiju osnovu čini SOAP - tj. RESTful H TTP uslugama. Pri-
stupanje svim uslugam a podleže bezbednosnim ograničenjim a provere identiteta.
Izgradnja i primena
U prethodnim prim erim a mogli sm o da se provučem o bez indikatora -flexlib na kom an-
dnoj liniji, ali da bism o preveli ovaj program , indikatorom -flexlib m oram o specificirati
mesto datoteke flex-config.xml. Sledeća kom anda je prikladna za m oju instalaciju, a vi
ćete m orati da je m odifikujete u skladu sa sopstvenom konfiguracijom. (Kom anda se piše
u jednom redu koji se zbog dužine nastavlja i u narednom ):
//:! gui/flex/bui1d-command.txt
mxmlc -flexlib C:/"Program Files"/Adobe/Flex/jrun4/servers/default/flex/
WEB-INF/flex pesme.mxml
///■■-
Ta će kom anda izgraditi aplikaciju u obliku SWF datoteke koju možete prikazati u či-
taču Weba, ali pošto u datoteci za đistribuciju koda ove knjige nem a ni MP3 datoteka niti
JPG slika, kada pokrenete aplikaciju nećete čuti nijednu pesm u niti ćete videti om ote al-
bum a, nego sam o osnovnu stru k tu ru (engl. fram eivork).
Pored toga, m orate konfigurisati neki server da biste iz Flex aplikacije mogli da kom u-
nicirate s Java datotekam a. Flexov probni paket obuhvata i server JRun, a njega nakon
instalacije Flexa m ožete pokrenuti bilo koristeći menije operativnog sistema, bilo s ko-
m andne linije:
Proverite da li je server uspešno p o k ren ut tako što ćete u čitaču Weba otvoriti http://lo-
calhost:8700/samples i p r ik n z a ti r n z n e u z o r k e (engl. samplcs). To ie i d o b n r nnčin za u p o -
znavanje m ogućnosti Flexa.
Umesto da aplikaciju prevodite na kom andnoj liniji, možete da je prevedete posredst-
vom servera. To se radi tako što izvorne datoteke pesama, CSS opise stilova itd. smestite
u direktorijum jrun4/servers/default/flex i pristupite im u čitaču Weba otvaranjem adre-
se http://localhost:8700/flex/pesm e.m xm l.
Poglavlje 22: Grafička korisnička okruženja 1 149
Instaliranje SWT-a
Za SWT aplikacije potrebno je da preuzm ete i instalirate SWT biblioteku projekta Eclip-
se. Izaberite neki od preslikanih servera na lokaciji www.eclipse.org/dow nloads/. Sledite
veze do tekućeg builda Eclipsea i pronađite kom prim ovanu datoteku ćije ime počinje sa
swt i sadrži ime vaše platform e (na prim er, win32). U toj datoteci naći ćete swt.jar. Da-
toteku swt.jar najlakše ćete instalirati ukoliko je smestite u direktorijum jre/lib/ext (jer u
tom slučaju nećete m orati da m enjate svoju p u tan ju klasa). Kada dekom prim ujete biblio-
teku SWT, m ožda ćete naći još datoteka koje treba instalirati na m estim a odgovarajućim
za vašu platform u. Na prim er, W in32 distribucija obuhvata i DLL datoteke koje treba
smestiti negde u java.Iibrary.path. (O bično je ta putanja jednaka onoj koju opisuje si-
stemska prom enljiva PATH, ali stvarnu vrednost java.Iibrary.path možete saznati po-
m oću program om object/ShowProperties.java). Kada to uradite, trebalo bi da možete
transparentno da prevodite i pokrećete SWT aplikacije, kao sve druge Java programe.
D okum entacija za SWT preuzim a se zasebno.
D ruga m ogućnost je da instalirate program za uređivanje teksta Eclipse; on obuhvata
i SWT i SWT dokum entaciju koju m ožete prikazati pom oću Eclipseovog sistema pomoći.
Zdravo, SW T
Počnim o najjednostavnijom m ogućom aplikacijom u stilu „zdravo sviina":
//: swt/ZdravoSWT.java
// {Requires: o r g . e c l i ps e. sw t. wi dge ts.D i s p l a y ; Morate
// instalirati SWT biblioteku s lokacije http://www.eclipse.org }
import org.eclipse.swt.widgets.*;
//: swt/ljuskeSuGlavniProzori.java
import org.eclipse.swt.widgets.*;
Kada pokrenete ovaj program , dobićete deset glavnih prozora. Zbog načina na koji je
program napisan, ako zatvorite bilo koji od tih prozora, zatvorićete i sve ostale.
I SWT koristi raspoređivače - različite od onih u Svvingu, ali iste u principu. U nared-
nom neznatno složenijem prim eru dodaćem o ljusci tekst rezultata m etode
S y stem .g e tP ro p ertie s():
//: swt/SvojstvaKlaseDisplay.java
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import java.io.*;
1juska.setLayout(new FillLayout());
Text tekst = new Text(ljuska, SWT.WRAP | SWT.V_SCROLL);
StringWriter svojstva = new StringWriter();
System.getProperti e s ().1i st(new Pri ntWri ter(svojstva));
tekst.setText(svojstva. t o S t r i n g O ) ;
1juska.open();
wh i1e (!1juska.isDisposed())
i f (!prikaz.readAndDi spatch())
p r i k az. s lee pO ;
prikaz.disposeO;
}
} III--
U SW T-u svi vidžeti m oraju im ati roditeljski objekat opšteg tipa C o m p o site koji kon-
struktoru vidžeta m orate proslediti kao prvi argum ent. To vidite u k o n struktoru klase
Text kojem je prvi argum ent ljuska. Gotovo svi konstruktori prim aju i indikatorski argu-
m ent pom oću kojega možete zadati proizvoljan broj direktiva u vezi sa stilom , u zavisno-
sti od toga šta vidžet zahteva. Sve direktive u vezi sa stilom podvrgavaju se logičkoj
disjunkciji po bitovim a (operator I), kao u preth od no m prim eru.
Prilikom pravljenja T e x t() objekta dodao sam indikatore stila koji prelam aju tekst
(engl. w rap) i autom atski m u dodaju vertikalnu traku za pom eranje (engl. scroll bar), ako
treba. Videćete da se u SWT-u m nogo toga radi pom oću konstruktora; m noge atribute
vidžeta teško je iii nem oguće menjati sem pom oću konstruktora. U dokum entaciji vidže-
tovog konstruktora uvek m orate proveriti koji su dozvoljeni indikatori. Neki konstrukto-
ri zahtevaju indikatorski argum ent čak i kada u dokum entaciji nem aju nijedan
„dozvoljen" indikator. Time se om ogućuje buduće proširenje, a da se interfejs ne menja.
Izbegavanje redundantnosti
Pre nego što nastavimo, navešćemo šta m orate da uradite u svakoj SWT aplikaciji.
U SWT-u uvek m orate da napravite prikaz tj. objekat klase Display, od tog prikaza na-
pravite ljusku tj. objekat kiase Shell, napišete petlju rea d A n d D isp a tc h () itd. Naravno, u
nekim posebnim slučajevima to ne m orate da radite, ali o n i su toliko retki da se isplati
sabrati sav taj kod u jedan program i izbeći ponavljanje, kao što sm o za Swing uradili u
program u net.mindvievv.util.SvvingKonzoIa.
N ateraćem o svaku aplikaciju da se usaglasi sa interfejsom :
//: swt/util/SWTAplikacija.java
package swt.uti1 ;
import org.eclipse.swt.widgets.*;
Aplikaciji se predaje objekat tipa C om posite (čija je Shell potklasa), pom oću kojeg ap-
likađja m etodom createC ontents() m ora da napravi sav svoj sadržaj. U odgovarajućem
trenutku, SW TKonzola.run() poziva m etodu createC ontents(), zadaje veličinu Ijuske u
skladu sa argum entom koji je korisnik prosledio m etodi r u n (), otvara tu Ijusku, pokreće
petlju događaja i na kraju uklanja Ijusku kada program završi s radom :
//: swt/util/SWTKonzola.java
package swt.uti1;
import org.eclipse.swt.widgets.*;
Pored već navedenog, tim e se na naslovnoj traci ispisuje im e klase kojoj pripada SW-
TAplikacija i zadaje vrednost param etara sirina i visina za ljusku tipa Shell.
N apisaćem o varijaciju program a SvojstvaKlaseDisplay.java koji pom oću objekta tipa
SWTKonzoIa prikazuje sistemsko okruženje:
//: swt/PrikaziSistemskoOkruzenje.java
import swt.uti1 .*;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import java.util.*;
Meniji
Prikazaćem o osnovne menije tako što će sledeći prim er ućitati sopstveni izvorni kod i
razdeliti ga na reči, a zatim njim a pop un iti menije:
//: swt/Meniji.java
// Zabava s menijima.
import swt.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import java.util.*;
import net.mindview.util.*;
}
}
static Listener prijemnik = new Listener() {
public void handleEvent(Event e) {
System.out.println(e.toString());
void
addItem(Menu linija, Iterator<String> it, Menultem stavkaMenija) {
Menultem stavka = new MenuItem(stavkaMenija.getMenu(),SWT.PUSH);
stavka.addListener(SWT.Selection, prijemnik);
stavka.setText(it.next());
}
public static void main(String[] args) {
SWTKonzola.run(new Meniji(), 600, 200);
}
} ///= -
Meni (objekat tipa Menu) m o ra biti sm ešten u neku ljusku tj. objekat tipa Shell, a nat-
klasa C om posite om ogućuje da tu Ijusku pribavite m etodom getS h ell(). TextFile je deo
paketa net.m indview .util i opisana je u p reth od no m delu knjige; ovde se rečima po-
punjava skup tipa TreeSet, pa će njihov poredak biti uređen. Početni elementi su brojevi,
koji se potom odbacuju. Pom oću toka reči daju se im ena m enijim a najvišeg nivoa u traci
menija, zatim se prave podm eniji i popunjavaju rečima dok ih ne ponestane.
Kao odgovor na izbor stavke menija, prijem nik (objekat tipa Listener) samo ispisuje
taj događaj tako da m ožete videti vrstu inform acija koje sadrži. Kada pokrenete program ,
videćete da te inform acije obuhvataju i natpis na m eniju, pa na osnovu njega možete na-
praviti odgovor m e n ija -ili ćete za svaki m eni napraviti zaseban prijem nik (što jebezbed-
nije u pogledu internacionalizacije).
//; swt/0knoSKarticama.java
// Smeštanje SWT komponenata u okna s karticama.
import swt.uti1 .*;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.browser.*;
Poglavjje 22: Grafička korisnička okruženja 1157
M etoda buttonTab() prikazuje raznu osnovnu dugm ad. M etoda sIiderTab() pona-
vlja Swing prim er iz prethodnog dela poglavlja u kojem se klizač veže s trakom za prika-
zivanje napredovanja.
M etoda scribbleTab() šaljivo prikazuje grafičke m ogućnosti. Program za crtanje je
napravljen pom oću nekoliko redova koda.
Najzad, m etoda browserTab() prikazuje snagu SWT kom ponente Browser - to je
potpuni čitač Weba u jednoj kom ponenti.
Grafika
Ovo je Swing progrSm Sinusoida.java preveden u SWT:
//: swt/Sinusoida.java
// Swing program Sinusoida.java preveden u SWT.
import swt.uti1 .* ; •
import org.ecl1pse.swt.*;
import org.eclipse.swt.widgets.*;
import o rg.eclipse.swt.events.*;
import org.eclipse.swt.layout.*;
setCycles(5);
}
public void zadajCikluse(int noviCiklusi) {
ciklusi = noviCiklusi;
tacke = FAKTORSKALE * ciklusi * 2;
sinusi = new double[tacke];
for(int i = 0; i < tacke; i++) {
double radijani = (Math.PI / FAKTORSKALE) * i;
sinusi[i] = Math.sin(radijani);
}
redraw();
}
}
//: swt/ObojeneKutije.java
// SWT prevod Swing programa ObojeneKutije.java.
import swt.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.* ;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.1ayout.*;
import java.util.concurrent.*;
import java.util.*;
import net.mindview.util.*;
getDisplay().asyncExec(new Runnable() {
public void run() {
try { redraw(); } catch(SWTException e) {}
// Izuzetak SWTException ne smeta kada je roditelj
// okončan na nižem nivou izvršavanja.
}
});
Timellni t.MILLISECONDS.sleep(pauza);
}
} catch(InterruptedException e) {
// Prihvatljiv načirl izlaska
} catch(SWTException e) {
// Prihvatljiv način 'ižlaska: roditelj
// okončan na nižem nivou izvršavanja.
}
}
}
Sažetak
Od svih biblioteka u Javi, biblioteka grafičkih okruženja pretrpela je najveće izmene.
AWT iz Jave 1.0 često je kritikovan kao jedan od najgorih projekata koji su se ikada poja-
vili, i m ada je om ogućavao izradu prenosivih program a, dobijeno grafičko korisničko
okruženje bilo je „podjednako osrednje na svim platformama". Bilo je ograničenih m o-
gućnosti i nezgrapno, a koristilo se teže nego m atične alatke za razvoj aplikacija dostupne
za razne platforme.
Kada su u Javu 1.1 uvedeni nov model događaja i zrna Jave, stanje je poboljšano: sada
je m oguće napraviti kom ponente grafičkog okruženja koje se lako prevlače u n u tar alatki
za vizuelno pravljenje aplikacija. O sim toga, m odel događaja i zrna jasno je pokazao kako
se vodilo računa o lakoći program iranja i održavanja koda (što nije bilo očigledno u bi-
blioteci AWT Jave 1.0). M eđutim , tek kada su se pojavile klase IFC/S\ving, posao je okon-
čan. Uz kom ponente biblioteke Svving, program iranje grafičkih okruženja prenosivih na
razne platform e postalo je relativno bezbolno.
Prava revolucija odigrala se u oblasti alatki za pravljenje aplikacija. Ako želite da se po-
boljša neka kom ercijalna alatka za pravljenje aplikacija, m orate da držite palčeve i nadate
Poglavlje 22: Grafička korisnička okruženja 1165
se da će vam autori okruženja dati ono što želite. Ipak, Java je otvoreno okruženje, što
znači da dozvoljava konkurentska okruženja za pravljenje aplikacija, i podržava takav pri-
stup. D a bi te alatke iko uzeo u obzir, one m oraju da podržavaju Javina zrna. To podra-
zum eva otvoreno igralište: ako se pojavi bolja alatka za pravljenje aplikacija, više nećete
m orati da koristite o nu koju ste dotad koristili, već možete da pređete na novu i tim e po-
većate produktivnost. Ovakva vrsta konkurentskog okruženja za vizuelne alatke ranije
nije postojala, a tržište koje se na ovaj način bude form iralo m ože dati sam o pozitivne re-
zultate u pogledu produktivnosti program era.
Ovo poglavlje trebalo je da vas uvede u osnove program iranja GKO i da vam prikaže
osnovne tehnike za prilično lako pronalaženje sopstvenog p u ta kroz biblioteku. O no što
ste dosad videli verovatno će zadovoljiti dobar deo vaših potreba pri projektovanju kori-
sničkih okruženja. M eđutim , Swing, SWT i Flash/Flex m ogu i m nogo više od toga, jer sa-
drže p o tp u n asortim an alatki za grafička okruženja. Verovatno postoji način da
postignete gotovo sve što m ožete da zamislite.
Resursi
M režne prezentacije Bena G albraitha na adresi w w w .galbraiths.org/presentationslepo ob-
jašnjavaju i Swing i SWT.
Rešenja odabranih vcžbi data su u elektronskom dokumentu The Thinking in Java Annotated Solu-
tion Gtiide, koji se može kupiti na lokaciji www.MindView.cotn.
A: Docfaci
Ova knjiga im a više dodataka, m eđ u kojim a su i sadržaji, sem inari i usluge dostupni na Web
lokaciji M indView .
Te d o p u n e s u o p i s a n e u o v o m d o d a t k u k a k o b i s t e m o g l i d a p r o c e n i t e d a l i v a m
one m ogu koristiti.
O bratite pažnju na to da se sem inari obično drže javno, ali m ogu biti držani i privatno,
na vašoj lokaciji, sam o za vaše osoblje.
Knjige
Efikastto program iranje na Javi, Joshua Bloch (M ikro knjiga, 2004). Obavezna lektira.
Napisao ju je čovek koji je popravio Javinu biblioteku kolekcija. Pisano po u zoru na klasik
Effective C++ koji je napisao Scott Meyer.
Core Java 2, sedm o izdanje, H orstm ann & Cornell, u dva tom a (Prentice Hall, 2005).
Velika, sveobuhvatna, prva knjiga koju uzm em u ruke kada m i zatrebaju odgovori. Pre-
poručujem vam ovu knjigu kada pročitate M isliti na Javi i poželite da pređete na viši nivo.
The Java Class Libraries: An A nnotated Reference, Patrick C han i Rosana Lee (Addi-
son-Wesley, 1997). Nažalost, zastarela je i rasprodata. Takva je trebalo da bude Sunova
HTM L đokum entacija razvojnog program skog paketa za Javu: sa dovoljno opisa da bude
upotrebljiva. O bim na je, skupa, a ni kvalitet prim era m e ne zadovoljava. Pa ipak, m ože
vam poslužiti kada zapadnete u nevolje, a izgleda da su navedena detaljnija objašnjenja
nego u većini drugih knjiga. M eđutim , Core Java 2 im a ažurnija objašnjenja m nogih
kom ponenata biblioteka.
Java N etw ork Programming, drugo izdanje, Eliotte Rusty H arold ( 0 ’Reilly, 2000).
Nisam razum eo umrežavanje u Javi (niti um režavanje uopšte, što se toga tiče) sve dok ni-
sam pronašao ovu knjigu. Sm atram i da Web lokacija autora knjige, Cafe au Lait, pruža
stim ulativan, stručan i aktuelan pogled na poboljšanja u Javi, neoptercćen obavezama
prem a bilo kom proizvođaču. Zbog redovnih ažuriranja, lokacija je u toku s novinam a u
Javi koje se javljaju veoma brzo. Posetite lokaciju w w w .cafeaulait.org.
Design Patterns, Gamm a, Helm, Johnson & Vlissides (Addison-Wesley, 1995). O rigi-
nalna knjiga koja je započela razm atranje projektnih obrazaca i koja se spom inje na više
mesta u ovoj knjizi.
Refactoring to Patterns, Joshua Kerievsky (Addison-Wesley, 2005). Spaja preradu
program a („ponovnu podelu na proste faktore“) i projektne obrasce. Najvredniji aspekt
ove knjige jeste to što pokazuje kako dalje razviti projekat korišćenjem projektnih obra-
zaca po potrebi.
The A rt o f UNIX Programming, Eric Raymond (Addison-Wesley, 2004). Iako je Java
jezik za pisanje program a koji se izvršavaju na svim platform am a, U nix/Linux treba znati
zbog dom inacije Jave na serverima. Ericova knjiga je odličan uvod u istoriju i filozofiju
ovog operativnog sistema. Zanimljivo štivo i za one koji sam o žele da saznaju nešto o ko-
renim a računarstva.
Analiza i projektovanje
Extreme Program m ing Explained, drugo izdanje, Kent Beck uz pom oć Cynthiae Andres
(Addison-Wesley, 2005). Oduvek sam osećao da bi m orao postojati m nogo drugačiji,
m nogo bolji postupak razvijanja program a, i mislim da m u je XP prišao veom a blizu. Je-
dina kniiga koia je ostavila sličan utisak na m ene bila je Peopleware (opisana u nastavku),
koja se pretezno bavi okruženjem i opstankom u poslovnom svetu. E xtrem e Program niing
Explained govori o program iranju i posm atra m nogo toga, čak i najnovija dostignuća, iz
1172 Misliti na Javi
tog ugla. A utori čak tvrde da su slike u redu sve dok ne provodite m nogo vrem ena gleda-
juči ih i sprem ni su da ih izbace. (Prim etićete da se na knjizi ne nalazi „pečat UML“.) Od-
lučivao sam se da li ću raditi za neku kom paniju isključivo na osnovu toga da li koriste XP.
Mala knjiga, kratka poglavlja koja se lako čitaju, a teraju na razmišljanje. Počećete da za-
mišljate kako radite u takvoj atm osferi i otvoriće vam se novi vidici.
UML ukratko, M artin Fovvler (M ikro knjiga, 2004). Kada se prvi p u t sretnete s jezi-
kom UML, uplašićete se jer im a m nogo dijagram a i detalja. Prem a Fowleru, veći deo toga
je nepotreban i on će vam objasniti o n o osnovno. Za većinu projekata potrebno je da po-
znajete sam o nekoliko alatki za crtanje dijagram a, a Fowlerov cilj je da dobije dobar pro-
gram , a n e da brine o tom e kako je stigao do njega. Lepa, tanka, čitljiva knjiga; prva koju
treba da nabavite ako želite da razum ete UML.
D om ain-D riveti Design, Eric Evans (Addison-Wesley, 2004). Bavi se tnodelom dom ena
kao prvenstvenim obeležjem postupka projektovanja. Na osnovu sopstvenog iskustva
uvideo sam da je to prem eštanje naglaska važno, jer pom aže program erim a da ostanu na
pravom nivou apstrakcije.
The Unified Sofhvare D evelopm ent Process, Ivar Jacobsen, G rady Booch i James
Rum baugh (Addison-Wesley, 1999). Bio sam p o tp u n o siguran da mi se ova knjiga neće
svideti. Izgledalo je kao da ima sva obeležja dosadnog fakultetskog udžbenika. Ali, prijat-
no sam se iznenadio: na vrlo malo mesta sreću se objašnjenja za koja bi se m oglo pomisliti
da ni autorim a nisu jasna. Veći deo knjige ne sam o da je jasan, već je i prijatan. Najbolje
od svega je to što su objašnjenja prilično praktična. To ipak nije E xtrem e Program m ing (i
nije toliko jasna u pogledu testiranja), ali je takođe deo UML menažerije: čak i ako vam se
ne sviđa XP, većina program era je sada usvojila stav„U M L je d o b a r“ (bez obzira na njihov
stvarni nivo iskustva s tom m etodom ). Na kraju, i vi ćete m orati da se ukrcate u taj voz.
M islim da je ova knjiga najbolja za proučavanje UML-a, i trebalo bi da je pročitate ako
posle Fowlerove knjige U M L ukratko poželite da saznate više detalja.
Pre nego što se odlučite za bilo koju m etodu, korisno je da se posavetujete s nekim ko
ne želi da vas ubedi da je koristite. Ćesto se dešava da se neka m etoda usvoji, a da se pri
tom ne razum e zaista šta se želi od nje ili koje će prednosti ostvariti. Drugi je koriste, što
izgleda kao dovoljno privlačan razlog. Ljudi im aju jednu čudnu osobinu: ako žele da ve-
ruju kako će nešto rešiti njihove problem e, oni će to i probati. (To je eksperimentisanje,
što je dobro.) M eđutim , ako ne reše svoje problem e, m ožda će udvostručiti napore i na
sva usta objaviti kako su otkrili nešto sjajno. (To je nepriznavanje stvarnosti, što nije do-
bro.) M ožda je pouka da nećete biti usam ljeni, ako nagovorite druge ljude da se ukrcaju
na isti brod, čak i ako taj brod ne ide nikuda (ili tone).
Ovo ne znači da nijedna m etođologija nikuda ne vodi, već da treba da budete
oprem ljeni m entalnim alatkam a koje će vam pom oći da ostanete na nivou eksperimen-
tisanja („Ovo ne radi: hajde da probam o nešto drugo.“), a da izbegavate odricanje (,,To
zapravo i nije problem . Sve ie divno, ne m oram o ništa da m eniam o"). Mislim da će vam
sledeće knjige, ako ih pročitate pre nego što izaberete neku m etodu, obezbediti te alatke.
Softvvare Creativity, Robert Glass (Prentice Hall, 1995). Ovo je najbolja knjiga koja
razm atra perspektivu celog problem a m etodologije. To je zbirka kratkih radova koje je
Glass pisao, ponekad i dobijao od drugih (jedan od sarađnika je P. J. Plauger), a koji izra-
žavaju njegovo dugogodišnje razm išljanje i proučavanje ove teme. Dovoljno su zabavni,
Dodatak B: Resursi 1173
a dugački sam o toliko koliko je potrebno da kažu ono što je neophodno. Takođe, radovi
nisu sam o šuplja priča; nude na stotine referenci na druge radove i istraživanja. Svi pro-
gram eri i m enadžeri trebalo bi da pročitaju ovu knjigu pre nego što se zapute na klizav te-
ren m etodologije.
Softw are Runaways: M onum ental Software Disasters, Robert Glass (Prentice Hall,
1997). O dlična osobina ove knjige je ta što naglas govori o onom e o čem u se ne priča: ko-
liko projekata ne sam o da je propalo, već je propalo spektakularno. U stanovio sam da ve-
ćina program era misli: ,,To m eni ne može da se desi“ (ili ,,Ne može da se desi ponovo'‘) i
sm atram da to nije dobro. Ako im ate na um u da nešto uvek može da krene naopako, u
m nogo ste boljem položaju da učinite da sve radi.
Peopleware, drugo izdatije, Tom DeM arco i T im othy Lister (Dorset H ouse, 1999).
Ovu knjigu m orate da pročitate. Ne sam o da je zabavna, ona će uzdrm ati svet u kojem
prebivate i uništiti pretpostavke na kojim a počiva. Iako su autori projektanti softvera, ova
knjiga je posvećena projektim a i tim ovim a uopšte. M eđutim , istaknuta je važnost Ijudi i
njihovih potreba, a ne tehnologije i njenih potreba. A utori govore o pravljenju okruženja
u kom e će ljudi biti zadovoljni i produktivni, a ne donose pravila koja bi ljudi trebalo da
poštuju kako bi bili odgovarajući delovi mašine. Mislim da je ovaj stav najviše doprineo
tom e se program eri podsm evaju usvajanju m etode XYZ i da nastave da rade kako su od-
uvek radili.
Secrets o f Consulting: A Guide to G iving & G etting Advice Successfully, Gerald M.
VVeinberg (D orste H ouse, 1985). Izvanredna knjiga, jedna od najboljih koju sam pročitao.
Savršena je ukoliko pokušavate da radite kao konsultant ili plaćate konsultante i niste za-
dovoljni njihovim rezultatim a. Iz kratkih poglavlja ispunjenih pričam a i anegdotam a,
učite kako doći do suštine uz najm anje napora. Pročitajte i nastavak M ore Secrets o f Con-
sulting objavljen 2002. ili gotovo bilo koju drugu W einbergovu knjigu.
Com plexity, M. Mitchell W aldrop (Sim on & Schuster, 1992). Knjiga opisuje sastanke
grupe naučnika različitih struka u gradu Santa Fe u Meksiku, koji su raspravljali o razli-
čitim problem im a što u njihovim disciplinam a nisu rešeni (berza u ekonom iji, nastanak
života u biologiji, zašto Ijudi rade to što rade u sociologiji itd.). Suočavanjem gledišta fi-
zike, ekonom ije, hem ije, m atem atike, računarstva, sociologije i drugih nauka, razvijen je
m ultidisciplinarni p ristup tim problem im a. M eđutim , važnije je bilo to što se počelo na
drugi način razm išljati o pom enutim , veom a složenim problem im a: udaljavanje od ma-
tem atičkog determ inizm a i iluzije da se m ože napisati jednačina koja predviđa ponašanje,
i približavanje stavu da treba prvo posm a tra ti i tražiti neku pravilnost, a p otom je opona-
šati na sve m oguće načine. (Knjiga dokum entuje, na prim er, pojavljivanje genetskih al-
goritam a.) Sm atram da je ovakav način razm išljanja koristan jer ukazuje na načine
upravljanja sve složenijim softverskim projektim a.
Jezik Python
L e a rn in g P y th o n , d ru g o izd a n je, M ark Lutz i David Ascher ( 0 ’Reilly, 2003). Lep uvod za
program ere na m om om iljenom jeziku, odličnom pratiocu Jave. Knjiga sadrži i kratak
uvod u Jython koji om ogućuje kom binovanje Jave i Pythona u istom program u (inter-
pretator jezika Jvthon pravi čist Javin bajtkod, pa vam ne treba ništa posebno da biste to
postigli). U jedinjenje ova dva jezika obećava velike m ogućnosti.
1174 Misliti na Javi
Kompletan spisak tcrmina koji se koriste u izdanjim a Mikro knjigc nalazi se na adresi www.mk. c o .y u /r e c m k.
Indeks
i @TestObjectCreate, >
za @Unit, 869
!>75 >, 73
@throws, 61
!=, 73 >=, 73
@Unit, 864
» ,8 0
upotreba, 864
» = ,8 1
& @version, 60
&, 79
&&, 75 A
&=, 80 f abecedno uredivanje, 325
[], operator indeksiranja,
abstract (apstraktna)
145
klasa, 239
nasleđivanje od
.NET, 39 A apstraktnih klasa, 240
.new, sintaksa, 270
A, 80 rezervisana reč, 240
.this, sintaksa, 270 u odnosu na interfejs, 253
^ = ,8 0
AbstractButton, 1069
@ AbstractSequentialList, 684
I AbstractSet, 629
@, simbol za anotacije, 845
@author, 60 I,79 ActionEvent, 1091, 1130
l= , 80 ActionListener, 1055
@Deprecated, anotacija, 845
@deprecated, Javadoc II,75 ActionScript, za Adobe Flex,
oznaka, 62 1139
@docRoot, 60 Adapter, projektni obrazac,
+
250,257, 338,495,582,
@inheritDoc, 60 +,71
@interface, i rezervisana reč 585,631
konverzija u tip String adapteri prijemnika, 1065
extends, 854 operatorom +, 67, 85,
@link, 60 adapterska metoda, 338
393 a d d ( ), m etoda klase
@Override, 845
@param, 61 ArrayList, 303
< addActionListener( ), 1128,
@Retention, 846
@return, 61 1134
< ,73
addChangeListener, 1094
@see, 60 <=, 73
@since, 61 addListener, 1060
« , 80
Adler32, 777
@SuppressWarnings, 845 « = , 81
@Target, 846 Adobe Flex, 1138
<■■''l'est, 8 16
Adobe, proizvođač sistema
Flex, 1138
@Test, za @Unit, 864
@TestObjectCleanup, ==,73 agentski orijentisano
program iranje, 1042
oznaka za alatku @Unit,
872 agregacija, 21
1178 Misliti na Javi
LinkedList, ulančana lista, Map, klasa, 302, 306, 326 pojava pseudonim a pri
311,319, 329,649 EnumM ap, 821 pozivanju metoda, 68
List, klasa, 302, 306, 311, iscrpno istraživanje, 661 polim orfni pozivi metoda,
649,1081 poređenje performansi, 211
poređenje performansi, 698 ponašanje polim orfnih
688 Map.Entry, 673 m etoda unutar
uređivanje i pretraživanje, MappedByteBuffer, 769 konstruktora, 230
705 m a rk (), 737 preklapanje, 117
lista m arker anotacija, 847 prim ena m etode na
grafičke liste, 1081 mašine stanja i nabrojani sekvencu, 577
padajuća lista, 1080 tipovi, 830 privatna, 232
Lister, Timothy, 1173 matcher( ), m etoda za razlikovanje preklopljenih
Listlterator, 649 pronalaženje metoda, 119
literal regularnog izraza, 414 redefinisanje privatnih,
double, 78 matches( ), m etoda klase 221
float, 78 String, 409 rekurzivna, 398
klase, 439,451 matematički operatori, 69, statična, 129, 215
long, 78 773 ugrađivanje poziva
vrednosti, 77 M ath.random ( ), metoda metoda direktno
little endian, 763 opseg rezultata, 695 u kod, 204
logaritmi, prirodni, 79 M ath.random ( ), metoda unutrašnje klase
logička za generisanje slučajnih u m etodam a
konjunkcija, 86 brojeva, 326 i oblastima važenja,
disjunkcija, 86 mehanizam za raspodelu 273
operator i nepotpuno procesorskog vremena vezivanje poziva metoda,
izračunavanje, 76 nitim a, 891 214
operatori, 75 meni zaštićene metode, 198
lokalna JDialog, JApplet, JFrame, Meyer, Jeremy, 845, 879,
unutrašnja klasa, 274 1085 1105
promenljiva, 50 JPopupM enu, 1091 Meyers, Scott, 20
long metaanotacije, 848 Microsoftov Visual BASIC,
i višenitni rad, 926 Metapodaci, 845 1120
marker (L) literala, 78 Method, 1126 migracijska kompatibilnost,
LRU, algoritam najdavnijeg klasa za refleksiju, 462 516
korišćenja, 667 M ethodDescriptor, 1126 m ikropoređenje
Ivrednost, 67 metoda perform ansi, 694
alatka za pronalaženje, miksin, 565
1061 mkdirs( ), 730
M dodavanje metoda mnemonici (prečice
main( ), 184 program u, 179 s tastatiiff), ! 090
manitest datoteka, za JAR finalna, 204,215,232 množenje, 69
datoteke, 779, 1135 generička, 496 modulo, 69
manje ili jednako (<=), 73 inicijalizacijapromenljivih m ogućnost ponovnog
manje od (<), 73 u metodam a, 136 korišćenja, 21
Indeks 1189