06.04.2015 Views

Skripta - FER

Skripta - FER

Skripta - FER

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Sveu£ili²te u Zagrebu<br />

Fakultet elektrotehnike i ra£unarstva<br />

Marko Topolnik<br />

Uvod u programski jezik Java<br />

<strong>Skripta</strong> uz kolegij Seminar Java+XML<br />

Zagreb, 2005.


Sadrºaj<br />

Predgovor<br />

ix<br />

Uvod 1<br />

1 Osnovni elementi jezika Java 3<br />

1.1 Objektna paradigma, enkapsulacija . . . . . . . . . . . . . . 3<br />

1.2 Pokretanje programa pisanih u Javi . . . . . . . . . . . . . . 5<br />

1.2.1 Rad u naredbenom retku . . . . . . . . . . . . . . . . 6<br />

1.2.2 Rad u okruºju Eclipse . . . . . . . . . . . . . . . . . 6<br />

1.3 Pojam klase . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

1.4 Povezivanje objekata, izgradnja objektnog sustava . . . . . . 8<br />

1.5 Inicijalizacija objekta konstruktorom . . . . . . . . . . . . . 9<br />

1.6 Povezivanje objekata agregacijom . . . . . . . . . . . . . . . 11<br />

1.7 Polimorzam i Javino su£elje . . . . . . . . . . . . . . . . . . 13<br />

1.8 Izbjegavanje ponavljanja koda naslježivanjem . . . . . . . . 18<br />

1.9 Klasna hijerarhija, apstraktna klasa . . . . . . . . . . . . . . 20<br />

1.10 Paketi, imenovanje klasa i su£elja . . . . . . . . . . . . . . . 23<br />

1.10.1 Koncepcija paketa . . . . . . . . . . . . . . . . . . . 23<br />

1.10.2 Pravila imenovanja paketa . . . . . . . . . . . . . . . 24<br />

1.10.3 Pravila za raspored datoteka po kazalima . . . . . . . 25<br />

1.10.4 Uloga paketno-privatne razine dostupnosti . . . . . . 25<br />

1.11 Refaktorizacija . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />

1.11.1 Modikator final kontrola polimorzma . . . . . . 26<br />

1.12 Uvoženje vi²e varijanti iste metode preoptere¢enje . . . . . 28<br />

1.13 Prijelaz s agregacije na kompoziciju kopiranje objekata . . 30<br />

2 Razrada detalja jezi£nih svojstava 33<br />

2.1 Varijabla, referenca, objekt . . . . . . . . . . . . . . . . . . . 33<br />

2.1.1 Varijabla primitivne vrste . . . . . . . . . . . . . . . 33<br />

i


ii<br />

SADRšAJ<br />

2.1.2 Varijabla referentne vrste . . . . . . . . . . . . . . . . 34<br />

2.1.3 Opseg vidljivosti (scope) lokalne varijable . . . . . . . 35<br />

2.2 Primitivne vrste podataka . . . . . . . . . . . . . . . . . . . 36<br />

2.3 Numeri£ki i logi£ki izrazi . . . . . . . . . . . . . . . . . . . . 37<br />

2.3.1 Numeri£ki operatori . . . . . . . . . . . . . . . . . . 37<br />

2.3.2 Logi£ki operatori . . . . . . . . . . . . . . . . . . . . 38<br />

2.3.3 Uvjetni (ternarni) operator . . . . . . . . . . . . . . . 39<br />

2.3.4 Operatori sloºene dodjele . . . . . . . . . . . . . . . . 39<br />

2.3.5 Pretvorbe izmežu numeri£kih vrsta . . . . . . . . . . 39<br />

2.4 Enumeracija . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />

2.5 Konstrukcije za upravljanje slijedom izvr²avanja . . . . . . . 43<br />

2.6 Sve razine dostupnosti . . . . . . . . . . . . . . . . . . . . . 45<br />

2.6.1 Model klasne enkapsulacije . . . . . . . . . . . . . . . 46<br />

2.7 Stati£ka metoda izuzeta od polimorzma . . . . . . . . . . 46<br />

2.8 Stati£ki atribut, konstanta . . . . . . . . . . . . . . . . . . . 48<br />

2.8.1 Modikator final rekapitulacija . . . . . . . . . . 49<br />

2.9 Pravila oblikovanja identikatora . . . . . . . . . . . . . . . 49<br />

2.10 Upcast i downcast . . . . . . . . . . . . . . . . . . . . . . . . 50<br />

2.10.1 Operator instanceof . . . . . . . . . . . . . . . . . 51<br />

2.11 Implicitni parametar metode this . . . . . . . . . . . . . . 52<br />

2.11.1 Parametar this i polimorzam . . . . . . . . . . . . 53<br />

2.12 Detalji o hijerarhiji klasa . . . . . . . . . . . . . . . . . . . . 55<br />

2.12.1 Korijenska klasa Object . . . . . . . . . . . . . . . . 55<br />

2.12.2 Denicije izraza vezanih uz hijerarhiju . . . . . . . . 55<br />

2.12.3 Ulan£avanje poziva konstruktora . . . . . . . . . . . 56<br />

2.13 Java kao softverska platforma . . . . . . . . . . . . . . . . . 57<br />

3 Najvaºnije ugražene klase 59<br />

3.1 Uvodne upute za snalaºenje u Javinoj dokumentaciji API-ja 59<br />

3.2 Klasa java.lang.Object . . . . . . . . . . . . . . . . . . . 62<br />

3.2.1 Metoda equals . . . . . . . . . . . . . . . . . . . . . 62<br />

3.2.2 Metoda hashCode . . . . . . . . . . . . . . . . . . . . 64<br />

3.2.3 Metoda toString . . . . . . . . . . . . . . . . . . . . 66<br />

3.3 Klasa java.lang.String . . . . . . . . . . . . . . . . . . . 66<br />

3.3.1 Provjera jednakosti znakovnih nizova . . . . . . . . . 67<br />

3.3.2 Nepromjenjivost instanci String-a . . . . . . . . . . 68<br />

3.4 Klasa System . . . . . . . . . . . . . . . . . . . . . . . . . . 69


SADRšAJ<br />

iii<br />

3.5 Klase-omota£i primitivnih vrsta, autoboxing . . . . . . . . . 69<br />

3.6 Konverzija izmežu podatka i njegovog znakovnog zapisa . . . 70<br />

4 Iznimke 73<br />

4.1 Bacanje iznimke prijava iznimne situacije . . . . . . . . . . 74<br />

4.2 Hvatanje iznimke obrada iznimne situacije . . . . . . . . . 76<br />

4.3 Zavr²ne radnje prije bacanja iznimke blok finally . . . . 78<br />

4.4 Vi²estruki blokovi catch . . . . . . . . . . . . . . . . . . . . 79<br />

4.5 Najava iznimke; provjeravana i neprovjeravana iznimka . . . 81<br />

4.5.1 Najava vi²e iznimki . . . . . . . . . . . . . . . . . . . 82<br />

4.6 Klasna hijerarhija iznimki . . . . . . . . . . . . . . . . . . . 82<br />

4.7 Kori²tenje ugraženih i vlastitih iznimki . . . . . . . . . . . . 83<br />

5 Kolekcije objekata 87<br />

5.1 Su£elja Collection, List i Set . . . . . . . . . . . . . . . . 87<br />

5.2 Prelazak preko svih £lanova kolekcije . . . . . . . . . . . . . 90<br />

5.3 Kolekcije i primitivne vrste podataka . . . . . . . . . . . . . 91<br />

5.4 Napredni prelazak po £lanovima kolekcije . . . . . . . . . . . 92<br />

5.5 Mapa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93<br />

5.6 ƒesto kori²teni algoritmi nad kolekcijama . . . . . . . . . . . 94<br />

5.6.1 Implementiranje su£elja java.lang.Comparable . 94<br />

5.6.2 Poredak razli£it od prirodnog su£elje<br />

java.util.Comparator . . . . . . . . . . . . . . 95<br />

5.6.3 Primjer kori²tenja metode Collections.sort . . . . 96<br />

6 Polja 99<br />

6.1 Osnovni elementi sintakse . . . . . . . . . . . . . . . . . . . 99<br />

6.2 Polje kao objekt . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />

6.3 Interakcija s kolekcijama . . . . . . . . . . . . . . . . . . . . 101<br />

7 Komunikacija s okolinom 103<br />

7.1 Tokovi podataka . . . . . . . . . . . . . . . . . . . . . . . . 103<br />

7.2 Pisa£i i £ita£i . . . . . . . . . . . . . . . . . . . . . . . . . . 104<br />

7.3 Komunikacija s korisnikom . . . . . . . . . . . . . . . . . . . 106<br />

7.4 Rad s datotekama . . . . . . . . . . . . . . . . . . . . . . . . 107<br />

7.5 Rad s internetskim konekcijama . . . . . . . . . . . . . . . . 107


iv<br />

SADRšAJ<br />

8 Uzorci u dizajnu koda 109<br />

8.1 Template method . . . . . . . . . . . . . . . . . . . . . . . . 109<br />

8.2 Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110<br />

8.3 Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113<br />

8.4 Decorator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />

9 Savjeti za rad u Eclipse-u 119<br />

9.1 Automatizacija rada na kodu . . . . . . . . . . . . . . . . . 119<br />

9.2 Povijest datoteke . . . . . . . . . . . . . . . . . . . . . . . . 120<br />

9.3 Seljenje projekta s ra£unala na ra£unalo . . . . . . . . . . . 121<br />

9.3.1 Tipi£an problem prilikom selidbe . . . . . . . . . . . 121<br />

9.3.2 Savjeti za uspje²nu selidbu . . . . . . . . . . . . . . . 121<br />

Literatura 123


Popis ispisa<br />

1.1 Klasa SimpleCounter . . . . . . . . . . . . . . . . . . . . . . . 3<br />

1.2 Klasa ModuloCounter s druga£ijom metodom increment . . . . 4<br />

1.3 Izvr²na klasa . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

1.4 Program koji koristi dvije instance iste klase . . . . . . . . . 7<br />

1.5 Klasa DifferentialCounter . . . . . . . . . . . . . . . . . . . 8<br />

1.6 Pro²irena varijanta klase SimpleCounter . . . . . . . . . . . . 9<br />

1.7 Kori²tenje konstruktora . . . . . . . . . . . . . . . . . . . . . 10<br />

1.8 Analizator teksta . . . . . . . . . . . . . . . . . . . . . . . . 11<br />

1.9 Analiza vi²edijelnog dokumenta . . . . . . . . . . . . . . . . 12<br />

1.10 Nadomjesna implementacija klase TextFile . . . . . . . . . . 12<br />

1.11 Polimorfni analizator teksta . . . . . . . . . . . . . . . . . . 13<br />

1.12 Su£elje Counter . . . . . . . . . . . . . . . . . . . . . . . . . 14<br />

1.13 Polimorzam na djelu . . . . . . . . . . . . . . . . . . . . . 14<br />

1.14 Jo² jedan primjer kori²tenja polimorzma . . . . . . . . . . . 16<br />

1.15 Obvezivanje na implementaciju su£elja . . . . . . . . . . . . 17<br />

1.16 Naslježivanje osnovna klasa . . . . . . . . . . . . . . . . . 18<br />

1.17 Naslježivanje izvedena klasa . . . . . . . . . . . . . . . . . 19<br />

1.18 Apstraktna klasa . . . . . . . . . . . . . . . . . . . . . . . . 21<br />

1.19 Konkretne klase izvedene iz apstraktne . . . . . . . . . . . . 22<br />

1.20 Nova verzija apstraktne klase . . . . . . . . . . . . . . . . . 27<br />

1.21 Nove konkretne klase . . . . . . . . . . . . . . . . . . . . . . 27<br />

1.22 Pro²ireno su£elje Counter . . . . . . . . . . . . . . . . . . . . 29<br />

1.23 Pro²irenje apstraktne klase . . . . . . . . . . . . . . . . . . . 29<br />

1.24 Dodatak metode za kopiranje u su£elje Counter . . . . . . . . 30<br />

1.25 Klasa SimpleCounter s dodanom podr²kom za kopiranje . . . 30<br />

1.26 Analizator teksta koji koristi kompoziciju . . . . . . . . . . . 31<br />

2.1 Implicitna pretvorba vrste cjelobrojnih podataka . . . . . . . 39<br />

2.2 Eksplicitna pretvorba vrste cjelobrojnih podataka . . . . . . 40<br />

v


vi<br />

POPIS ISPISA<br />

2.3 Jednostavna enumeracija . . . . . . . . . . . . . . . . . . . . 40<br />

2.4 Osnovno kori²tenje enumeracije . . . . . . . . . . . . . . . . 41<br />

2.5 Kori²tenje enumeracije u bloku switch . . . . . . . . . . . . . 41<br />

2.6 Oboga¢ena enumeracija . . . . . . . . . . . . . . . . . . . . . 42<br />

2.7 Kratki primjeri uporabe upravlja£kih konstrukcija . . . . . . 43<br />

2.8 Izjave break i continue koje koriste oznake . . . . . . . . . . . 44<br />

2.9 Model klasne enkapsulacije . . . . . . . . . . . . . . . . . . . 46<br />

2.10 Poziv stati£ke metode . . . . . . . . . . . . . . . . . . . . . . 47<br />

2.11 Poziv stati£ke metode sintaksom kao da je instancina metoda 48<br />

2.12 Stati£ki atribut, javna konstanta . . . . . . . . . . . . . . . . 48<br />

2.13 ModuloCounter s dodatnom metodom setModulus . . . . . . . . 50<br />

2.14 Metoda koja upravlja ModuloCounter-om . . . . . . . . . . . . 51<br />

2.15 Doražena metoda useCounter . . . . . . . . . . . . . . . . . . 52<br />

2.16 Implicitni parametar this . . . . . . . . . . . . . . . . . . . 52<br />

2.17 Neuspio poku²aj uvoženja eksplicitnog parametra this . . . 53<br />

2.18 Neuspio poku²aj kori²tenja metode s eksplicitnim this . . . . 54<br />

2.19 Eksplicitan poziv konstruktora natklase . . . . . . . . . . . . 56<br />

2.20 Poziv konstruktora iste klase . . . . . . . . . . . . . . . . . . 57<br />

3.1 Implementacija metode equals . . . . . . . . . . . . . . . . . 62<br />

3.2 Tipi£na pogre²ka s preoptere¢enjem umjesto nadja£avanjem 63<br />

3.3 Predloºak za generiranje he²-koda . . . . . . . . . . . . . . . 65<br />

3.4 Implementacija metode toString . . . . . . . . . . . . . . . . 66<br />

3.5 Posljedice pogre²nog kori²tenja == za usporedbu String-ova . 67<br />

4.1 ModuloCounter s provjerom parametra . . . . . . . . . . . . . 73<br />

4.2 Bacanje i ponovno bacanje iznimke . . . . . . . . . . . . . . 75<br />

4.3 Izvje²taj o ba£enoj iznimci . . . . . . . . . . . . . . . . . . . 75<br />

4.4 Hvatanje iznimke . . . . . . . . . . . . . . . . . . . . . . . . 76<br />

4.5 Blok finally . . . . . . . . . . . . . . . . . . . . . . . . . . . 78<br />

4.6 Vi²estruki blokovi catch . . . . . . . . . . . . . . . . . . . . 79<br />

4.7 Najava iznimke . . . . . . . . . . . . . . . . . . . . . . . . . 81<br />

4.8 Deniranje vlastite iznimke . . . . . . . . . . . . . . . . . . 84<br />

5.1 Osnovno baratanje kolekcijom . . . . . . . . . . . . . . . . . 87<br />

5.2 Lista i skup . . . . . . . . . . . . . . . . . . . . . . . . . . . 88<br />

5.3 Prolaz po kolekciji petljom enhanced for . . . . . . . . . . . 90<br />

5.4 Kolekcija i primitivne vrste podataka . . . . . . . . . . . . . 91<br />

5.5 Prolaz po kolekciji eksplicitnim kori²tenjem iteratora . . . . 92<br />

5.6 Kori²tenje iteratora za mijenjanje liste . . . . . . . . . . . . 92


POPIS ISPISA<br />

vii<br />

5.7 Klasa Student koja demonstrira mapu . . . . . . . . . . . . . 93<br />

5.8 Metoda compareTo . . . . . . . . . . . . . . . . . . . . . . . . 95<br />

5.9 Komparator za studente koji name¢e poredak po ID-ju . . . 95<br />

6.1 Rad s jednodimenzionalim poljem . . . . . . . . . . . . . . . 99<br />

6.2 Rad s dvodimenzionalnim poljem . . . . . . . . . . . . . . . 99<br />

6.3 Prolaz po svim £lanovima polja petljom enhanced for . . . . 100<br />

6.4 Trokutasto dvodimenzionalno polje . . . . . . . . . . . . . . 100<br />

6.5 Prolaz po polju kori²tenjem eksplicitnog indeksa . . . . . . . 101<br />

6.6 Pretvorbe izmežu polja i liste . . . . . . . . . . . . . . . . . 101<br />

7.1 Rad s izlaznim tokom . . . . . . . . . . . . . . . . . . . . . . 103<br />

7.2 Rad s ulaznim tokom . . . . . . . . . . . . . . . . . . . . . . 104<br />

7.3 Pisa£ i £ita£ . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

7.4 Omota£i podatkovnih tokova . . . . . . . . . . . . . . . . . . 105<br />

8.5 Su£elje oslu²kiva£a dolaznih paketa . . . . . . . . . . . . . . 111<br />

8.6 Jednostavna implementacija oslu²kiva£a dolaznih paketa . . 111<br />

8.7 Komunikacijski £vor s podr²kom za oslu²kiva£e . . . . . . . . 111<br />

8.8 Kori²tenje sustava . . . . . . . . . . . . . . . . . . . . . . . . 112<br />

8.9 Pokazna izvedba adaptera na znakovni tok . . . . . . . . . . 113<br />

8.10 Primjer primjene uzorka Decorator . . . . . . . . . . . . . . 116


Predgovor<br />

Svrha ovog kolegija (to£nije, njegovog dijela posve¢enog jeziku Java) je<br />

ne samo uputiti studente u sintaksu jezika Java, ve¢ prije svega podu£iti<br />

tehnike objektno-orijentiranog programiranja i dizajna sustava objekata.<br />

Na ovaj predmet uglavnom se dolazi s tek minimalnim predznanjem programiranja<br />

u imperativnoj paradigmi i bez razvijenih vje²tina u dizajnu<br />

programskog sustava. To minimalno predznanje naºalost £ak predstavlja i<br />

smetnju u savladavanju Jave i objektno-orijentiranog pristupa jer daje la-<br />

ºan dojam poznatosti jezi£nim konstrukcijama koje ustvari imaju druga£ije<br />

zna£enje i, ²to je jo² vaºnije, druga£iju ulogu u jeziku.<br />

Pri savladavanju nove, objektne paradigme student kao prvo treba dobro<br />

razumjeti osnovna svojstva i mogu¢nosti objekta, ali to jo² uvijek nije<br />

dovoljno za uspje²no rje²avanje zadataka. Jednako je bitno razviti vje²tinu<br />

organiziranja £itavog sustava objekata koji predstavlja rje²enje zadanog<br />

problema. Za mnoge elementarne probleme postoje standardna rje²enja<br />

koja je razvijateljska zajednica prihvatila kao najbolja. Sva takva rje²enja<br />

treba usvojiti umjesto otkrivati toplu vodu vlastitim pristupima. Po£etnik<br />

treba biti svjestan da jo² nije do²ao u poziciju da ima puni pregled nad<br />

razlozima za²to je neko rje²enje bolje od drugog. 1 Takožer, tendencija studenata<br />

je da se za rje²avanje nekih od problema oslanjaju na ve¢ poznate<br />

tehnike iz jezika C i imaju velik otpor pri usvajanju tehnika uobi£ajenih za<br />

Javu.<br />

U£enje programskog jezika u mnogo£emu podsje¢a na u£enje prirodnog<br />

jezika. Iz istog razloga za²to nitko jo² nije nau£io npr. engleski jezik isklju-<br />

£ivo £itaju¢i gramatiku i rje£nik, nitko nije i ne¢e nau£iti Javu isklju£ivo<br />

£itaju¢i ovu skriptu i slu²aju¢i predavanja. Neizostavna aktivnost jest tre-<br />

1 Detaljnija argumentacija ovoga krije se iza fraze shu-ha-ri (usvajanje pravilakr²enje<br />

pravila-napu²tanje pravila) o kojoj se moºe informirati na Webu, npr.<br />

c2.com/cgi/wiki?ThreeLevelsOfAudience<br />

ix


x<br />

Predgovor<br />

ning, a to u ovom kontekstu zna£i vlastoru£no pisanje koda iz nule, uz stalni<br />

nadzor asistenta koji zadaje zadatke, prou£ava va²a rje²enja i konstantno<br />

vas korigira i usmjerava. Dakle, nije ispravno preuzeti zadatak i rje²avati<br />

ga u izolaciji: kriterij uspjeha nije krajnji proizvod sadrºan u datotekama<br />

.java, nego vje²tina koju ste stekli. Zadatak je tek sredstvo, primjer na<br />

kojem se moºe najbolje utvrditi razina va²e vje²tine i, ponajprije, pratiti<br />

podizanje te razine tijekom semestra. U praksi, studenti koji bez interakcije<br />

s asistentom na kraju semestra dolaze s rje²enjem zadatka dijele se u dvije<br />

skupine: oni £iji kod je niske kvalitete i jasno je da nisu ni²ta nau£ili i oni<br />

£iji kod je kvalitetan jer se nadaju da asistent ne¢e shvatiti da taj kod nije<br />

njihovo djelo. U drugoj skupini ima dodu²e i ne²to onih koji zaista posjeduju<br />

potrebne vje²tine, ali tek vrlo malo onih koji su te vje²tine usvojili<br />

samostalno tijekom semestra.<br />

Iako nije dovoljna sama za sebe, ova skripta ima vaºnu ulogu u £itavom<br />

procesu u£enja: da biste uop¢e mogli komunicirati s nekim poznavateljem<br />

Jave, morate ve¢ razumjeti odreženi skup pojmova i stru£nih izraza. Ako<br />

asistent kaºe npr. ovaj atribut ne bi trebao biti stati£ki jer mu je vrijednost<br />

razli£ita za svaku instancu, onaj tko nije ni pro£itao skriptu ne¢e imati<br />

koristi od takvog savjeta jer ga naprosto ne¢e razumjeti. Jednako tako,<br />

prilikom ispitivanja dogodit ¢e vam se da ne razumijete postavljeno pitanje,<br />

iako biste moºda £ak i znali odgovor.<br />

Na kraju, ºelim se izjasniti i po pitanju jedne vrlo pro²irene predrasude:<br />

pisanje dobrog koda nije nikakvo mehanizirano ²trikanje, to je intelektualno<br />

zahtjevan i kreativan proces sa svojom vlastitom estetikom. Ako netko<br />

smatra druga£ije, to je najvjerojatnije stoga ²to nije jo² razvio estetske kriterije<br />

da bi uvidio razliku izmežu lijepog, kreativnog i ruºnog, nabacanog<br />

koda. Ta £injenica predstavlja zna£ajnu prepreku: u£enik prvo treba skupiti<br />

motivaciju da umo£i noge da bi uop¢e do²ao u poziciju da cijeni vlastiti<br />

rad, a tko ne cijeni ono ²to radi ne moºe ni imati dobro mi²ljenje o svojoj<br />

struci.<br />

dr.sc. Marko Topolnik<br />

Zagreb, 1. rujna 2005.


Uvod<br />

Nastava na kolegiju Seminar Java+XML sastoji se iz dva dijela: predavanja<br />

i laboratorijske vjeºbe, s time da je teºi²te na vjeºbama. Organizacija<br />

samih vjeºbi na kolegiju je speci£na. Glavnina termina (oko pet-²est) posve¢ena<br />

je izradi malog softverskog projekta. Termine vjeºbi treba prije<br />

svega koristiti za komunikaciju s asistentom koji ¢e nadzirati va² napredak<br />

na projektu. Ovaj moment je kriti£an jer se va²a ocjena prvenstveno formira<br />

upravo tijekom te komunikacije, a na kraju semestra bit ¢e posebno<br />

ispitivani samo oni studenti koji iz bilo kojeg razloga nisu suraživali s asistentom<br />

tijekom semestra. Kriterij za ocjenu nije ispravan rad aplikacije koju<br />

ste razvili, ve¢ razina vje²tine programiranja koju ste stekli. Izradi projekta<br />

prethode manji zadaci na kojima ¢ete savladati najosnovnije tehnike rada<br />

s razvojnim alatom i pisanja samog koda. Rje²enja zadataka ocjenjuju se<br />

neovisno o projektu.<br />

<strong>Skripta</strong> se odnosi na Javinu verziju 5 (Java 2 Platform Standard Edition<br />

5.0). Sadrºaj ove skripte bit ¢e izlagan na predavanjima, tako da se<br />

studentima preporu£a da unaprijed pro£itaju poglavlje £ija ¢e se materija<br />

izlagati na sljede¢em predavanju.<br />

U skripti je obražen samo osnovni podskup jezika Java. Neka od izostavljenih<br />

podru£ja:<br />

• ugnjeºžene klase nose zna£ajan dio ukupne kompleksnosti jezika i<br />

intenzivno se koriste u programiranju GUI-ja;<br />

• reeksija sluºi za zaobilaºenje ograni£enja Javinog stati£kog odreživanja<br />

vrste izraza;<br />

• model vi²enitnosti omogu¢uje kori²tenje vi²e izvr²nih niti (dretvi) i<br />

sinkroniziran pristup zajedni£kim resursima. Bit ¢e pokriven u predmetu<br />

Konkurentno programiranje.<br />

1


2 Uvod<br />

Takožer je iznimno vaºno imati na umu da je i materija koja se obražuje<br />

u skripti obražena samo sa svrhom uvoda u Javu za po£etnika nikako se ne<br />

smije shvatiti kao potpuna informacija. Gotovo sve ovdje re£eno predstavlja<br />

drasti£no pojednostavljenje u odnosu na preciznu i potpunu istinu, sve u<br />

svrhu lak²e razumljivosti po£etniku. Ako je £itatelj ve¢ poznavatelj Jave i<br />

takva ga pojednostavljenja smetaju, neka slobodno odloºi skriptu jer njemu<br />

ona i ne treba.<br />

Studentu se preporu£a da prvo poglavlje pro£ita u cijelosti jer se tamo<br />

spominje velik broj najvaºnijih pojmova, a tekst je pisan kao jedna pri£a<br />

u kojoj se pojmovi uvode gdje koji zatreba. Tekst daljnjih poglavlja nije<br />

toliko usko povezan pa £itanje po redu nije tako bitno.


Poglavlje 1<br />

Osnovni elementi jezika Java<br />

1.1 Objektna paradigma, enkapsulacija<br />

U objektno-orijentiranom programskom jeziku izrada programa moºe se u<br />

osnovi svesti na izgradnju sustava objekata. Objekti su mežusobno povezani<br />

(znaju jedan za drugog) i mežusobno komuniciraju razmjenom poruka,<br />

²to se svodi na to da jedan objekt poziva metode drugoga. Dakle, pri kretanju<br />

u rje²avanje zadanog problema prvo treba osmisliti koji objekti ¢e biti<br />

potrebni i kako ¢e biti povezani. ƒesto je mogu¢e posti¢i da programski<br />

objekti modeliraju stvarne objekte. Za svaki objekt najvaºnije je utvrditi<br />

²to ¢e se od njega o£ekivati da radi, tj. njegovo pona²anje. U praksi to se<br />

svodi na popis metoda koje ¢e on posjedovati. Za svaku metodu najbitnije<br />

je koje argumente prima i kakvu vrijednost vra¢a. Takožer je bitno na koji<br />

na£in poziv metode utje£e na stanje objekta. O stanju ovisi kako ¢e objekt<br />

odgovarati na budu¢e pozive metoda. Dakle, identi£an poziv metode u razli£itim<br />

trenucima moºe vratiti razli£itu vrijednost, ovisno o stanju u kojem<br />

je objekt zate£en. Stanje objekta u praksi se svodi na vrijednosti njegovih<br />

atributa (unutarnjih varijabli, na engleskom se u kontekstu Jave £esto<br />

koristi i izraz eld). Osnovno na£elo dizajna objekata enkapsulacija ili<br />

zatvaranje nalaºe da stanje objekta mora ostati skriveno, a izvana trebaju<br />

biti vidljive samo one posljedice tog stanja koje su zaista potrebne dakle<br />

utjecaj stanja na povratne vrijednosti metoda. Evo jednostavnog primjera<br />

opisa objekta u Javi:<br />

public class SimpleCounter<br />

Ispis 1.1: Klasa SimpleCounter<br />

3


4 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

{<br />

private int counterState;<br />

}<br />

public void increment()<br />

{<br />

this.counterState++;<br />

}<br />

public int getState()<br />

{<br />

return this.counterState;<br />

}<br />

Na² objekt opisan klasom SimpleCounter raspolaºe dvjema metodama,<br />

od kojih jedna mijenja njegovo stanje (increment), a druga to stanje dojavljuje<br />

vanjskom svijetu (getState). Dakle, rezultat metode getState je ovisan<br />

o trenutnom stanju objekta. Stanje je pohranjeno u atributu counterState.<br />

To je obi£an cijeli broj (vrste int, poznat iz jezika C). Pri kreiranju objekta<br />

svi brojevni atributi se implicitno inicijaliziraju na vrijednost nula.<br />

UML-ov dijagram klase SimpleCounter prikazan je na slici 1.1.<br />

SimpleCounter<br />

-counterState : int<br />

+increment()<br />

+getState() : int<br />

Slika 1.1: UML-ov dijagram klase SimpleCounter<br />

Vaºno je da svi atributi imaju oznaku private, £ime se ostvaruje na£elo<br />

skrivenosti stanja odnosno enkapsulacije. U protivnom bi ih objektu iza<br />

leža mogli mijenjati drugi objekti. Recimo da imamo malo druga£iju klasu,<br />

ModuloCounter, £ija metoda increment ustvari izgleda ovako:<br />

Ispis 1.2: Klasa ModuloCounter s druga£ijom metodom increment<br />

public class ModuloCounter<br />

{<br />

public void increment()<br />

{<br />

if ( this.counterState == 9 )<br />

this.counterState = 0;<br />

else<br />

this.counterState++;<br />

}


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 5<br />

}<br />

... atribut counterState i metoda getState isti kao u SimpleCounter ...<br />

dakle, da broja£ cirkularno broji samo do devet i onda se vra¢a na nulu. Kad<br />

atribut counterState ne bi bio privatan, mogao bi se izvana promijeniti na<br />

proizvoljnu vrijednost, npr. 10. Time bi se naru²ila funkcionalnost objekta<br />

jer bi se, kao prvo, broja£ na²ao u nedozvoljenom stanju i, kao drugo, daljnji<br />

pozivi metode increment nastavili bi brojati prema beskona£nosti.<br />

Enkapsulacija, iako ju sam jezik Java ne name¢e, uglavnom je neizostavan<br />

dio dizajna objekta jer spre£ava velik broj programerskih pogre²aka uz<br />

naj£e²¢e malu ili nikakvu ºrtvu u eleganciji koda. Takožer daje programeru<br />

ve¢u slobodu u eventualnom redizajniranju objekta. Imena i podatkovne<br />

vrste (tipovi) atributa mogu se proizvoljno mijenjati dok god se zadrºava<br />

isto pona²anje objekta kakvo se o£ituje u vrijednostima koje vra¢aju metode.<br />

1.2 Pokretanje programa pisanih u Javi<br />

U jeziku Java program se pokre¢e tako da se prvo denira izvr²nu klasu<br />

koja sadrºi posebnu metodu main. Ona ¢e biti implicitno pozvana kad izvr-<br />

²imo klasu, tj. pokrenemo svoj program. Metoda prima parametar polje<br />

znakovnih nizova. U njemu su pobrojani argumenti zadani u naredbenom<br />

retku prilikom pokretanja programa.<br />

Ispis 1.3: Izvr²na klasa<br />

public class AppThatUsesTheCounter<br />

{<br />

public static void main( String[] args )<br />

{<br />

SimpleCounter cntr = new SimpleCounter();<br />

System.out.println( "Po£etno stanje broja£a: " + cntr.getState() );<br />

cntr.increment();<br />

System.out.println( "Stanje nakon poziva metode increment: "<br />

+ cntr.getState() );<br />

}<br />

}<br />

Prikazana metoda main stvara jedan objekt, koristi ga i o rezultatima<br />

izvje²¢uje korisnika ispisom teksta na zaslon. Izraz new SimpleCounter() kreira<br />

novi objekt klase SimpleCounter. Referenca na taj objekt zapisuje se u


6 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

lokalnu varijablu cntr i nakon toga se preko te varijable mogu pozivati<br />

metode objekta. Ispis koji generira program bi trebao biti sljede¢i:<br />

Po£etno stanje broja£a: 0<br />

Stanje nakon poziva metode increment: 1<br />

1.2.1 Rad u naredbenom retku<br />

Nekim editorom teksta treba stvoriti datoteku s izvornim kodom. Ime datoteke<br />

mora biti identi£no imenu klase + suks .java. Datoteku se prevodi<br />

(kompajlira) Javinim alatom javac (Java compiler):<br />

$ javac AppThatUsesTheCounter.java<br />

Rezultat ovog koraka nije izvr²na datoteka koju se moºe izravno pokrenuti.<br />

Rezultat je klasna datoteka (eng. class le) koja sadrºi binarnu<br />

deniciju klase u posebnom Javinom mežukodu (eng. bytecode, vidjeti odjeljak<br />

2.13). To je izvr²ni kod za tzv. Javin virtualni stroj (eng. Java Virtual<br />

Machine odnosno JVM) posebnu hardversku platformu koja postoji samo<br />

na papiru, a svaka realna hardverska platforma ju emulira. Dakle, u naredbenom<br />

retku treba pokrenuti emulator, tj. JVM, i proslijediti mu ime<br />

na²e izvr²ne klase:<br />

$ java AppThatUsesTheCounter<br />

Vaºno! Kao argument ove naredbe ne navodi se ime klasne datoteke, ve¢<br />

samo ime klase. To ¢e pogotovo biti vaºno kasnije, kad uvedemo pakete<br />

(odjeljak 1.10).<br />

1.2.2 Rad u okruºju Eclipse<br />

Kako pokrenuti ovaj primjer u Eclipse-u (vrijedi za verziju 3.1):<br />

1. File, New, Project... Java Project, Next<br />

2. Upisati ime projekta, uvjeriti se da je uklju£eno Create separate source<br />

and output folders, Finish<br />

3. U okviru Package Explorer na¢i projekt, ekspandirati ga, namjestiti<br />

oznaku na direktorij src.


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 7<br />

4. File, New, Class. Upisati ime klase. U editorskom okviru otvara se<br />

stvorena datoteka i u njoj ve¢ pi²e prvi redak koda. Upisati ostatak<br />

koda.<br />

5. Pokretanje klase: desni klik na izvr²nu klasu u Package Explorer-u,<br />

Run As, Java Application. Ili, dok je kurzor u editoru doti£ne klase,<br />

upotrijebiti kombinaciju tipaka Alt-Shift-X, J.<br />

6. U okviru Console vidi se ispis programa.<br />

7. Ako bilo koji od okvira nije vidljiv, moºe ga se otvoriti pomo¢u Window,<br />

Show view<br />

1.3 Pojam klase<br />

U ovom trenutku moºe se usvojiti i osnovna ideja klase. Moºemo ju smatrati<br />

predlo²kom za objekte koji prvenstveno propisuje koje atribute ¢e objekt<br />

imati za £uvanje stanja i sadrºi denicije metoda koje ¢e se nad njim mo¢i<br />

pozivati. Objekt se jo² naziva i instancom svoje klase. Sve instance iste klase<br />

imaju isti popis atributa, ali naravno svaka ima svoje vlastite vrijednosti<br />

tih atributa. To se moºe ilustrirati ako izmijenimo tekst gornje metode main<br />

na sljede¢i:<br />

Ispis 1.4: Program koji koristi dvije instance iste klase<br />

public static void main( String[] args )<br />

{<br />

SimpleCounter cntr1 = new SimpleCounter();<br />

SimpleCounter cntr2 = new SimpleCounter();<br />

System.out.println( "Po£etna stanja broja£a: " + cntr1.getState() +<br />

", " + cntr2.getState() );<br />

cntr1.increment();<br />

System.out.println( "Stanja nakon poziva metode increment nad cntr1: "<br />

+ cntr1.getState() + ", " + cntr2.getState() );<br />

}<br />

O£ekivani ispis:<br />

Po£etna stanja broja£a: 0, 0<br />

Stanja nakon poziva metode increment nad cntr1: 1, 0<br />

Klasa AppThatUsesTheCounter s ispisa 1.3 je druga£ija: ona ne opisuje<br />

nikakav smisleni objekt, ve¢ samo sadrºi metodu main koja je poseban slu£aj<br />

i nije normalna metoda objekta. O tome ¢e se vi²e raspravljati kasnije (u<br />

odjeljku 2.7).


8 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

1.4 Povezivanje objekata, izgradnja objektnog<br />

sustava<br />

Dva objekta moºe se povezati tako da jedan objekt sadrºi atribut koji<br />

pokazuje na drugi objekt, npr.<br />

Ispis 1.5: Klasa DifferentialCounter<br />

public class DifferentialCounter<br />

{<br />

private SimpleCounter cntr1 = new SimpleCounter();<br />

private SimpleCounter cntr2 = new SimpleCounter();<br />

}<br />

public void increment1()<br />

{<br />

this.cntr1.increment();<br />

}<br />

public void increment2()<br />

{<br />

this.cntr2.increment();<br />

}<br />

public int getDifference()<br />

{<br />

return this.cntr2.getState() - this.cntr1.getState();<br />

}<br />

public void reset()<br />

{<br />

this.cntr1 = new SimpleCounter();<br />

this.cntr2 = new SimpleCounter();<br />

}<br />

UML-ovi klasni i objektni dijagram za ovaj slu£aj prikazani su na slikama<br />

1.2 i 1.3.<br />

cntr1<br />

diffCounter1<br />

cntr2<br />

Slika 1.2: Objektni dijagram minimalnog objektnog sustava


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 9<br />

DifferentialCounter<br />

+increment1()<br />

+increment2()<br />

+getState() : int<br />

-cntr1, cntr2<br />

1 2<br />

SimpleCounter<br />

-counterState : int<br />

+increment()<br />

+getState() : int<br />

Slika 1.3: Klasni dijagram minimalnog objektnog sustava<br />

Objekt klase DifferentialCounter povezan je s dva objekta klase Simple-<br />

Counter i komunicira s njima. Na primjer, kad se pozove metoda getDifference,<br />

objekt ¢e stupiti u kontakt sa svakim od broja£a i zatraºiti njihovo trenutno<br />

stanje. Time je izgražen minimalan sustav objekata. Asocijativni odnos izmežu<br />

DifferentialCounter i SimpleCounter naziva se kompozicijom. Klju£na<br />

karakteristika ovog odnosa je da podreženi objekti postoje isklju£ivo kao<br />

sastavni dio nadreženog i njihovo postojanje je uvjetovano tim objektom. U<br />

to se moºemo uvjeriti ako uvidimo da je DifferentialCounter taj koji stvara<br />

podrežene objekte i da nijedan vanjski objekt ne moºe do¢i do reference<br />

na njih (zapisane su u privatnim atributima, nijedna javna metoda ih ne<br />

vra¢a).<br />

Prikazani slu£aj u kojem se atributi cntr1 i cntr2 inicijaliziraju na mjestu<br />

gdje su i deklarirani samo je jedan od na£ina inicijalizacije atributa. Na<br />

primjer, metoda reset takožer ih (re)inicijalizira, stvaraju¢i nove objekte<br />

SimpleCounter (time se stari objekti implicitno odbacuju i bit ¢e automatski<br />

uklonjeni iz radne memorije postupkom zvanim garbage collection, sakupljanje<br />

sme¢a). Tipi£an na£in inicijalizacije atributa je unutar konstruktora,<br />

o kojem ¢e se pri£ati u sljede¢em odjeljku.<br />

1.5 Inicijalizacija objekta konstruktorom<br />

Svaki put kad upotrijebimo klju£nu rije£ new kojom zatraºimo kreiranje<br />

nove instance zadane klase, implicitno smo zatraºili i pozivanje posebne<br />

inicijalizacijske procedure opisane konstruktorom objekta. Svaka klasa ima<br />

bar jedan konstruktor, a ako ih ima vi²e, oni se razlikuju po broju i vrstama<br />

parametara. Pomo¢u parametara moºemo prenijeti dodatnu informaciju<br />

inicijalizacijskom postupku, £ime postiºemo da se objekt inicijalizira<br />

u onakvo stanje kakvo je potrebno u danom trenutku. Primjerice, moºemo<br />

pro²iriti na²u klasu SimpleCounter:


10 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

Ispis 1.6: Pro²irena varijanta klase SimpleCounter<br />

public class SimpleCounter<br />

{<br />

private int counterState;<br />

public SimpleCounter( int initialState )<br />

{<br />

this.counterState = initialState;<br />

}<br />

public SimpleCounter()<br />

{<br />

//ne radi ni²ta dodatno; counterState se automatski<br />

//inicijalizira na nulu<br />

}<br />

}<br />

... deklaracije metoda increment i getState kao ranije ...<br />

U kodu 1.6 denirana su dva konstruktora: prvi prima jedan cijeli broj <br />

po£etnu vrijednost broja£a. Drugi je takozvani podrazumijevani konstruktor<br />

onaj koji ne prima nijedan parametar. U ranijem slu£aju (ispis 1.1),<br />

iako nije bio deklariran nijedan, klasi je implicitno dodan upravo ovakav,<br />

prazan podrazumijevani konstruktor. Mežutim, £im deklariramo bilo koji<br />

konstruktor eksplicitno, ni²ta se ne dodaje implicitno. Dakle, klasa ne mora<br />

imati podrazumijevani konstruktor, iako bi se to moglo zaklju£iti sude¢i po<br />

njegovom imenu.<br />

Primjer poziva konstruktora:<br />

Ispis 1.7: Kori²tenje konstruktora<br />

public static void main( String[] args )<br />

{<br />

SimpleCounter cntr1 = new SimpleCounter();<br />

SimpleCounter cntr2 = new SimpleCounter( 3 );<br />

System.out.println( "Po£etna stanja broja£a: " + cntr1.getState() +<br />

", " + cntr2.getState() );<br />

cntr1.increment();<br />

System.out.println( "Stanja nakon poziva metode increment nad cntr1: "<br />

+ cntr1.getState() + ", " + cntr2.getState() );<br />

}<br />

U ovom primjeru cntr1 se inicijalizira podrazumijevanim konstruktorom,<br />

a cntr2 konstruktorom koji putem parametra zaprima ºeljeno po£etno<br />

stanje. O£ekivani ispis:<br />

Po£etno stanje broja£a: 0, 3<br />

Stanje nakon poziva metode increment nad cntr1: 1, 3


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 11<br />

1.6 Povezivanje objekata agregacijom<br />

Uzmimo da imamo objekt koji sluºi za jednostavnu analizu tekstualne<br />

datoteke ustanovljuje koliko redaka teksta sadrºi. Datoteku predstavlja<br />

objekt vrste TextFile. Nad njim moºemo pozvati metodu readLine koja<br />

£ita jedan redak datoteke i metodu endReached koja nas izvje²tava ima li jo²<br />

redaka za pro£itati. Za £uvanje rezultata brojanja koristi se objekt vrste<br />

SimpleCounter. Objekt-analizator teksta je instanca klase TextAnalyzer:<br />

public class TextAnalyzer<br />

{<br />

private SimpleCounter cntr;<br />

private TextFile file;<br />

private boolean countDone;<br />

}<br />

Ispis 1.8: Analizator teksta<br />

public TextAnalyzer( SimpleCounter cntr, TextFile file )<br />

{<br />

this.cntr = cntr;<br />

this.file = file;<br />

}<br />

public int countLines()<br />

{<br />

if ( !this.countDone )<br />

{<br />

while ( !file.endReached() )<br />

{<br />

file.readLine();<br />

this.cntr.increment();<br />

}<br />

this.countDone = true;<br />

}<br />

return cntr.getState();<br />

}<br />

Najvaºnije je uo£iti da konstruktor klase prima kao parametre instance<br />

klasa SimpleCounter i TextFile. Moºemo u zapisu konstruktora vidjeti da<br />

se reference na te objekte izravno prepisuju u atribute cntr odnosno file.<br />

Klasa TextAnalyzer stoga ima asocijativnu vezu prema SimpleCounter-u i<br />

TextFile-u, ali ovdje se ne radi o kompoziciji, koju smo sreli ranije. To<br />

se vidi po tome ²to TextAnalyzer prima izvana stvorene objekte, tako da<br />

oni nisu njegovi privatni. Ovdje to moºe biti sasvim korisno: recimo da<br />

imamo jedan ve¢i dokument razbijen u vi²e datoteka, npr. jedna datoteka


12 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

za svako poglavlje. Ono ²to nas zanima je ukupan broj redaka u £itavom<br />

dokumentu. To moºemo posti¢i kori²tenjem na²eg analizatora teksta ovako<br />

(primjer pretpostavlja da imamo dokument razbijen u dvije datoteke):<br />

Ispis 1.9: Analiza vi²edijelnog dokumenta<br />

public static void main( String[] args )<br />

{<br />

TextFile file1 = new TextFile( 15 ); // datoteka od 15 redaka<br />

TextFile file2 = new TextFile( 25 ); // datoteka od 25 redaka<br />

SimpleCounter cntr = new SimpleCounter();<br />

}<br />

TextAnalyzer ta1 = new TextAnalyzer( cntr, file1 );<br />

System.out.println(<br />

"Broj redaka u prvoj datoteci: " + ta1.countLines() );<br />

// Instanciramo analizator druge datoteke, ali s istim broja£em:<br />

TextAnalyzer ta2 = new TextAnalyzer( cntr, file2 );<br />

System.out.println( "Ukupan broj redaka: " + ta2.countLines() );<br />

O£ekivani ispis:<br />

Broj redaka u prvoj datoteci: 15<br />

Ukupan broj redaka: 40<br />

Iz primjera je jasno da je ºivotni vijek objekta broja£a neovisan o<br />

objektu analizatora. Objekt analizatora, nakon ²to je obavio svoj posao,<br />

moºe se ukloniti iz memorije a da to ne utje£e na objekt broja£a. Za takav<br />

slu£aj koristi se izraz agregacija to je op¢enito odnos izmežu cjeline i njenih<br />

sastavnih dijelova, bez obzira na to pripadaju li dijelovi isklju£ivo toj<br />

cjelini ili ne. Uo£imo da kompozicija takožer zadovoljava ovu deniciju, ali<br />

je njena denicija stroºa. Dakle, svaka kompozicija je ujedno i agregacija,<br />

ali obrat ne vrijedi. UML-om se agregacija prikazuje kao na slici 1.4.<br />

TextFile 1 1<br />

TextAnalyzer<br />

-cntr<br />

SimpleCounter<br />

+countLines() : int<br />

-file<br />

+countLines() : int<br />

1<br />

1<br />

+increment()<br />

+getState() : int<br />

Slika 1.4: UML-ov dijagram klasa u odnosu agregacije<br />

U kodu klase TextAnalyzer koristi se objekt vrste TextFile. Umjesto punog<br />

objekta koji radi s pravim datotekama radi isprobavanja koristimo ovaj<br />

jednostavan nadomjestak:


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 13<br />

Ispis 1.10: Nadomjesna implementacija klase TextFile<br />

public class TextFile<br />

{<br />

private int linesTotal;<br />

private int linesRead;<br />

}<br />

public TextFile( int lineCount )<br />

{<br />

this.linesTotal = lineCount;<br />

}<br />

public void readLine()<br />

{<br />

if ( this.linesRead < this.linesTotal )<br />

this.linesRead++;<br />

}<br />

public boolean endReached()<br />

{<br />

return this.linesRead == this.linesTotal;<br />

}<br />

1.7 Polimorzam i Javino su£elje<br />

U Javi moºemo razdvojeno opisati su£elje objekta prema okolini (dakle<br />

popis metoda s kojima raspolaºe) i implementaciju tog su£elja (sam kod<br />

metoda, atributi potrebni za £uvanje stanja). Posebno je bitno da za jednu<br />

deniciju su£elja moºemo imati mnogo razli£itih implementacija. Ovo je<br />

korisno iz mnogo razloga, a ovdje ¢emo opisati jedan od njih.<br />

Recimo da imamo gornji slu£aj analizatora teksta. šelimo da, ovisno o<br />

konkretnoj primjeni, moºemo dobiti ili ukupan broj redaka ili samo njegovu<br />

posljednju znamenku (brojanje modulo 10, kao u ispisu 1.2). Treba uvesti<br />

samo sitnu izmjenu u kod s ispisa 1.8:<br />

public class TextAnalyzer<br />

{<br />

private Counter cntr;<br />

private TextFile file;<br />

private boolean countDone;<br />

Ispis 1.11: Polimorfni analizator teksta<br />

public TextAnalyzer( Counter cntr, TextFile file )<br />

{<br />

this.cntr = cntr;


14 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

}<br />

this.file = file;<br />

}<br />

public int countLines()<br />

{<br />

if ( !this.countDone )<br />

{<br />

while ( !file.endReached() )<br />

{<br />

file.readLine();<br />

this.cntr.increment();<br />

}<br />

this.countDone = true;<br />

}<br />

return cntr.getState();<br />

}<br />

Ovaj kod je identi£an kao ranije, osim ²to se umjesto SimpleCounter<br />

koristi vrsta Counter, £iju deniciju jo² nismo vidjeli. Radi se o Javinom<br />

su£elju:<br />

public interface Counter<br />

{<br />

void increment();<br />

int getState();<br />

}<br />

Ispis 1.12: Su£elje Counter<br />

U su£elju, kao ²to smo najavili, nema denicije metoda, ve¢ samo popis<br />

metoda koje objekt s tim su£eljem posjeduje, tj. deklaracije metoda. Konstruktor<br />

klase TextAnalyzer prihvatit ¢e bilo koji objekt koji implementira<br />

to su£elje, a metodi countLines bit ¢e svejedno o kakvoj se to£no implementaciji<br />

radi. Mežutim, razli£ite implementacije rezultirat ¢e razli£itim<br />

pona²anjem metode. Ova karakteristika objektno-orijentiranih jezika zove<br />

se polimorzam ili vi²eobli£nost. Metoda misli da uvijek radi s objektom<br />

iste vrste (Counter), koji se iz te perspektive doima kao da se mijenja od<br />

slu£aja do slu£aja jednom se moºe pona²ati kao SimpleCounter, a drugi<br />

put kao ModuloCounter. Uo£imo i jo² jednu sitnicu: metode u su£elju nisu<br />

ozna£ene kao public jer se to podrazumijeva nemogu¢e je u su£elju imati<br />

metodu koja ne bi bila javna. Klasu TextAnalyzer moºemo koristiti npr.<br />

ovako:<br />

Ispis 1.13: Polimorzam na djelu


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 15<br />

public static void main( String[] args )<br />

{<br />

TextAnalyzer ta1 =<br />

new TextAnalyzer( new SimpleCounter(), new TextFile( 37 ) );<br />

TextAnalyzer ta2 =<br />

new TextAnalyzer( new ModuloCounter(), new TextFile( 37 ) );<br />

}<br />

int count1 = ta1.countLines();<br />

int count2 = ta2.countLines();<br />

System.out.println( "SimpleCounter je izbrojao " + count1 );<br />

System.out.println( "ModuloCounter je izbrojao " + count2 );<br />

O£ekivani ispis:<br />

SimpleCounter je izbrojao 37<br />

ModuloCounter je izbrojao 7<br />

Prisutnost polimorzma moºemo uo£iti ve¢ u prvom retku koda. Konstruktoru<br />

klase TextAnalyzer, koji je deniran da prima argumente vrste<br />

Counter, proslježuje se referenca na instancu klase SimpleCounter. Jezi£na<br />

pravila to dopu²taju jer objekt SimpleCounter implementira su£elje Counter<br />

(uz sitan dodatak, prikazan u ispisu 1.15) i nad njim je mogu¢e pozvati sve<br />

metode deklarirane u su£elju. U drugom retku istom konstruktoru proslježuje<br />

se referenca na instancu druge klase ovaj put ModuloCounter. U tre¢em<br />

i £etvrtom retku najjasnije se vidi djelovanje polimorzma. Nad ta1 i ta2<br />

poziva se ista metoda s identi£nim argumentom. Time se pokre¢e identi£an<br />

kod metode countLines (ispis 1.11). Mežutim, ta dva poziva rezultiraju razli£itim<br />

povratnim vrijednostima, ²to se manifestira u ispisu u posljednja dva<br />

retka. Razlika nastupa u trenutku izvr²avanja retka this.cntr.increment(),<br />

gdje se donosi odluka koju to£no metodu increment() treba pozvati. U prvom<br />

slu£aju to ¢e biti metoda denirana u klasi SimpleCounter, a u drugom<br />

slu£aju u ModuloCounter. Dakle, taj redak propisuje samo ime metode koju<br />

¢e se pozvati (i parametre koje ona mora imati), ali ne veºe se ni uz jednu<br />

konkretnu metodu. Ovaj postupak zove se dinami£ko povezivanje za razliku<br />

od stati£kog, gdje se u trenutku prevoženja (kompajliranja) donosi<br />

kona£na odluka o tome koja metoda ¢e biti pozvana.<br />

Novu situaciju sa su£eljem moºemo vizualizirati UML-ovim dijagramom<br />

sa slike 1.5. Ovakav stil dizajna, u usporedbi s dizajnom sa slike 1.4, od<br />

presudne je vaºnosti u objektno-orijentiranim jezicima. Gotovo bi se moglo<br />

re¢i da je £injenica da omogu¢uju takav dizajn smisao njihovog postojanja.


16 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

Uz malu izmjenu na ispisu 1.13 moºemo pokazati jo² jedan tipi£an na£in<br />

kori²tenja polimorzma:<br />

Ispis 1.14: Jo² jedan primjer kori²tenja polimorzma<br />

public static void main( String[] args )<br />

{<br />

Counter cntr1 = new SimpleCounter();<br />

Counter cntr2 = new ModuloCounter();<br />

TextAnalyzer ta1 = new TextAnalyzer( cntr1 );<br />

TextAnalyzer ta2 = new TextAnalyzer( cntr2 );<br />

}<br />

... ostatak identi£an kao gore ...<br />

Ovdje se u prva dva retka varijablama vrste Counter pridjeljuju reference<br />

na instance klasa SimpleCounter odnosno ModuloCounter. Zatim se s tim varijablama<br />

poziva konstruktor klase TextAnalyzer. Ovaj uzorak, gdje se za<br />

vrstu varijable koristi su£elje umjesto konkretne klase, vrlo je uobi£ajen u<br />

praksi. Isto vrijedi i za vrste atributa. Su£elje se redovito koristi i, kao ²to<br />

smo vidjeli u primjeru, za vrste parametara a takožer i za vrste povratnih<br />

vrijednosti metoda. Na£elno, uvijek treba koristiti najop¢enitiju vrsta koja<br />

dolazi u obzir a to je u na²im primjerima su£elje Counter.<br />

TextAnalyzer<br />

+countLines() : int<br />

1<br />

-cntr<br />

«interface»<br />

Counter<br />

1 +increment()<br />

+getState() : int<br />

SimpleCounter<br />

ModuloCounter<br />

+increment()<br />

+getState() : int<br />

+increment()<br />

+getState() : int<br />

Slika 1.5: UML-ov dijagram klasa za polimorfni analizator teksta (klasa<br />

TextFile je ovdje izostavljena)<br />

U Javi svaka klasa mora se unaprijed obvezati koja sve su£elja (moºe ih<br />

biti vi²e) ¢e implementirati. U na²em slu£aju bit ¢e potrebno dodati redak<br />

implements Counter u deklaracije obiju klasa. Puni kod tako izmijenjenih


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 17<br />

klasa prikazan je na ispisu 1.15. Primijetimo i mali dodatak u konstruktoru<br />

klase ModuloCounter kojim se osigurava od slu£aja zadavanja prevelikog<br />

po£etnog broja uzimanjem ostatka dijeljenja s 10.<br />

Ispis 1.15: Obvezivanje na implementaciju su£elja<br />

public class SimpleCounter<br />

implements Counter<br />

{<br />

private int counterState;<br />

}<br />

public SimpleCounter( int initialState )<br />

{<br />

this.counterState = initialState;<br />

}<br />

public SimpleCounter()<br />

{<br />

}<br />

public void increment()<br />

{<br />

this.counterState++;<br />

}<br />

public int getState()<br />

{<br />

return this.counterState;<br />

}<br />

public class ModuloCounter<br />

implements Counter<br />

{<br />

private int counterState;<br />

public ModuloCounter( int initialState )<br />

{<br />

this.counterState = initialState % 10;<br />

}<br />

public ModuloCounter()<br />

{<br />

}<br />

public void increment()<br />

{<br />

if ( this.counterState == 9 )<br />

this.counterState = 0;<br />

else<br />

this.counterState++;


18 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

}<br />

}<br />

public int getState()<br />

{<br />

return this.counterState;<br />

}<br />

1.8 Izbjegavanje ponavljanja koda naslježivanjem<br />

Ono ²to nas u ispisu 1.15 moºe zasmetati, a u kompleksnijem kodu moºe<br />

prerasti i u puno ve¢i problem, je ponavljanje koda u dvije o£ito srodne<br />

klase. Obje klase koriste isti atribut, a i metoda getState im je identi£na.<br />

Java omogu¢uje tehniku kojom se takvo ponavljanje moºe izbje¢i naslježivanje<br />

(eng. inheritance). U na²em slu£aju mogli bismo postupiti tako<br />

da propi²emo da klasa ModuloCounter ne nastaje od nule, ve¢ da je izvedena<br />

(eng. derived) iz klase SimpleCounter. U tom slu£aju ona ¢e naslijediti<br />

sve njene metode i atribute. Nakon toga moºemo predenirati (nadja£ati,<br />

eng. override) one metode £ija implementacija vi²e ne odgovara (metodu<br />

increment). Izvodom se izmežu ModuloCounter i SimpleCounter uspostavlja<br />

odnos potklasa-natklasa.<br />

Konstruktori se ne naslježuju i moramo ih uvijek pisati iznova. Mežutim,<br />

u prvom retku konstruktora moºemo pozvati odgovaraju¢i konstruktor<br />

iz natklase i time prepustiti njemu odraživanje zajedni£kog dijela posla.<br />

public class SimpleCounter<br />

implements Counter<br />

{<br />

int counterState;<br />

Ispis 1.16: Naslježivanje osnovna klasa<br />

public SimpleCounter( int initialState )<br />

{<br />

this.counterState = initialState;<br />

}<br />

public SimpleCounter()<br />

{<br />

}<br />

public void increment()<br />

{


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 19<br />

}<br />

this.counterState++;<br />

}<br />

public int getState()<br />

{<br />

return this.counterState;<br />

}<br />

Ispis 1.17: Naslježivanje izvedena klasa<br />

public class ModuloCounter<br />

extends SimpleCounter<br />

{<br />

public ModuloCounter( int initialState )<br />

{<br />

super( initialState % 10 );<br />

}<br />

}<br />

public ModuloCounter()<br />

{<br />

}<br />

@Override<br />

public void increment()<br />

{<br />

super.increment();<br />

if ( this.counterState == 10 )<br />

this.counterState = 0;<br />

}<br />

U ovom slu£aju bili smo prisiljeni pro²iriti dostupnost atributu counter-<br />

State uklanjanjem modikatora private. Time je njegova dostupnost pro²irena<br />

na paketno-privatnu (eng. package-private) razinu: dostupan je iz svih<br />

klasa unutar istog paketa (o tome kasnije, u odjeljku 1.10), pa tako i iz<br />

potklase ModuloCounter. Prou£imo novu implementaciju metode increment<br />

u potklasi. Implementacija je izvedena tako da se prvo pozove izvorna implementacija<br />

iz natklase (super.increment), a zatim se korigira slu£aj kad<br />

broja£ dože do 10 i treba ga poni²titi. Ova tehnika je vrlo prikladna u u£estaloj<br />

situaciji iz prakse gdje je posao koji obavlja izvorna metoda ve¢inom<br />

ispravan, ali ga treba jo² malo korigirati ili pro²iriti. Op¢enito, nadja£avaju¢a<br />

metoda nema obvezu pozivati metodu iz natklase, a ako ju poziva,<br />

nije nuºno da to £ini u prvom retku koda. Poziv pomo¢u super koristi se i<br />

u zapisu konstruktora vi²e o ovome u odjeljku 2.12.3.


20 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

Iznad denicije metode increment u ModuloCounter-u pojavljuje se i @Override.<br />

To je tzv. Javin annotation, primjedba uz metodu. Postoje razne vrste<br />

annotation-a, a ovdje kori²ten Override nazna£uje da ta metoda nadja£ava<br />

metodu iz natklase. Njegovo kori²tenje je opcionalno, ali korisno. Time prevoditelju<br />

dajemo do znanja da predvižamo da ta metoda nadja£ava metodu<br />

iz natklase pa ¢e nas on upozoriti ako pogrije²imo i npr. navedemo krivo<br />

ime metode, recimo inkrement. Takve pogre²ke ponekad je te²ko uo£iti jer<br />

¢emo time umjesto nadja£avanja denirati novu metodu a da toga nismo<br />

svjesni.<br />

Uo£imo jo² i sljede¢e: klasa ModuloCounter ne ponavlja izjavu implements<br />

Counter jer se to podrazumijeva potklasa automatski, samom prirodom<br />

naslježivanja, implementira sva su£elja koja implementira njena natklasa.<br />

1.9 Klasna hijerarhija, apstraktna klasa<br />

«interface»<br />

Counter<br />

+increment()<br />

+getState() : int<br />

SimpleCounter<br />

-counterState : int<br />

+increment()<br />

+getState() : int<br />

ModuloCounter<br />

+increment()<br />

Slika 1.6: UML-ov dijagram klasa iz ispisa 1.16<br />

UML-ov dijagram slu£aja iz ispisa 1.16 prikazan je na slici 1.6. Ova organizacija<br />

je rije²ila problem ponavljanja koda. Mežutim, sad se pojavio novi<br />

problem. Dosad je bilo nagla²eno da polimorzam omogu¢uje instancama<br />

razli£itih klasa da se pona²aju kao da su instance su£elja koje implementiraju.<br />

Isto se odnosi i na slu£aj instance izvedene klase ona se moºe koristiti<br />

i kao instanca svoje natklase. U ovom slu£aju to zna£i da sve instance klase


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 21<br />

ModuloCounter moºemo tretirati kao da je njihova vrsta SimpleCounter. Ako<br />

npr. negdje imamo deklariranu metodu<br />

void setSimpleCounter( SimpleCounter cntr )<br />

ona ¢e uredno prihvatiti i argument vrste ModuloCounter. Mežutim, najvjerojatnije<br />

je da metoda o£ekuje objekt s pona²anjem deniranim u Simple-<br />

Counter i ne¢e ispravno raditi s druga£ijim objektima. U protivnom je mogla<br />

npr. zahtijevati argument op¢enite vrste Counter. Ovu situaciju moºemo izbje¢i<br />

uz uporabu vi²e koda, ali jo² uvijek zadrºavaju¢i tu prednost da se ne<br />

ponavlja deklaracija atributa ni implementacija metode getState. Moºemo<br />

u hijerarhiju ubaciti apstraktnu klasu:<br />

abstract class AbstractCounter<br />

implements Counter<br />

{<br />

int counterState;<br />

Ispis 1.18: Apstraktna klasa<br />

public AbstractCounter( int initialState )<br />

{<br />

this.counterState = initialState;<br />

}<br />

public AbstractCounter()<br />

{<br />

}<br />

public int getState()<br />

{<br />

return this.counterState;<br />

}<br />

}<br />

public abstract void increment();<br />

Glavna novost ovdje je metoda increment, koja je sad postala apstraktna.<br />

Najavljeno je njeno postojanje, ali nema implementacije. Logi£no slijedi da<br />

se ovakvu klasu ne smije mo¢i instancirati jer je opis njenog pona²anja<br />

nepotpun ne zna se ²to bi se trebalo dogoditi prilikom poziva metode<br />

increment. Upravo tu zabranu uvodi modikator abstract nad klasom. Op-<br />

¢enito, svaku klasu smije se proglasiti apstraktnom i time zabraniti njeno<br />

instanciranje bez obzira ima li ona apstraktnih metoda ili ne. S druge<br />

strane, postojanje ijedne apstraktne metode prisiljava nas da takvu klasu<br />

proglasimo apstraktnom.


22 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

Koji je smisao ovog zahvata? Pogledajmo preureženi kod dviju otprije<br />

poznatih klasa:<br />

Ispis 1.19: Konkretne klase izvedene iz apstraktne<br />

public class SimpleCounter<br />

extends AbstractCounter<br />

{<br />

public SimpleCounter( int initialState )<br />

{<br />

super( initialState );<br />

}<br />

}<br />

public SimpleCounter()<br />

{<br />

}<br />

@Override<br />

public void increment()<br />

{<br />

this.counterState++;<br />

}<br />

public class ModuloCounter<br />

extends AbstractCounter<br />

{<br />

public ModuloCounter( int initialState )<br />

{<br />

super( initialState % 10 );<br />

}<br />

}<br />

public ModuloCounter()<br />

{<br />

}<br />

@Override<br />

public void increment()<br />

{<br />

if ( this.counterState == 9 )<br />

this.counterState = 0;<br />

else<br />

this.counterState++;<br />

}<br />

Ovu situaciju prikazuje UML-ov dijagram na slici 1.7. Sad su obje konkretne<br />

klase izvedene iz AbstractCounter. Sav zajedni£ki kod nalazi se u apstraktnoj<br />

klasi, a metoda increment implementirana je u svakoj konkretnoj<br />

klasi posebno. Takva situacija bolje odgovara ulozi jedne i druge klase, koje


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 23<br />

su po svojoj prirodi dvije paralelne implementacije su£elja Counter. Uo£imo<br />

jo² na ispisu 1.18 da je za klasu AbstractCounter izostavljen modikator<br />

public. Ona ima pomo¢ni karakter i ne bi trebala sluºiti da se deklariraju<br />

varijable ili parametri £ija vrsta je AbstractCounter. Umjesto toga treba<br />

uvijek koristiti su£elje Counter. Stoga je njena vidljivost smanjena na samo<br />

unutar svog paketa.<br />

«interface»<br />

Counter<br />

+increment()<br />

+getState() : int<br />

AbstractCounter<br />

-counterState : int<br />

+increment()<br />

+getState() : int<br />

SimpleCounter<br />

ModuloCounter<br />

+increment()<br />

+increment()<br />

Slika 1.7: UML-ov dijagram klasa iz ispisa 1.18 i 1.19<br />

1.10 Paketi, imenovanje klasa i su£elja<br />

1.10.1 Koncepcija paketa<br />

Dosad se ve¢ vi²e puta spominjala koncepcija paketa. Vrijeme je da se<br />

razjasni taj pojam, a i da ga se stavi u potpunu uporabu u kodu. Jezik Java<br />

omogu¢ava grupiranje vi²e klasa u jednu cjelinu koja se naziva paketom.<br />

Pripadnost klase paketu objavljuje se u prvom retku datoteke u kojoj je<br />

denirana, npr. ovako:<br />

package hr.fer.tel.jsem;<br />

public interface Counter<br />

{<br />

void increment();<br />

int getState();<br />

}


24 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

Time se su£elje Counter na²lo u paketu hr.fer.tel.jsem i sada je njegovo<br />

puno ime hr.fer.tel.jsem.Counter. U kodu svih klasa unutar tog istog<br />

paketa ne mora se navoditi puno ime, ali u bilo kojem drugom paketu to<br />

postaje potrebno. Da kod ipak ne bi postao previ²e zagu²en ovako duga£kim<br />

imenima, Java podrºava i uvoz pojedinih imena klasa/su£elja tako da<br />

se na njih moºe pozivati samo kratkim imenom. Za to sluºi klju£na rije£<br />

import koja se pojavljuje odmah ispod deklaracije paketa:<br />

package hr.fer.tel.jsem;<br />

import java.util.List;<br />

import java.util.ArrayList;<br />

import javax.swing.*;<br />

public class Xyz<br />

{<br />

...<br />

}<br />

Kori²enjem zvjezdice, kao u javax.swing.*, uvozi se kompletan paket.<br />

Postoji i poseban paket java.lang koji je automatski uvezen, kao da je uvijek<br />

navedeno import java.lang.*. U njemu su najvaºnije ugražene klase kao<br />

²to su String i Object.<br />

1.10.2 Pravila imenovanja paketa<br />

U praksi se paketi redovito koriste, tako da sav na² dosada²nji kod u tom<br />

smislu odudara od prakse i svaku datoteku treba dopuniti tom deklaracijom.<br />

Ve¢ je vjerojatno o£ito da su na snazi i posebna pravila imenovanja<br />

paketa: on se imenuje prema imenu internetske domene organizacije pod<br />

£ijom kapom se izražuje kod, i to tako da se kre¢e od segmenta najvi²e<br />

hijerarhijske razine. Na² Zavod ima domenu tel.fer.hr i stoga sva imena<br />

paketa izraženih na Zavodu trebaju po£injati na hr.fer.tel. Za sasvim<br />

male projekte bit ¢e dovoljan jedan paket, ali ve¢ za samo malo ve¢e trebat<br />

¢e ih obi£no vi²e. U tom slu£aju opet ¢emo imati glavno ime, kao u na²em<br />

primjeru hr.fer.tel.jsem, na koje ¢e se dodavati jo² segmenata po potrebi.<br />

Paketi se primjenjuju prije svega zato da ne dože do sudara izmežu<br />

imena klasa koje kreiraju razli£iti autori i imaju razli£ite namjene. Upravo<br />

zato su denirana gornja pravila imenovanja paketa koja osiguravaju jedinstvenost<br />

njegovog imena. Iz istog razloga treba dobro paziti koja imena


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 25<br />

se uvoze da se ne bi uvezlo pogre²no. Na primjer, u standardnu biblioteku<br />

klasa koja dolazi s Javom ugraženi su i java.awt.List i java.util.List, dvije<br />

klase vrlo razli£itih namjena. Uvozom pogre²nog imena do¢i ¢e do vrlo £udnih<br />

pogre²aka gdje se prevoditelj (kompajler) buni za svaku metodu koju<br />

pozivamo, iako sve izgleda ispravno.<br />

1.10.3 Pravila za raspored datoteka po kazalima<br />

Ime paketa u kojem je klasa povla£i za sobom i propis u kojem kazalu<br />

ona mora biti smje²tena u sustavu datoteka. Po£ev²i od nekog proizvoljnog<br />

kazala koje se progla²ava korijenskim potrebno je izgraditi hijerarhiju<br />

kazala identi£nu hijerarhiji segmenata u imenu paketa. Konkretno, datoteka<br />

Counter.java mora se na¢i u kazalu /hr/fer/tel/jsem. Ime<br />

same datoteke takožer je propisano i mora biti jednako imenu klase deklarirane<br />

u njoj. Bez obzira na ovakav hijerarhijski na£in organiziranja paketa<br />

po kazalima, formalno izmežu dva paketa razli£itog imena ne postoji nikakva<br />

povezanost. Dakle, paket hr.fer nije u ni²ta bliºem odnosu s paketom<br />

hr.fer.tel nego s npr. paketom com.sun.<br />

Svaki razvojni alat automatski se brine za po²tivanje svih ovih pravila<br />

i stoga je bitno koristiti tu njegovu podr²ku. Na primjer, u ovom trenutku<br />

trebalo bi sve datoteke, koje su dosad bile u neimenovanom, tzv. podrazumijevanom<br />

ili korijenskom paketu, preseliti u paket hr.fer.tel.jsem. Jedan<br />

zgodan na£in u okruºju Eclipse je da se u Package Explorer-u ozna£i sve<br />

klase i pritisne kombinacija tipaka Alt-Shift-V, £ime se zahtijeva preseljenje<br />

klasa u drugi paket. Doti£ni jo² ne postoji pa ga se u prozoru koji se<br />

pojavio stvori pomo¢u gumba New.<br />

1.10.4 Uloga paketno-privatne razine dostupnosti<br />

Sad se moºe bolje razumjeti ono ²to se ranije govorilo o razini dostupnosti<br />

atributa, metoda i klasa. Svaki razvojni projekt u Javi oslanja se na velik<br />

broj klasa i su£elja koja ve¢ postoje mnogo njih dolazi ve¢ kao dio samog<br />

jezika, odnosno dostupan je odmah nakon instaliranja Jave na ra£unalo.<br />

Ve¢ina samih softverskih proizvoda takožer su takve prirode da trebaju<br />

sluºiti drugim razvijateljima kao gradivni elementi. Korisniku svih tih paketa<br />

ne¢e biti dostupni oni dijelovi koji nisu ozna£eni kao javni (public).<br />

Time razvijatelj paketa moºe imati bolju kontrolu nad time kako ¢e se


26 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

njegov kod koristiti: olak²ava korisniku ispravno kori²tenje, a i zadrºava<br />

slobodu kasnijih izmjena nad svim dijelovima koji nisu javni. Na primjer,<br />

uklanjanje oznake public s klase AbstractCounter sprije£it ¢e korisnka na²eg<br />

paketa da radi izravno s tom klasom. Kasnije ju moºemo npr. preimenovati<br />

ili moºda potpuno ukinuti bez utjecaja na korisnika na²eg paketa.<br />

1.11 Refaktorizacija<br />

Kako smo napredovali u izgradnji koda na²eg primjera, u vi²e navrata radili<br />

smo prepravke u kodu s ciljem njegove bolje organizacije (npr. izbjegavanje<br />

ponavljanja), a bez utjecaja na pona²anje objekata. Takve izmjene u£estale<br />

su u razvoju softvera i nazivaju se refaktorizacijom. Gledaju¢i trenutno<br />

stanje na²eg koda (ispisi 1.18 i 1.19), opet moºemo uo£iti jednu sitnicu<br />

koja bi se mogla popraviti. Klasa ModuloCounter na dva neovisna mjesta<br />

name¢e ciklus brojanja od nula do devet: posebno u konstruktoru i u metodi<br />

increment. Takva situacija otvara mogu¢nost za nekonzistentnost moºe se<br />

gre²kom dogoditi da se na jednom mjestu name¢e npr. ciklus od nula do 7,<br />

a na drugom do 9. Poºeljno je provesti jo² jednu refaktorizaciju koja ¢e ovo<br />

otkloniti. Vi²e je na£ina za to, ali ovdje ¢emo pokazati jedan koji sadrºi i<br />

neke novosti u dizajnu objekata.<br />

Dosad smo se susreli s enkapsulacijom atributa kao jednom od najosnovnijih<br />

tehnika dizajna. Mežutim, £esto je korisno enkapsulirati i metode. To<br />

¢emo ovdje iskoristiti: uvest ¢emo novu, paketno-privatnu metodu setState<br />

koja ¢e se pozivati prilikom svake promjene stanja i u kojoj ¢e se provoditi<br />

kontrola nailaska na gornju granicu ciklusa. Dakle, svaka od dviju na²ih<br />

konkretnih klasa imat ¢e svoju implementaciju setState, a time ¢e nestati<br />

potreba da metoda increment ima razli£ite implementacije ona ¢e se oslanjati<br />

na promjenjivo pona²anje metode setState. Za takvu metodu kaºe<br />

se da je polimorfna. Konstruktor ¢e takožer pozivati ovu metodu. Metoda<br />

setState nije dio javnog su£elja objekta i stoga se denicija Counter-a ne<br />

mijenja. Iz perspektive koda koji barata objektima vrste Counter nije se<br />

dogodila nikakva promjena.<br />

1.11.1 Modikator final kontrola polimorzma<br />

U novi kod apstraktne klase dodan je jo² jedan detalj: modikatorom final<br />

ozna£ene su metode koje je zabranjeno nadja£avati. Uvijek je dobro svaku


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 27<br />

metodu za koju je predviženo da bude ista za sve potklase ozna£iti kao<br />

final. Polimorzam, osim ²to daje eksibilnost, moºe i oteºati £itanje koda<br />

upravo zbog toga ²to pravila odabira metode koja ¢e biti pozvana postaju<br />

znatno sloºenija. Oznaka final na metodi garantira da nije potrebno<br />

pregledavati sve potklase da se ustanovi nije li neka od njih nadja£ala tu<br />

metodu. To je prakti£nije i za programera koji dodaje novu potklasu jer<br />

taj modikator isti£e ulogu metode u £itavoj organizaciji klasa i ukazuje<br />

na to kako ispravno uvesti novu klasu.<br />

package hr.fer.tel.jsem;<br />

abstract class AbstractCounter<br />

implements Counter<br />

{<br />

int counterState;<br />

Ispis 1.20: Nova verzija apstraktne klase<br />

public AbstractCounter( int initialState )<br />

{<br />

setState( initialState );<br />

}<br />

public AbstractCounter()<br />

{<br />

}<br />

public final int getState()<br />

{<br />

return this.counterState;<br />

}<br />

public final void increment()<br />

{<br />

setState( getState() + 1 );<br />

}<br />

}<br />

abstract void setState( int newState );<br />

U ispisima koji slijede modikatorom final ozna£ene su klase. Kad djeluje<br />

na klasu, ovaj modikator zabranjuje deklaraciju klase izvedene iz nje.<br />

Time se ponovo osigurava od polimorzma tamo gdje je on nepoºeljan <br />

ako npr. parametar neke metode ima vrstu SimpleCounter, garantirano je<br />

da ¢e prosliježeni objekt biti instanca upravo te klase, a ne neke potklase s<br />

izmijenjenim pona²anjem. Takav nepoºeljan slu£aj upravo smo imali ranije,<br />

prije uvoženja apstraktne klase.


28 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

package hr.fer.tel.jsem;<br />

Ispis 1.21: Nove konkretne klase<br />

public final class SimpleCounter<br />

extends AbstractCounter<br />

{<br />

public SimpleCounter( int initialState )<br />

{<br />

super( initialState );<br />

}<br />

}<br />

public SimpleCounter()<br />

{<br />

}<br />

@Override<br />

void setState( int newState )<br />

{<br />

this.counterState = newState;<br />

}<br />

package hr.fer.tel.jsem;<br />

public final class ModuloCounter<br />

extends AbstractCounter<br />

{<br />

public ModuloCounter( int initialState )<br />

{<br />

super( initialState );<br />

}<br />

}<br />

public ModuloCounter()<br />

{<br />

}<br />

@Override<br />

void setState( int newState )<br />

{<br />

this.counterState = newState % 10;<br />

}<br />

1.12 Uvoženje vi²e varijanti iste metode preoptere¢enje<br />

Ovisno o tome za ²to nam sve treba broja£, mogao bi se pojaviti npr.<br />

zahtjev da treba omogu¢iti i ve¢e korake brojanja umjesto samo po jedan.


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 29<br />

U tom slu£aju mogli bismo poja£ati na²e objekte novom varijantom metode<br />

increment koja prima i jedan brojevni parametar. Tada ¢emo imati dvije<br />

metode istog imena, ali razli£itih parametara to se zove preoptere¢enjem<br />

metode (eng. overloading). ’to se ti£e jezika Java, te dvije metode jednako<br />

su razli£ite kao da imaju i razli£ita imena jer se potpuni identitet metode<br />

sastoji od njenog imena i popisa vrsta parametara koje prima. Ta cjelina<br />

naziva se potpisom metode (eng. signature). Konkretno, ovdje ¢emo imati<br />

metode increment() i increment( int ). Uo£imo da s druge strane povratna<br />

vrijednost ne ulazi u potpis metode i ne smije se denirati dvije metode<br />

koje su iste po svemu osim po povratnoj vrijednosti.<br />

public interface Counter<br />

{<br />

void increment();<br />

void increment( int step );<br />

int getState();<br />

}<br />

package hr.fer.tel.jsem;<br />

Ispis 1.22: Pro²ireno su£elje Counter<br />

Ispis 1.23: Pro²irenje apstraktne klase<br />

abstract class AbstractCounter<br />

implements Counter<br />

{<br />

.. po£etak koda identi£an kao ranije ...<br />

}<br />

public final void increment()<br />

{<br />

increment( 1 );<br />

}<br />

public final void increment( int step )<br />

{<br />

setState( getState() + step );<br />

}<br />

abstract void setState( int newState );<br />

Moºemo uo£iti tipi£an pristup koji se koristi u izgradnji sustava preoptere¢enih<br />

metoda: metoda koja prima cijeli broj je op¢enitija, a metoda<br />

bez parametara je u biti njen poseban slu£aj u kojem se podrazumijeva jedini£ni<br />

korak. Stoga se poziv metode bez parametara delegira na op¢enitiju<br />

metodu, koju se poziva s podrazumijevanom vrijednosti.


30 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

Primjer kori²tenja preoptere¢enih metoda:<br />

public static void main( String[] args )<br />

{<br />

Counter cntr = new SimpleCounter();<br />

cntr.increment();<br />

System.out.println( "Stanje nakon poziva increment(): "<br />

+ cntr.getState() );<br />

cntr.increment( 2 );<br />

System.out.println( "Stanje nakon poziva metode increment( 2 ): "<br />

+ cntr.getState() );<br />

}<br />

1.13 Prijelaz s agregacije na kompoziciju kopiranje<br />

objekata<br />

U ispisu 1.11 denirana je klasa koja primjenjuje agregaciju, ²to je ilustrirano<br />

slikom 1.4. Konstruktor klase TextAnalyzer zaprima gotovu instancu<br />

broja£a i ugražuje ju u svoju instancu. U ve¢ini situacija iz prakse ovdje<br />

¢e ustvari isto biti potrebna prava kompozicija da bi se analizator teksta<br />

za²titio od neºeljenih intervencija izvana. Ako ºelimo imati oba svojstva <br />

i da se objekt dobiva izvana, i da se primjenjuje £ista kompozicija bit<br />

¢e potrebno omogu¢iti kopiranje objekata. Analizator teksta zaprimit ¢e<br />

objekt izvana i od njega napraviti obrambenu kopiju (eng. defensive copy)<br />

identi£an objekt, ali privatan. Daljnje promjene nad objektom dobivenim<br />

izvana ne¢e utjecati na njegov rad. U Javi postoji poseban mehanizam za<br />

ovo metoda clone mežutim, on se zbog odreženih tehni£kih problema<br />

po£eo izbjegavati. Ovdje ¢emo stoga prikazati preporu£enu alternativu <br />

kopiraju¢i konstruktor (eng. copy constructor) u kombinaciji s posebnom<br />

metodom getCopy. Su£elje broja£a moramo pro²iriti ovom metodom:<br />

Ispis 1.24: Dodatak metode za kopiranje u su£elje Counter<br />

public interface Counter<br />

{<br />

void increment();<br />

void increment( int step );<br />

int getState();<br />

Counter getCopy();<br />

}<br />

Implementacije ove metode koristit ¢e kopiraju¢e konstruktore. Ovdje se<br />

donosi primjer za SimpleCounter; za ModuloCounter postupa se analogno.


POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 31<br />

Ispis 1.25: Klasa SimpleCounter s dodanom podr²kom za kopiranje<br />

package hr.fer.tel.jsem;<br />

public final class SimpleCounter<br />

extends AbstractCounter<br />

{<br />

public SimpleCounter( int initialState )<br />

{<br />

super( initialState );<br />

}<br />

}<br />

public SimpleCounter()<br />

{<br />

}<br />

// Kopiraju¢i konstruktor -- broja£ se postavlja na<br />

// istu vrijednost kao u izvorniku.<br />

// Privatan! Sluºi samo kao podr²ka metodi getCopy<br />

private SimpleCounter( SimpleCounter cntr )<br />

{<br />

this.counterState = cntr.counterState;<br />

}<br />

public Counter getCopy()<br />

{<br />

return new SimpleCounter( this );<br />

}<br />

void setState( int newState )<br />

{<br />

this.counterState = newState;<br />

}<br />

Metodu getCopy, naºalost, ne moºemo rije²iti op¢enito i staviti u apstraktnu<br />

klasu jer u svakoj klasi treba stvarati instancu upravo te klase.<br />

Klasu TextAnalyzer jednostavno je prepraviti da koristi mehanizam kopiranja.<br />

Potrebna je samo sitna izmjena u konstruktoru 1 :<br />

Ispis 1.26: Analizator teksta koji koristi kompoziciju<br />

public class TextAnalyzer<br />

{<br />

private TextFile file;<br />

private Counter cntr;<br />

public TextAnalyzer( TextFile file, Counter cntr )<br />

1 U konstruktoru se koristi i metoda getCopy iz klase TextFile. Njena implementacija<br />

nije prikazana u skripti.


32 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />

}<br />

{<br />

}<br />

this.file = file.getCopy();<br />

this.cntr = cntr.getCopy();<br />

public int countLines()<br />

{<br />

while ( !file.endReached() )<br />

{<br />

file.readLine();<br />

this.cntr.increment();<br />

}<br />

return cntr.getState();<br />

}


Poglavlje 2<br />

Razrada detalja jezi£nih<br />

svojstava<br />

2.1 Varijabla, referenca, objekt<br />

Jedan od najbitnijih elemenata potrebnih za precizno razumijevanje jezi£nih<br />

konstrukcija u Javi jest pojam varijable. Ono ²to ¢e ovdje biti re£eno<br />

za varijablu vrijedi i za parametre metode, uhva¢ene iznimke i atribute<br />

objekta. Jedna od razlika izmežu lokalne varijable i atributa koju treba<br />

imati na umu je da se atribut inicijalizira automatski, a lokalna varijabla<br />

ne. Prevoditelj (kompajler) ¢e analizirati izvorni kod i prijaviti pogre²ku<br />

ako kod omogu¢ava £itanje varijable prije inicijalizacije.<br />

Varijabla je spremnik u radnoj memoriji kojem je dodijeljeno ime (identikator)<br />

i vrsta podatka koji se u nju sprema. Jezik Java razlikuje varijable<br />

primitivne i referentne vrste.<br />

2.1.1 Varijabla primitivne vrste<br />

Varijabla primitivne vrste je jednostavniji od dva slu£aja i poznat je iz jezika<br />

C. Vrijednost takve varijable je podatak primitivne vrste, npr. boolean,<br />

int ili double. Sam podatak je izravno spremljen u varijablu i vrijednost varijable<br />

moºe se izmijeniti jednostavnom dodjelom:<br />

int i; //deklaracija varijable<br />

i = 2; //prva dodjela vrijednosti (inicijalizacija)<br />

i = 3; //druga dodjela vrijednosti ...<br />

Atributi primitivne vrste inicijaliziraju se na vrijednost nula odnosno, za<br />

logi£ku vrstu, na false. Varijable se usporežuju po vrijednosti na uobi£ajen<br />

33


34 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

na£in:<br />

int i = 0;<br />

int j = 0;<br />

if ( i == j )<br />

System.out.println( "Varijable imaju istu vrijednost" );<br />

else<br />

System.out.println( "Varijable imaju razli£ite vrijednosti" );<br />

(ispisat ¢e se "Varijable imaju istu vrijednost"). Ako varijablu proslijedimo<br />

kao argument metodi, metoda ¢e dobiti vlastitu kopiju vrijednosti varijable,<br />

tako da promjene na toj kopiji ne utje£u na original:<br />

private static void useInt( int i )<br />

{<br />

i++;<br />

}<br />

public static void main( String[] args )<br />

{<br />

int i = 0;<br />

useInt( i );<br />

System.out.println( i ); // ispisuje "0"<br />

}<br />

2.1.2 Varijabla referentne vrste<br />

U varijablu referentne vrste sprema se referenca na objekt. Vrijednost varijable<br />

nikad nije sam objekt, ve¢ uvijek referenca na njega. To je vaºno<br />

imati na umu, pogotovo u sljede¢im situacijama:<br />

• Dvije varijable mogu imati referencu na isti objekt. Manipulacija<br />

objektom putem jedne varijable manifestirat ¢e se i kad se objektu<br />

pristupa putem druge varijable:<br />

Counter cntr1 = new SimpleCounter();<br />

Counter cntr2 = cntr1;<br />

System.out.println( cntr1.getState() ); // ispisuje "0"<br />

cntr2.increment();<br />

System.out.println( cntr1.getState() ); // ispisuje "1"<br />

• Ako se metodi proslježuje objekt putem parametra referentne vrste,<br />

akcije nad objektom obavljene unutar metode ostat ¢e vidljive na<br />

objektu i nakon ²to metoda zavr²i:


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 35<br />

private static void useCntr( Counter cntr )<br />

{<br />

cntr.increment();<br />

}<br />

public static void main( String[] args )<br />

{<br />

Counter cntr1 = new SimpleCounter();<br />

System.out.println( cntr1.getState() ); // ispisuje "0"<br />

useCntr( cntr1 );<br />

System.out.println( cntr1.getState() ); // ispisuje "1"<br />

}<br />

• Vrijednost dviju varijabli je jednaka samo ako sadrºe istu referencu, tj.<br />

pokazuju na jedan te isti objekt. Ako pokazuju svaka na svoj objekt,<br />

njihove vrijednosti ne¢e biti jednake bez obzira na to jesu li sami<br />

objekti mežusobno jednaki:<br />

public static void main( String[] args )<br />

{<br />

Counter cntr1 = new SimpleCounter();<br />

Counter cntr2 = new SimpleCounter();<br />

if ( cntr1 == cntr2 )<br />

System.out.println( "Varijable imaju istu vrijednost" );<br />

else<br />

System.out.println( "Varijable imaju razli£ite vrijednosti" );<br />

}<br />

(ispisat ¢e se Varijable imaju razli£ite vrijednosti). Za usporedbu<br />

samih objekata mora se koristiti posebnu metodu equals (odjeljak<br />

3.2.1).<br />

Varijabla referentne vrste moºe imati i posebnu vrijednost null, ²to<br />

zna£i da ne sadrºi nikakvu referencu. Atributi referentne vrste se automatski<br />

inicijaliziraju na ovu vrijednost.<br />

2.1.3 Opseg vidljivosti (scope) lokalne varijable<br />

Lokalna varijabla uobi£ajeno se deklarira unutar glavnog bloka metode.<br />

Mežutim, op¢enito njena vidljivost proteºe se uvijek od mjesta deklaracije<br />

do kraja bloka u kojem je deklarirana:<br />

public static void method()<br />

{<br />

int i = 0;


36 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

}<br />

if ( i >= 0 )<br />

{<br />

int j = i + 1;<br />

}<br />

j++; // prevoditelj prijavljuje pogre²ku da nije deklarirana<br />

Nijedna deklaracija lokalne varijable ne smije prekriti drugu lokalnu<br />

varijablu koja je na tom mjestu vidljiva:<br />

public static void method()<br />

{<br />

int i = 0;<br />

if ( i >= 0 )<br />

{<br />

int i = 3; // prevoditelj se tuºi da je varijabla 'i' ve¢ deklarirana<br />

}<br />

}<br />

Sintaksa petlje for omogu¢uje da se ve¢ prije otvorene vitice deklarira<br />

varijabla koja ¢e vrijediti unutar bloka petlje:<br />

public static void method()<br />

{<br />

for ( int i = 0; i < 33; i++ )<br />

{<br />

System.out.println( i % 7 );<br />

}<br />

i++; // prevoditelj prijavljuje pogre²ku da nije deklarirana<br />

}<br />

2.2 Primitivne vrste podataka<br />

Jezik Java raspolaºe ograni£enim skupom primitivnih vrsta podataka. Ne<br />

raspolaºe npr. brojevima bez predznaka. Denirane su sljede¢e vrste:<br />

• boolean: logi£ka vrijednost, poprima jednu od dvije konstante: true ili<br />

false.<br />

• byte: 8-bitni cijeli broj (zapisan u jednom oktetu).<br />

• short: 16-bitni cijeli broj.<br />

• char: 16-bitni Unicode-ov znak. Moºe se tretirati i kao broj. Podrºane<br />

su doslovne vrijednosti, npr. 'a', '\n' za znak prelaska u novi redak,<br />

'\t' za znak tabulatora.


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 37<br />

• int: 32-bitni cijeli broj, koristi se za sve cijele brojeve op¢e namjene,<br />

kad nema posebno jakih razloga za kori²tenje nekog drugog.<br />

• long: 64-bitni cijeli broj.<br />

• float: 32-bitni broj s plivaju¢im zarezom. Preciznost je oko 6 decimalnih<br />

mjesta.<br />

• double: 64-bitni broj s plivaju¢im zarezom. Preciznost je oko 15 decimalnih<br />

mjesta. Najbolje je rutinski koristiti double za decimalne<br />

brojeve.<br />

2.3 Numeri£ki i logi£ki izrazi<br />

Jezik Java raspolaºe uobi£ajenim skupom unarnih i binarnih numeri£kih i<br />

logi£kih operatora. Ovdje se donosi njihov kratak pregled.<br />

2.3.1 Numeri£ki operatori<br />

Unarni operatori:<br />

• negacija broja, npr -i<br />

• komplement broja bit-po-bit, npr. £i<br />

• pre-increment, npr. ++i<br />

• pre-decrement, npr. --i<br />

• post-increment, npr. i++<br />

• post-decrement, npr. i--<br />

Binarni operatori:<br />

• zbroj, npr. i + j; isti znak koristi se i za operator ulan£avanja (vidjeti<br />

3.3)<br />

• razlika, npr. i - j<br />

• umnoºak, npr. i * j


38 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

• koli£nik, npr. i / j. Ako su i i j cijeli brojevi, rezultat ¢e biti cijeli<br />

broj puta koliko j stane u i. Drugim rije£ima, rezultat se zaokruºuje<br />

prema nuli odnosno na manju apsolutnu vrijednost.<br />

• ostatak dijeljenja, npr. i % j. Pravilo za rezultat operacije: (i/j)*j +<br />

(i%j) = i<br />

Binarni operatori koji djeluju na razini bitova:<br />

• pomak (shift) ulijevo, npr. i > j. Na ispraºnjena lijeva<br />

mjesta stavlja se vrijednost koju je imao najljeviji bit prije operacije.<br />

Koristi se kad se nad cijelim brojem, bio pozitivan ili negativan, ºeli<br />

posti¢i efekt dijeljenja s 2 j .<br />

• pomak udesno uz zero-extension, npr. i >>> j. Na ispraºnjena lijeva<br />

mjesta stavljaju se nule. Za negativne brojeve naru²ava efekt dijeljenja<br />

potencijom broja dva.<br />

• operacija i, npr. i & j<br />

• operacija ili, npr. i | j<br />

• operacija isklju£ivo ili, npr. i ¢ j<br />

Za usporedbu brojeva koriste se operatori == (jednakost), != (nejednakost),<br />


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 39<br />

• isklju£ivo ili, npr. a ¢ b<br />

• uvjetno i, npr. a && b. Izraz b ¢e se izra£unavati jedino ako je izraz a<br />

bio istinit.<br />

• uvjetno ili, npr. a || b. Izraz b ¢e se izra£unavati jedino ako je izraz<br />

a bio laºan.<br />

Osim za posebne namjene treba uvijek koristiti uvjetne verzije operatora<br />

jer je to ra£unski ekonomi£nije. Jedino u slu£aju kad je nuºno da oba<br />

izraza uvijek budu izra£unata (zbog nekih dodatnih posljedica koje ti izrazi<br />

ostavljaju) treba koristiti bezuvjetne operatore.<br />

2.3.3 Uvjetni (ternarni) operator<br />

Ovaj operator sluºi za odabir izmežu dva izraza od kojih ¢e jedan biti<br />

rezultat £itave operacije ovisno o zadanom uvjetu. Tipi£an primjer uporabe:<br />

public static void showErrorMessage( String msg )<br />

{<br />

String fullMsg = "Error: " + ( msg != null? msg : "" );<br />

System.out.println( fullMsg );<br />

}<br />

Ternarni operator koristi se u izrazu msg != null? msg : "". Prvo se provjerava<br />

uvjet msg != null. Ako je uvjet zadovoljen, rezultat £itavog izraza<br />

je msg, a u protivnom je rezultat prazan niz, "".<br />

Ovaj operator nije primjereno koristiti kao op¢enitu zamjenu za<br />

konstrukciju if-then-else, ve¢ samo u slu£ajevima kao gore, gdje se radi o<br />

sasvim kratkim izrazima i gdje se time postiºe bolja £itljivost koda.<br />

2.3.4 Operatori sloºene dodjele<br />

Za sve binarne operatore za koje je to smisleno postoje i odgovaraju¢i operatori<br />

sloºene dodjele, npr. i += 3 postoje¢oj vrijednosti varijable i dodaje<br />

3, a &= b varijabli a pridjeljuje rezultat operacije a & b, itd.<br />

2.3.5 Pretvorbe izmežu numeri£kih vrsta<br />

Ako se u numeri£kom izrazu koriste podaci razli£itih vrsta npr. int i<br />

long, svi podaci niºe preciznosti bit ¢e implicitno pretvoreni u podatke vi²e<br />

preciznosti. Razmotrimo ovaj primjer kori²tenja cijelih brojeva:


40 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

Ispis 2.1: Implicitna pretvorba vrste cjelobrojnih podataka<br />

int int1 = 1;<br />

long long1 = 1;<br />

long long2 = int1;<br />

long2 = int1 + long1;<br />

int int2 = long1; // pogre²ka!<br />

int2 = int1 + long1; // pogre²ka!<br />

U tre¢em i £etvrtom retku vrijednost vrste int se pridjeljuje varijabli vrste<br />

long i to je ispravno. Mežutim, u petom i ²estom retku poku²ava se vrijednost<br />

vrste long pridijeliti varijabli vrste int. Ovdje prevoditelj prijavljuje<br />

pogre²ku Type mismatch: cannot convert from long to int.<br />

Java op¢enito po²tuje sljede¢e pravilo: ako zatreba pretvorba na ve¢u<br />

preciznost, odvija se automatski; ako zatreba pretvorba na manju preciznost,<br />

potrebno je ekplicitno zatraºiti konverziju. Pogre²ke iz gornjeg primjera<br />

ispravit ¢emo ovako:<br />

Ispis 2.2: Eksplicitna pretvorba vrste cjelobrojnih podataka<br />

int int2 = (int) long1;<br />

int2 = (int) ( int1 + long1 );<br />

Sad prevoditelj ne¢e prijaviti pogre²ku, ali naravno treba imati na umu<br />

opasnost od prekora£enja maksimalnog opsega int-a. Sve ovdje re£eno vrijedi<br />

i za odnose izmežu float i double, kao i izmežu float i double s jedne<br />

strane i cjelobrojnih vrsta s druge strane.<br />

2.4 Enumeracija<br />

U softverskom razvoju vrlo £est slu£aj je da neki podatak moºe poprimiti<br />

svega nekoliko razli£itih vrijednosti. Na primjer, ako razvijamo neku karta²ku<br />

igru, trebat ¢e podatak s £etiri mogu¢e vrijednosti (pik, karo, herc,<br />

tref ). Za takve slu£ajeve treba denirati posebnu klasu, tzv. enumeraciju.<br />

Najosnovniji slu£aj je vrlo jednostavan:<br />

package hr.fer.tel.jsem;<br />

public enum Suit<br />

Ispis 2.3: Jednostavna enumeracija


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 41<br />

{<br />

}<br />

clubs, diamonds, hearts, spades;<br />

Enumeracija je ustvari obi£na klasa s javnim konstantama (vidjeti odjeljak<br />

2.8) £ija vrsta je upravo ta klasa. To moºemo vidjeti na sljede¢em<br />

primjeru kori²tenja enumeracije:<br />

Ispis 2.4: Osnovno kori²tenje enumeracije<br />

public static void main( String[] args )<br />

{<br />

Suit suit1, suit2;<br />

suit1 = Suit.clubs;<br />

suit2 = Suit.hearts;<br />

System.out.println( "Prva boja: " + suit1 + "; druga boja: " + suit2 );<br />

}<br />

Odmah moºemo uo£iti da £lanovi enumeracije imaju ispravno implementiranu<br />

metodu toString koja vra¢a ime £lana.<br />

Konstrukcija switch op¢enito se ne moºe koristiti za referentne vrste, ali<br />

posebno je omogu¢ena za enumeraciju, ²to znatno olak²ava zapis koda kao<br />

u sljede¢em primjeru:<br />

Ispis 2.5: Kori²tenje enumeracije u bloku switch<br />

static void printSuitInCroatian( Suit suit )<br />

{<br />

String suitName;<br />

switch ( suit )<br />

{<br />

case clubs: suitName = "tref"; break;<br />

}<br />

case diamonds: suitName = "karo"; break;<br />

case hearts: suitName = "herc"; break;<br />

case spades: suitName = "pik"; break;<br />

default: suitName = "";<br />

}<br />

System.out.println( "Boja karte je " + suitName );<br />

public static void main( String[] args )<br />

{<br />

printSuitInCroatian( Suit.clubs );<br />

printSuitInCroatian( Suit.hearts );<br />

}<br />

Da nema mogu¢nosti kori²tenja bloka switch, morali bismo pisati ovako:


42 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

if ( suit == Suit.clubs )<br />

suitName = "tref";<br />

else if ( suit == Suit.diamonds )<br />

suitName = "karo";<br />

else if ( suit == Suit.hearts )<br />

suitName = "herc";<br />

else if ( suit == Suit.spades )<br />

suitName = "pik";<br />

else<br />

suitName = "";<br />

Primijetimo da je £lanove enumeracije uvijek sigurno usporeživati po referenci,<br />

operatorom ==, jer je njihova priroda takva da su svaka dva distinktna<br />

£lana mežusobno razli£iti. Dodatna prednost konstrukcije switch je u tome<br />

²to omogu¢uje prevoditelju da nas upozori ako smo slu£ajno izostavili neki<br />

£lan enumeracije.<br />

Sve enumeracije implementiraju i su£elje Comparable (vidjeti odjeljak<br />

5.6.1). Izmežu £lanova enumeracije utvržen je poredak koji odgovara poretku<br />

kojim su navedeni u deniciji enumeracije. To nam omogu¢uje da<br />

napi²emo npr. ovakav kod:<br />

public static void main( String[] args )<br />

{<br />

Suit suit1, suit2;<br />

suit1 = Suit.clubs;<br />

suit2 = Suit.hearts;<br />

int difference = suit1.compareTo( suit2 );<br />

if ( difference == 0 )<br />

System.out.println( "Boje su iste" );<br />

else if ( difference > 0 )<br />

System.out.println( "Prva boja pobježuje!" );<br />

else<br />

System.out.println( "Druga boja pobježuje!" );<br />

}<br />

Kao i ostale klase, i enumeraciju je mogu¢e pro²irivati dodatnim atributima<br />

i metodama. U na²em slu£aju mogli bismo npr. dodati hrvatsko ime<br />

boje:<br />

package hr.fer.tel.jsem;<br />

public enum Suit<br />

{<br />

clubs( "tref" ),<br />

diamonds( "karo" ),<br />

Ispis 2.6: Oboga¢ena enumeracija


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 43<br />

}<br />

hearts( "herc" ),<br />

spades( "pik" );<br />

private String croName;<br />

private Suit( String name )<br />

{<br />

this.croName = name;<br />

}<br />

public String getCroName()<br />

{<br />

return this.croName;<br />

}<br />

Ovdje smo dodali instancin atribut croName, odgovaraju¢i (privatni!)<br />

konstruktor i metodu getCroName. Uz ovako deniranu enumeraciju izvedba<br />

metode printSuitInCroatian postaje trivijalna:<br />

static void printSuitInCroatian( Suit suit )<br />

{<br />

System.out.println( "Boja karte je " + suit.getCroName() );<br />

}<br />

2.5 Konstrukcije za upravljanje slijedom izvr-<br />

²avanja<br />

Jezik Java raspolaºe upravlja£kim konstrukcijama poznatim iz jezika C: if,<br />

while, do-while, for i switch. Ovdje ¢e one biti prikazane samo u najkra¢im<br />

crtama, kroz minimalisti£ki primjer.<br />

Ispis 2.7: Kratki primjeri uporabe upravlja£kih konstrukcija<br />

static void controlStructures()<br />

{<br />

int j = 0, k = 1;<br />

if ( j == k )<br />

{<br />

k++;<br />

}<br />

else if ( j < k )<br />

{<br />

j++;<br />

}<br />

else


44 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

k = 0;<br />

//-------------------------------------<br />

for ( int i = 0; i < 5; i++ )<br />

{<br />

if ( i == j - 2 )<br />

continue;<br />

j += i;<br />

if ( j > 3 )<br />

break;<br />

}<br />

//-------------------------------------<br />

while ( j < 13 )<br />

j++;<br />

//-------------------------------------<br />

do<br />

{<br />

k++;<br />

} while ( k < 13 );<br />

//-------------------------------------<br />

switch ( j )<br />

{<br />

case 0:<br />

k++;<br />

break;<br />

case 1:<br />

j++;<br />

break;<br />

default:<br />

j = k - j;<br />

}<br />

}<br />

Posebnu pozornost zahtijevaju jedino naredbe za izvanredan prekid petlje<br />

(break) ili izravan pomak na sljede¢i korak petlje (continue), pogotovo<br />

u kombinaciji s ugnijeºženim petljama. Kori²tenjem oznaka (eng. label)<br />

mogu¢e je npr. u unutarnjoj petlji narediti izlazak iz vanjske petlje. To je<br />

demonstrirano ovim primjerom:<br />

Ispis 2.8: Izjave break i continue koje koriste oznake<br />

static void breakContinue()<br />

{<br />

outerLoop:<br />

for ( int i = 1; i < 4; i++ )<br />

{<br />

for ( int j = 1; j < 4; j++ )<br />

{<br />

if ( i - j == -1 )<br />

continue outerLoop;<br />

if ( i % j == 1 )<br />

break outerLoop;


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 45<br />

}<br />

}<br />

System.out.println( "i = " + i + ", j = " + j );<br />

O£ekivani ispis je:<br />

i = 1, j = 1<br />

i = 2, j = 1<br />

i = 2, j = 2<br />

i = 3, j = 1<br />

Ove konstrukcije uvedene su u jezik Javu kao zamjena za naj£e²¢i slu£aj<br />

kori²tenja naredbe goto, koja ne postoji u jeziku.<br />

2.6 Sve razine dostupnosti<br />

Dosad smo se susreli s tri razine dostupnosti:<br />

• privatna £lan je dostupan samo u klasi gdje je deniran;<br />

• paketno-privatna £lan je dostupan iz svih klasa u istom paketu;<br />

• javna £lan je dostupan bez ograni£enja.<br />

Java poznaje jo² jednu razinu, ²iru od paketno-privatne koja dodatno<br />

dozvoljava dostup iz svake potklase, bez obzira u kojem paketu se nalazila.<br />

Pristup je jo² uvijek zabranjen klasama iz drugih paketa ako nisu potklase.<br />

Ova razina dostupnosti nazna£uje se klju£nom rije£i protected. Koristi se<br />

prvenstveno u klasi koja je posebno namijenjena da korisnik paketa iz nje<br />

izvodi svoje klase. Sveukupno, razine dostupnosti mogu se sistematizirati<br />

tablicom 2.1.<br />

Tablica 2.1: Pregled razina dostupnosti<br />

razina (eng.) klasa paket potklase ostali<br />

private o<br />

package-private o o<br />

protected o o o<br />

public o o o o


46 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

2.6.1 Model klasne enkapsulacije<br />

Java koristi model enkapsulacije koji je zasnovan ne na instanci, ve¢ na<br />

klasi. Ako je npr. neki atribut ozna£en kao private, to zna£i da je on privatan<br />

za sve instance te klase i nedostupan instancama drugih klasa. Drugim<br />

rije£ima, sve instance iste klase mežusobno imaju neograni£en dostup atributima<br />

i metodama. Na primjer, ovaj kod ne¢e proizvesti prevoditeljsku<br />

pogre²ku:<br />

class Abc {<br />

private int i;<br />

}<br />

Ispis 2.9: Model klasne enkapsulacije<br />

public void manipulateAnotherObject( Abc anotherObject )<br />

{<br />

anotherObject.i++;<br />

}<br />

Ovakav model, iako nije striktno u duhu paradigme objekata s nedodirljivom<br />

unutra²njosti, u praksi je opravdan jer je sav kod koji moºe manipulirati<br />

privatnim dijelovima drugog objekta lociran na istom mjestu pa se<br />

ne mogu pojaviti problemi zbog kojih je i uvedena enkapsulacija da kod<br />

koji koristi neki objekt prestane funkcionirati zbog izmjena na njegovim<br />

unutarnjim detaljima.<br />

2.7 Stati£ka metoda izuzeta od polimorzma<br />

Modikator static ve¢ je usputno spomenut i kori²ten u ispisima koda uz<br />

posebnu metodu main. Njime je mogu¢e ozna£iti i bilo koju drugu metodu.<br />

Time se postiºe da se takva metoda ne poziva nad nekom instancom, ve¢<br />

samostalno. Dakle, ne prosliježuje joj se implicitni argument this (vidjeti<br />

odjeljak 2.11). Argument this klju£an je za funkcioniranje polimorzma i<br />

bez njega ne moºe do¢i do dinami£kog povezivanja.<br />

Takva metoda odgovara pojmu funkcije iz proceduralne paradigme (kao<br />

npr. u jezicima C ili Pascal). Za stati£ke metode koristi se stati£ko povezivanje<br />

tijekom izvr²avanja ne donosi se odluka koju metodu pozvati, to<br />

je ksirano u trenutku prevoženja. Za stati£ke metode ponekad se koristi i<br />

izraz metoda klase odnosno klasina metoda jer je vezana uz klasu, a ne uz<br />

njene instance.


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 47<br />

U ovoj skripti ponekad ¢e se koristiti stati£ke metode, u primjerima za<br />

koje dinami£ko povezivanje nije bitno. Podru£ja primjene u praksi:<br />

• Kad su parametri metode instance tužih klasa koje samo koristimo,<br />

a ne razvijamo pa nemamo dostup njihovom izvornom kodu i ne<br />

moºemo dodati svoju metodu. Takva primjena zna£ajno je oteºana<br />

gubitkom dinami£kog povezivanja.<br />

• Kad su svi parametri primitivne vrste. U tom slu£aju dinami£ko povezivanje<br />

ionako ne dolazi u obzir. Ovakva primjena prikazana je u<br />

primjeru koji slijedi malo niºe.<br />

• Kad metoda ne ovisi ni o jednom parametru. Tada jednostavno nema<br />

razloga za²to ne bi bila stati£ka, a stati£ko povezivanje je ekasnije<br />

od dinami£kog.<br />

Studenti ponekad, ugledaju¢i prevoditeljevu pogre²ku Instance method<br />

used in a static context, problem otklanjaju progla²avanjem metode stati£kom<br />

i time zapravo samo stvaraju dodatne probleme. Pogre²ku treba<br />

otkloniti tako da se jasno ustanovi nad kojom instancom je metoda trebala<br />

biti pozvana i da se kod ispravi u skladu s time.<br />

Ako se stati£ka metoda poziva iz neke druge klase, poziv se zapisuje u<br />

obliku Klasa.stati£kaMetoda:<br />

Ispis 2.10: Poziv stati£ke metode<br />

public class BooleanFunctions<br />

{<br />

public static boolean implies( boolean left, boolean right )<br />

{<br />

return !( left && !right ) ;<br />

}<br />

}<br />

public class Executable<br />

{<br />

public static void main( String[] args )<br />

{<br />

boolean a = true, b = false;<br />

boolean result = BooleanFunctions.implies( a, b );<br />

System.out.println( "" + a + " implies " + b + "? " + result );<br />

}<br />

}<br />

Ispis:


48 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

true implies false? false<br />

Specikacija jezika Java [4] dozvoljava i poziv stati£ke metode istom<br />

sintaksom kao da je instancina metoda, npr:<br />

Ispis 2.11: Poziv stati£ke metode sintaksom kao da je instancina metoda<br />

public class Executable<br />

{<br />

public static void main( String[] args )<br />

{<br />

boolean a = true, b = false;<br />

BooleanFunctions bf = null;<br />

boolean result = bf.implies( a, b );<br />

System.out.println( "" + a + " implies " + b + "? " + result );<br />

}<br />

}<br />

Prevoditelj utvržuje vrstu izraza lijevo od to£ke i ta vrsta mora biti<br />

klasa u kojoj je denirana doti£na metoda. Ovaj na£in zapisa ne treba<br />

nikada koristiti jer samo zavarava, a ne donosi nikakve dodatne prednosti<br />

(nema polimorzma). Primijetimo da je u gornjem primjeru metoda<br />

pozvana putem varijable £ija vrijednost je null, £ime se jasno demonstrira<br />

da se potpuno ignorira vrijednost varijable, bitna je samo njena vrsta.<br />

2.8 Stati£ki atribut, konstanta<br />

Sli£no kao metoda, atribut isto moºe biti stati£ki i time nevezan uz instancu.<br />

Postoji samo jedna, globalna vrijednost takvog atributa. Jedna od naj£e²¢ih<br />

uporaba stati£kih atributa je za deniranje konstanti, npr. mogli bismo u<br />

konstanti denirati podrazumijevanu duljinu ciklusa broja£a ModuloCounter:<br />

Ispis 2.12: Stati£ki atribut, javna konstanta<br />

public class ModuloCounter<br />

{<br />

public static final int DEFAULT_MODULUS = 10;<br />

private int modulus;<br />

public ModuloCounter()<br />

{<br />

this.modulus = DEFAULT_MODULUS;<br />

}<br />

...<br />

}


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 49<br />

2.8.1 Modikator final rekapitulacija<br />

Pri deniranju konstante pojavljuje se i modikator final. U odjeljku 1.11<br />

obja²njeno je kako se on primjenuje na metode i klase radi kontrole polimorzma.<br />

Na atribute i varijable djeluje tako da zabranjuje promjenu njihove<br />

vrijednosti nakon inicijalizacije. Njime se, ako zatreba, moºe ozna£iti<br />

i parametar metode, npr.<br />

public void increment( final int step )<br />

{ ... }<br />

Kori²tenjem modikatora final na svim varijablama gdje je to smisleno<br />

pove¢ava se £itljivost koda jer se time jasno nagla²ava da se ustvari radi<br />

o konstanti. U praksi velik broj, a moºda i ve¢ina varijabli imaju ulogu<br />

konstante.<br />

2.9 Pravila oblikovanja identikatora<br />

U jeziku Java na snazi su neformalna, ali vrlo snaºna pravila oblikovanja<br />

identikatora, od kojih se ne smije odudarati. Naj£vr²¢a pravila ti£u se<br />

rasporeda velikih i malih slova:<br />

• U imenu paketa zabranjeno je koristiti velika slova. Npr. ime<br />

paketa kori²tenog u primjerima je hr.fer.tel.jsem.<br />

• Ime referentne vrste podatka (klasa i su£elje) obavezno po£inje<br />

velikim slovom. Svaka daljnja rije£ u imenu ponovo po£inje velikim<br />

slovom. Npr. SimpleCounter.<br />

• Ime lokalne varijable, parametra, uhva¢ene iznimke, atributa i metode<br />

obavezno po£inje malim slovom. Svaka daljnja rije£ u imenu po-<br />

£inje velikim slovom. Npr. counterState.<br />

• Poseban slu£aj su javne konstante, koje se pi²u velikim slovima, a<br />

rije£i se odvajaju podvlakom. Npr. DEFAULT_MODULUS.<br />

Za sve identikatore koristi se isklju£ivo engleski jezik. Za su£elje<br />

se uvijek odabire najkra¢e, naj£i²¢e ime bez posebnih preksa ili suksa<br />

kao u na²em primjeru Counter. To je stoga ²to se upravo ime su£elja<br />

najvi²e koristi u kodu. Ime apstraktne klase u vrhu hijerarhije uobi£ajeno


50 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

je napraviti od imena su£elja s preksom Abstract npr. AbstractCounter.<br />

Konkretne klase uobi£ajeno zadrºavaju ime svog su£elja na zadnjem mjestu,<br />

a naprijed se dodaju rije£i koje pobliºe opisuju o kakvoj implementaciji se<br />

radi npr. ModuloCounter.<br />

2.10 Upcast i downcast<br />

Op¢enito, pojam type cast odnosi se na tretiranje podatka jedne vrste kao<br />

da je neke druge. U nekim drugim jezicima, prije svega C-u, mogu¢e je<br />

cast-ati izmežu vrsta na razli£ite na£ine na primjer, cijeli broj mogu¢e<br />

je cast-ati u pokaziva£. Za referentne vrste jedini dozvoljeni oblik castanja<br />

je unutar klasne hijerarhije ili prema gore (upcast), ili prema dolje<br />

(downcast).<br />

Slu£aj upcast-a je jednostavniji i dogaža se automatski. Tako se naziva<br />

situacija s kojom smo se ve¢ sreli na primjer, kad se instanca klase<br />

SimpleCounter tretira kao instanca su£elja Counter (u ispisu 1.13). Isto se dogaža<br />

i kad imamo dvije klase u odnosu natklasa-potklasa. Dakle, upcast je<br />

tehni£ko sredstvo koje omogu¢ava transparentno kori²tenje polimorzma.<br />

Njega je uvijek sigurno provesti ne moºe se dogoditi da instanca potklase<br />

nema neku metodu deniranu za natklasu.<br />

Java dozvoljava i cast-anje u suprotnom smjeru, prema dolje po hijerarhiji<br />

downcast. Njega se ne koristi da bismo instancu natklase tretirali kao<br />

instancu potklase dapa£e, to bi prouzro£ilo pogre²ku jer potklasa moºe<br />

imati dodatne metode koje natklasa nema. Downcast sluºi da bi se npr. u<br />

metodi koja prima op¢eniti parametar (npr. Counter) ipak mogla koristiti<br />

puna funkcionalnost instance konkretne klase (npr. ModuloCounter). Uzmimo<br />

konkretan primjer recimo da klasa ModuloCounter (ispis 1.21) ima dodatnu<br />

metodu setModulus koja nije denirana u su£elju Counter jer je speci£na za<br />

slu£aj cikli£kog broja£a:<br />

Ispis 2.13: ModuloCounter s dodatnom metodom setModulus<br />

package hr.fer.tel.jsem;<br />

public class ModuloCounter<br />

extends AbstractCounter<br />

{<br />

private int modulus = 10;<br />

public ModuloCounter( int initialState )


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 51<br />

{<br />

}<br />

super( initialState );<br />

}<br />

public ModuloCounter()<br />

{<br />

}<br />

public void setModulus( int newModulus )<br />

{<br />

this.modulus = newModulus;<br />

}<br />

void setState( int newState )<br />

{<br />

this.counterState = newState % this.modulus;<br />

}<br />

Nadalje, recimo da negdje imamo metodu koja prima parametar op¢enite<br />

vrste Counter, ali u slu£aju da se radi o cikli£kom broja£u, ºeli postaviti<br />

duljinu ciklusa na potrebnu vrijednost:<br />

Ispis 2.14: Metoda koja upravlja ModuloCounter-om<br />

public static void useCounter( Counter cntr )<br />

{<br />

ModuloCounter modCntr = (ModuloCounter) cntr;<br />

modCntr.setModulus( 7 );<br />

}<br />

Varijabli vrste ModuloCounter pridjeljuje se vrijednost parametra vrste<br />

Counter i stoga je potrebno eksplicitno provesti downcast izrazom<br />

(ModuloCounter) cntr<br />

2.10.1 Operator instanceof<br />

U slu£aju da obavimo downcast nad objektom koji nije instanca doti£ne<br />

klase (ili eventualno njena potklasa), tijekom izvr²avanja programa bit ¢e<br />

prijavljena pogre²ka. U trenutku kompilacije op¢enito nije mogu¢e ustanoviti<br />

ho¢e li se to dogoditi. Na primjer, ako imamo ovakav poziv metode<br />

useCounter:<br />

useCounter( new SimpleCounter() );


52 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

jasno je da mora do¢i do pogre²ke jer klasa SimpleCounter ne denira metodu<br />

setModulus. Java ne¢e dozvoliti ni da kod stigne do tog poziva jer ¢e<br />

prijaviti pogre²ku ve¢ prilikom poku²aja downcast-anja. Prekid izvr²avanja<br />

zbog pogre²ke moºe se izbje¢i obavljanjem prethodne provjere operatorom<br />

instanceof:<br />

Ispis 2.15: Doražena metoda useCounter<br />

public static void useCounter( Counter cntr )<br />

{<br />

if ( cntr instanceof ModuloCounter )<br />

{<br />

ModuloCounter modCntr = (ModuloCounter) cntr;<br />

modCntr.setModulus( 7 );<br />

}<br />

}<br />

Prije nego ²to se obavi downcast, provjerava se je li prosliježen objekt<br />

zaista instanca klase ModuloCounter. Operator instanceof s lijeve strane op-<br />

¢enito ima neki izraz £ija vrijednost je referentne vrste, a s desne strane<br />

doslovno navedeno ime neke referentne vrste (klase ili su£elja). Operator<br />

vra¢a true ako je lijevi operand instanca desnog operanda ili njegove potklase.<br />

Odnosno, ako je desni operand su£elje, provjerava je li lijevi operand<br />

objekt koji implementira to su£elje. U praksi, u ve¢ini slu£ajeva gdje se radi<br />

downcast potrebna je ovakva provjera.<br />

2.11 Implicitni parametar metode this<br />

Ve¢ smo se na samom po£etku, u ispisu 1.1, susreli s klju£nom rije£i this<br />

pomo¢u koje smo u kodu metode pristupali atributima objekta nad kojim<br />

je ona pozvana. Formalno, svaka instancina metoda (metoda objekta,<br />

tj. svaka koja nije stati£ka), osim eksplicitno navedenih parametara, prima<br />

implicitno i dodatni parametar £ije ime je this, vrsta mu je klasa u kojoj<br />

se metoda nalazi, a vrijednost referenca na objekt nad kojim je metoda<br />

pozvana. Na primjer, metodu increment iz ispisa 1.16 (deklaracija klase<br />

SimpleCounter) mogli smo zapisati i ovako, zadrºavaju¢i identi£no pona²anje:<br />

public void increment()<br />

Ispis 2.16: Implicitni parametar this


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 53<br />

{<br />

}<br />

SimpleCounter cntr = this;<br />

cntr.counterState++;<br />

Prilikom poziva metode, npr. cntr1.increment() u ispisu 1.4, referenca<br />

iz varijable cntr1 prepisujse se u parametar this.<br />

2.11.1 Parametar this i polimorzam<br />

Ono ²to je najvaºnije za parametar this je da upravo on omogu¢uje funkcioniranje<br />

polimorzma jer se samo za njega provodi dinami£ko povezivanje.<br />

Tijekom izvr²avanja programa prije svakog poziva metode prvo se provjerava<br />

to£na vrsta objekta na koji ¢e pokazivati this. Na osnovu te vrste<br />

donosi se odluka koju metodu pozvati. Za sve ostale, obi£ne parametre<br />

vrsta se ne utvržuje prilikom izvr²avanja, ve¢ samo prilikom prevoženja<br />

i to na osnovu deklarirane vrste izraza koji se pojavljuju kao argumenti<br />

metode. Drugim rije£ima, stati£ki se ustanovljuje potpis pozvane metode<br />

(vidjeti odjeljak 1.12).<br />

Bez ovog saznanja mogli bismo pomisliti da su npr. ova dva pristupa<br />

deniranju metode increment ekvivalentni (modikator static narežuje da<br />

se ne proslijedi implicitni parametar this, vidjeti odjeljak 2.7):<br />

Ispis 2.17: Neuspio poku²aj uvoženja eksplicitnog parametra this<br />

public interface Counter<br />

{<br />

void increment();<br />

void incrementWithExplicitThis( Counter explicitThis );<br />

... ostale metode ...<br />

}<br />

public class SimpleCounter<br />

implements Counter<br />

{<br />

private int counterState;<br />

public void increment()<br />

{<br />

this.counterState++;<br />

}<br />

public static void incrementWithExplicitThis( Counter explicitThis )<br />

{<br />

explicitThis.counterState++;


54 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

}<br />

}<br />

... ostale metode ...<br />

Metode bismo mogli poku²ati koristiti ovako:<br />

Ispis 2.18: Neuspio poku²aj kori²tenja metode s eksplicitnim this<br />

public static void main( String[] args )<br />

{<br />

Counter cntr = new SimpleCounter();<br />

cntr.increment();<br />

Counter.incrementWithExplicitThis( cntr );<br />

}<br />

Naoko, situacija je ekvivalentna prvi put se argument metode navodi<br />

lijevo od to£ke, a drugi put kao normalni argument u zagradama. Mežutim,<br />

uvoženje metode incrementWithExplicitThis ustvari generira £itav niz<br />

prevoditeljevih pogre²aka, sve zbog toga ²to Java ne provodi dinami£ko<br />

povezivanje nad normalnim argumentima.<br />

Prilikom prevoženja poziva incrementwithExplicitThis( cntr ) utvržuje<br />

se da je deklarirana (stati£ka) vrsta argumenta cntr jednaka Counter. Tu se<br />

nigdje ne pojavljuje prava vrsta objekta, SimpleCounter, i Java uop¢e ne¢e<br />

uzeti u obzir tu klasu. Dakle, nemogu¢e je da ¢e se pozvati metoda iz klase<br />

SimpleCounter. Vrsta Counter je ustvari su£elje u kojem uop¢e nema denicija<br />

metoda. Jasno je dakle da je ovakva situacija nemogu¢a i da mora<br />

uzrokovati prevoditeljevu pogre²ku. U praksi, prevoditeljeva pogre²ka pojavit<br />

¢e se ve¢ £im metodu incrementwithExplicitThis ozna£imo kao static<br />

jer je to metoda su£elja, a metode su£elja nikad nisu stati£ke iz upravo<br />

obja²njenog razloga.<br />

Nadalje, £ak i da se svi ovi problemi na neki neobja²njeni na£in rije²e<br />

i da zaista bude pozvana metoda incrementwithExplicitThis u klasi<br />

SimpleCounter, tamo stoji izjava explicitThis.counterState++. Parametar explicitThis<br />

je vrste Counter, a u toj vrsti ne spominje se nikakav atribut<br />

counterState i stoga ¢e se i za to prijaviti prevoditeljeva pogre²ka.<br />

Jo² jedan primjer u kojem se o£ituje razlika izmežu parametra this i<br />

ostalih moºe se vidjeti u raspravi u odjeljku 3.2.1.<br />

Pojam koji se u struci koristi za proces dinami£kog povezivanja je method<br />

dispatch. Za Javu se stoga kaºe da podrºava samo single dispatch jer


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 55<br />

obavlja dispatch po samo jednom parametru metode. Suprotan pojam je<br />

multiple dispatch.<br />

Stru£ni pojam za jezi£no svojstvo da se prilikom prevoženja, tj. stati£ki<br />

utvržuju vrste svih izraza je static typing. Java je, dakle, statically typed<br />

jezik. Suprotan pojam je dynamic typing.<br />

2.12 Detalji o hijerarhiji klasa<br />

2.12.1 Korijenska klasa Object<br />

U 1. poglavlju ve¢ se govorilo o izvedenim klasama. U Javi je svaka klasa izvedena<br />

iz to£no jedne klase, osim posebne ugražene klase java.lang.Object,<br />

koja nema natklasu. Ako nacrtamo detaljan UML-ov dijagram klasa prikazuju¢i<br />

sve veze izvedena iz, dobit ¢emo stablo £iji korijen je klasa java.lang.Object.<br />

Ako deklaracija klase ne navodi eksplicitno iz koje je klase izvedena, podrazumijeva<br />

se extends java.lang.Object. Klasa Object denira i neke metode<br />

koje stoga imaju svi objekti (vidjeti 3.2).<br />

Graf odnosa implementira su£elje s druge strane, jest hijerarhija, ali<br />

nije stablo. Jedna klasa moºe implementirati proizvoljan broj su£elja, a i<br />

su£elje moºe biti izvedeno iz proizvoljnog broja drugih su£elja.<br />

2.12.2 Denicije izraza vezanih uz hijerarhiju<br />

Kad se pri£a o hijerarhijskim odnosima izmežu klasa, treba imati na umu<br />

to£na zna£enja izraza kao ²to su natklasa i potklasa. Vrijede sljede¢e denicije:<br />

• Natklasa klase C je klasa B iz koje je ona izvedena, ali takožer i klasa<br />

A iz koje je izvedena B, itd. Koriste¢i izraze iz genealogije, natklasa<br />

odgovara pojmu pretka. Klasa Object je, dakle, natklasa svim ostalim<br />

Javinim klasama.<br />

• Potklasa klase A je, analogno, svaka klasa-potomak, tj. svaka klasa B<br />

kojoj je A natklasa.<br />

• Klasa-roditelj klase C je njena izravna natklasa ona iz koje je izvedena.


56 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

• Klasa-dijete klase C je svaka klasa koja je iz nje izvedena, tj. kojoj je<br />

klasa C roditelj.<br />

2.12.3 Ulan£avanje poziva konstruktora<br />

Svaki konstruktor, prije nego ²to se izvr²i njegov kod, duºan je prvo pozvati<br />

neki konstruktor svoje natklase. To se dogaža ili eksplicitno, kori²tenjem<br />

klju£ne rije£i super, ili, u odsustvu toga, implicitno se dodaje poziv podrazumijevanog<br />

konstruktora natklase (super()). Posljedica toga je da se<br />

konstruktori izvr²avaju ulan£ano, tako da se prvo uvijek izvr²i konstruktor<br />

klase Object, zatim kontruktor neke njene izravne potklase, itd. sve do<br />

konkretne klase koja se instancira. Zbog toga £ak i za apstraktne klase,<br />

koje se ne smiju instancirati, vrijede ista pravila o posjedovanju bar jednog<br />

konstruktora. Bez toga bi bilo nemogu¢e instancirati njihove potklase.<br />

Imaju¢i re£eno u vidu, moºemo uvidjeti da je puni oblik dvaju konstruktora<br />

u ispisu 1.20 ustvari ovakav:<br />

Ispis 2.19: Eksplicitan poziv konstruktora natklase<br />

public AbstractCounter( int initialState )<br />

{<br />

super();<br />

setState( initialState );<br />

}<br />

public AbstractCounter()<br />

{<br />

super();<br />

}<br />

Implicitne pozive treba uvijek imati na umu, posebice stoga ²to natklasa<br />

ne mora posjedovati podrazumijevani konstruktor. Ako dakle imamo konstruktor<br />

s izostavljenim pozivom konstruktora natklase, a natklasa nema<br />

podrazumijevani konstruktor, do¢i ¢e do pogre²ke prilikom prevoženja koja<br />

moºe biti prili£no zbunjuju¢a jer se dogodila u praznom prostoru u koji<br />

je implicitno dodan izostavljeni poziv super().<br />

Konstruktor moºe umjesto iz natklase pozvati i neki drugi konstruktor<br />

iz iste klase. Time je omogu¢ena vi²estruka iskoristivost koda u vi²e<br />

konstruktora. U kona£nici, nakon ²to se konstruktori klase mežusobno dogovore<br />

koji ¢e se prvi izvr²iti, taj konstruktor opet poziva konstruktor iz


POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 57<br />

natklase. Poziv susjednog konstruktora postiºe se kori²tenjem klju£ne rije£i<br />

this umjesto super. Na primjer, konstruktore u ispisu 1.20 mogli smo<br />

denirati i ovako:<br />

Ispis 2.20: Poziv konstruktora iste klase<br />

public AbstractCounter( int initialState )<br />

{<br />

super();<br />

setState( initialState );<br />

}<br />

public AbstractCounter()<br />

{<br />

this( 0 );<br />

}<br />

Kori²tenjem podrazumijevanog konstruktora AbstractCounter() prvo ¢e<br />

se izvr²iti podrazumijevani konstruktor natklase (super()), zatim ostatak<br />

konstruktora s jednim parametrom, a zatim ostatak podrazumijevanog<br />

konstruktora (u ovom slu£aju nema daljnjih akcija u njemu).<br />

Napomenimo na kraju da kori²tenje klju£nih rije£i super i this za pozive<br />

konstruktora nema nikakve veze s kori²tenjem istih klju£nih rije£i u<br />

smislu reference na trenutni objekt i pozivanja metode natklase. Radi se<br />

samo o ²tednji na klju£nim rije£ima njihovim vi²estrukim kori²tenjem u<br />

jeziku. Svaka dodatna klju£na rije£ zna£i jo² jedan zabranjeni identikator<br />

za varijable, metode itd.<br />

2.13 Java kao softverska platforma<br />

Pod pojmom Java ne podrazumijeva se samo programski jezik i njegova<br />

biblioteka klasa, nego i cjelokupna softverska platforma, £iji temelj je Javin<br />

virtualni stroj.<br />

Programi napisani u Javi ne prevode se, kao ²to je ina£e uobi£ajeno,<br />

u izvr²ni kod koji se izravno moºe izvr²avati na procesoru ra£unala, ve¢<br />

u posebnu vrstu izvr²nog koda za Javin virtualni stroj (JVM), koji onda<br />

konkretno ra£unalo emulira. Taj poseban binarni kod naziva se mežukodom<br />

ili byte-kodom (eng. bytecode). Cilj toga je postizanje neovisnosti o ra£unalskoj<br />

platformi: kod se kompilira jednom, a izvr²ava na bilo kojem stroju<br />

koji ima implementaciju JVM-a, bez obzira na vrstu procesora. Takožer<br />

se tehni£ki omogu¢uje i bolja kontrola nad interakcijom izmežu Javinog


58 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />

programa i ra£unala na kojem se izvr²ava. Lak²e je mogu¢e blokirati svaki<br />

poku²aj pristupa osjetljivim resursima i tako onemogu¢iti zlonamjerni kod.<br />

Vi²e o ovome moºe se doznati na [5].<br />

Sve ovo potje£e od prvotnog cilja Jave da sluºi za tzv. embedded applications,<br />

aplikacije koje se u£itavaju unutar webskih stranica. ƒim se ne²to<br />

implicitno u£itava putem Weba, otvara se prostor za napade na ra£unalo<br />

pisanjem zlonamjernog koda. U mežuvremenu se Java po£ela masovno koristiti<br />

i za pisanje obi£nih, samostojnih aplikacija, gdje je pitanje sigurnosti<br />

manje bitno. Na primjer, £itavo razvojno okruºje Eclipse, kao i Borlandov<br />

JBuilder, napisani su u Javi.


Poglavlje 3<br />

Najvaºnije ugražene klase<br />

Ve¢ je re£eno da jezik Java sadrºi i ve¢i broj ugraženih klasa i su£elja. Takožer<br />

je istaknut i jedan od ugraženih paketa koji je posebno usko povezan<br />

s jezikom paket java.lang. Ovdje ¢e biti izloºeni detalji nekoliko najvaºnijih<br />

klasa iz tog paketa. Za daljnje detalje treba se obratiti dokumentaciji<br />

Javine biblioteke klasa dostupne na Webu [1].<br />

3.1 Uvodne upute za snalaºenje u Javinoj dokumentaciji<br />

API-ja<br />

Javin sustav za HTML-ovsku dokumentaciju zove se Javadoc. Na primjer,<br />

na upravo spomenutoj internetskoj lokaciji, [1], nalazi se Javadoc-ova dokumentacija<br />

cjelokupne Javine biblioteke klasa. Kad otvorimo tu lokaciju<br />

u pregledniku Weba, dobit ¢emo prikaz kao na slici 3.1. Pregled treba zapo£eti<br />

u okviru gore lijevo. Tamo je popis svih paketa. Treba kliknuti na<br />

paket od interesa, u ovom slu£aju java.lang. Time se u okviru dolje lijevo<br />

suºava izbor i prikazuju se samo klase iz doti£nog paketa. U tom okviru<br />

zatim moºemo na¢i klasu koja nas zanima, npr. Object. U glavnom okviru<br />

(desno) pojavljuje se dokumentacija traºene klase. Na vrhu je op¢eniti opis<br />

klase, a zatim slijedi popis £lanova konstruktora, atributa i metoda. Prvo<br />

su u tablicama pobrojani svi £lanovi s kratkom napomenom. Klikom na<br />

ime £lana odlazi se dublje u stranicu, gdje je njegov detaljan opis. Na slici<br />

3.2 prikazana je detaljna dokumentacija za metodu equals.<br />

59


60 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />

Slika 3.1: Po£etna stranica Javadoc-a


POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 61<br />

Slika 3.2: Dokumentacija za metodu equals


62 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />

3.2 Klasa java.lang.Object<br />

Klasa Object je u korijenu Javine hijerarhije klasa, kao ²to je napomenuto<br />

u odjeljku 2.12.1. Ovdje ¢e se detaljnije govoriti o toj klasi. To nije kao<br />

²to bi se moglo pomisliti za najop¢enitiju klasu koja predstavlja bilo kakav<br />

objekt samo prazna, apstraktna klasa bez svojih funkcionalnosti. Radi<br />

se o konkretnoj klasi koju je mogu¢e normalno instancirati. Osim toga<br />

je i prili£no kompleksna i njena implementacija je vezana uz £itav niz programskih<br />

tehnika. Ovdje ¢e se od svega spominjati samo njene najosnovnije<br />

karakteristike.<br />

3.2.1 Metoda equals<br />

U odjeljku 2.1.2 spomenuto je da za provjeru jednakosti objekata treba<br />

koristiti metodu equals. Metodu se koristi tako da ju se pozove nad prvim<br />

objektom i kao argument se proslijedi drugi objekt: o1.equals( o2 ). Metoda<br />

equals denirana je u klasi Object, ali na trivijalan na£in usporežuju<br />

se reference (o1 == o2). Ako je potrebna smislena usporedba objekata neke<br />

klase, potrebno je nadja£ati ovu metodu odgovaraju¢om implementacijom.<br />

Potpis metode je equals( Object ) i upravo takvu metodu moramo dodati<br />

u svoju klasu. Budu¢i da je parametar vrste Object, prvo ¢e biti potrebno<br />

ustanoviti je li prosliježeni objekt odgovaraju¢e vrste jer ako nije, onda<br />

je jasno da nije jednak trenutnom objektu. Stoga kod ove metode rutinski<br />

zapo£inje kori²tenjem operatora instanceof, iza £ega slijedi odgovaraju¢i<br />

downcast. Na primjer, mogli bismo imati ovakav equals u klasi SimpleCounter<br />

(ispis 1.21):<br />

package hr.fer.tel.jsem;<br />

public final class SimpleCounter<br />

extends AbstractCounter<br />

{<br />

... sve isto kao ranije ...<br />

Ispis 3.1: Implementacija metode equals<br />

public boolean equals( Object o )<br />

{<br />

if ( !( o instanceof SimpleCounter ) )<br />

return false;<br />

SimpleCounter that = (SimpleCounter) o;


POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 63<br />

}<br />

}<br />

return this.counterState == that.counterState;<br />

Za metodu equals tipi£no je da se okori²tava klasnom enkapsulacijom i da<br />

izravno pristupa atributima drugog objekta.<br />

Jedna od tipi£nih pogre²aka kod studenata je deklaracija metode s parametrom<br />

£ija vrsta je trenutna klasa (npr. equals( SimpleCounter )). Parametar<br />

mora biti vrste Object, u protivnom se ne¢e raditi o nadja£avanju,<br />

ve¢ o deniranju nove metode koja samo preoptere¢uje ime equals. U<br />

jednostavnim primjerima kod ¢e £ak ispravno raditi jer ¢e se pozivati ta<br />

metoda umjesto prave metode equals. Sljede¢i primjer pokazuje u kojem<br />

slu£aju ¢e raditi, a u kojima ne:<br />

Ispis 3.2: Tipi£na pogre²ka s preoptere¢enjem umjesto nadja£avanjem<br />

public class EqualsTester<br />

{<br />

public boolean equals( EqualsTester et )<br />

{<br />

return true;<br />

}<br />

public static void main( String[] args )<br />

{<br />

EqualsTester et1 = new EqualsTester();<br />

EqualsTester et2 = new EqualsTester();<br />

boolean b;<br />

b = et1.equals( et2 );<br />

System.out.println( b ); // true<br />

// upcast argumenta na vrstu Object:<br />

Object o2 = et2;<br />

b = et1.equals( o2 );<br />

System.out.println( b ); // false!<br />

}<br />

}<br />

// upcast objekta nad kojim se poziva metoda:<br />

Object o1 = et1;<br />

b = o1.equals( et2 );<br />

System.out.println( b ); // false!<br />

Kad god se ispi²e false, zna£i da je pozvana originalna metoda equals<br />

iz klase Object, koja je ostala nenadja£ana. Razlog za ovakvo pona²anje,<br />

koje se nekima moºe £initi nelogi£nim s obzirom na postojanje dinami£kog


64 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />

povezivanja, je u tome ²to se dinami£ko povezivanje provodi samo nad parametrom<br />

this, tj. nad objektom lijevo od to£ke u pozivu metode (vidjeti<br />

odjeljak 2.11). Vrste argumenata, tj. potpis pozvane metode utvržuje se<br />

stati£ki, prilikom prevoženja. U prvom pozivu, et1.equals( et2 ) utvrženi<br />

potpis metode je equals( EqualsTester ) pa se poziva na²a metoda. U drugom<br />

slu£aju ispada equals( Object ) pa se poziva metoda nasliježena od<br />

klase Object.<br />

Tre¢i slu£aj je najsuptilniji. Argument je ponovo vrste EqualsTester <br />

kao u prvom slu£aju, koji je radio ispravno. Mežutim, sada je izraz lijevo<br />

od to£ke vrste Object. Prevoditelj u toj klasi traºi sve metode imena equals<br />

i nalazi samo jednu, s parametrom vrste Object. Na osnovu toga odabire<br />

se potpis metode equals( Object ). Tek tijekom izvr²avanja uo£it ¢e se da<br />

je stvarna vrsta objekta lijevo od to£ke EqualsTester, mežutim tada je ve¢<br />

ksiran potpis metode koju treba pozvati, i to je equals( Object ) metoda<br />

nasliježena iz klase Object.<br />

Problem kao u drugom slu£aju dogaža se uvijek kad se metoda preoptere¢uje<br />

tako da su vrste parametara u odnosu potklasa-natklasa. Takvo<br />

preoptere¢enje redovito treba izbjegavati. Ako postoji potreba za takve<br />

dvije metode, trebaju imati razli£ita imena. Problem kao u tre¢em slu£aju<br />

ne bi se dogodio da su obje verzije preoptere¢ene metode bile denirane u<br />

istoj klasi.<br />

3.2.2 Metoda hashCode<br />

Ova metoda vezana je uz vrlo zna£ajnu programsku tehniku he²-tablicu<br />

(eng. hash table ili hashtable). To je struktura koja sluºi za memorijski<br />

ekasno spremanje ve¢e koli£ine podataka uz sposobnost brzog dohva¢anja<br />

podatka prema njegovom klju£u he²-kodu. U Javu ugražene kolekcije<br />

(vidjeti 5. poglavlje) interno koriste he²-tablice (npr. £esto kori²tena klasa<br />

HashSet). One se oslanjaju na ispravnu implementaciju metode hashCode u<br />

svakom objektu koji se sprema u kolekciju. Ova metoda usko je povezana<br />

s metodom equals i njih dvije se uvijek nadja£ava u paru.<br />

Metoda hashCode zaduºena je da za svaki objekt vrati njegov he²-kod<br />

u obliku cijelog broja (int-a). Presudno je da dva jednaka objekta uvijek<br />

prijavljuju i jednak he²-kod. Bitno je i da dva razli£ita objekta ²to £e²¢e<br />

vra¢aju razli£it he²-kod dakle, da he²-kodovi budu ²to bolje raspr²eni po<br />

objektima. U protivnom ¢e se degradirati ekasnost he²-tablice. Jasno je


POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 65<br />

da objekt koji ima vi²e od 2 32 mogu¢ih stanja ne moºe za svako stanje<br />

prijaviti razli£it he²-kod i stoga je potrebno promi²ljeno implementirati<br />

ovu metodu da se postigne dobro raspr²enje. Postoji standardni predloºak<br />

za implementaciju ove metode i najbolje ga se uvijek drºati. U ispisu 3.3<br />

prikazana je klasa koja sadrºi sve mogu¢e slu£ajeve vrsta podataka i na£in<br />

izra£una njihovog he²-koda. Ukratko, postupak je sljede¢i:<br />

1. Deklariramo cjelobrojnu varijablu (u primjeru: hc) i inicijaliziramo ju<br />

na 17.<br />

2. Za svaki atribut koji sudjeluje u metodi equals izra£unamo njegov<br />

he²-kod kao u primjeru.<br />

3. U varijablu hc ugražujemo pojedini he²-kod atributa prema formuli<br />

hc = 37*hc + he²-kod<br />

4. Kona£nu vrijednost varijable hc vratimo kao ukupan he²-kod objekta.<br />

Ispis 3.3: Predloºak za generiranje he²-koda<br />

public class HashCodeExample<br />

{<br />

private boolean boolVal;<br />

private byte byteVal;<br />

private char charVal;<br />

private short shortVal;<br />

private int intVal;<br />

private long longVal;<br />

private float floatVal;<br />

private double doubleVal;<br />

private Object objVal;<br />

private int[] intArray = { 1, 2, 3 };<br />

public int hashCode()<br />

{<br />

int hc = 17;<br />

hc = 37*hc + ( boolVal? 1 : 0 );<br />

hc = 37*hc + byteVal;<br />

hc = 37*hc + charVal;<br />

hc = 37*hc + shortVal;<br />

hc = 37*hc + intVal;<br />

hc = 37*hc + (int) ( longVal ^ ( longVal >>> 32 ) );<br />

hc = 37*hc + Float.floatToIntBits( floatVal );<br />

long dblToLong = Double.doubleToLongBits( doubleVal );<br />

hc = 37*hc + (int) ( dblToLong ^ ( dblToLong >>> 32 ) );


66 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />

hc = 37*hc + objVal.hashCode();<br />

for ( int i = 0; i < intArray.length; i++ )<br />

hc = 37*hc + intArray[i];<br />

}<br />

}<br />

return hc;<br />

3.2.3 Metoda toString<br />

Pri razvoju programa, posebice tijekom traºenja i uklanjanja pogre²aka,<br />

program mora ispisivati detalje tijeka izvr²avanja. Na primjer, £esto je potreban<br />

ispis trenutnog stanja klju£nih objekata prije i nakon neke operacije.<br />

Takožer, u trenutku kad dože do pogre²ke iznimno je korisno dobiti izvje-<br />

²taj o stanju objekta koji je izazvao pogre²ku. Za ovakve i druge namjene<br />

postoji metoda toString koju imaju svi objekti jer je denirana u klasi<br />

Object. Metoda je zaduºena da vrati znakovni niz koji opisuje objekt.<br />

Izvorna implementacija u klasi Object prili£no je beskorisna (vra¢a ime<br />

klase £ija je instanca u pitanju i njen he²-kod u heksadecimalnom zapisu),<br />

stoga ju je redovito potrebno nadja£ati smislenom implementacijom. Studenti<br />

ponekad krivo shvate namjenu metode pa ju izvedu tako da sama<br />

ispisuje na zaslon. Pazite da izbjegnete takvu pogre²ku. Primjer implementacije<br />

u klasi SimpleCounter (ispis 1.21):<br />

package hr.fer.tel.jsem;<br />

Ispis 3.4: Implementacija metode toString<br />

public final class SimpleCounter<br />

extends AbstractCounter<br />

{<br />

... sve isto kao ranije ...<br />

}<br />

public String toString()<br />

{<br />

return "SimpleCounter:" + this.counterState;<br />

}<br />

3.3 Klasa java.lang.String<br />

U Javi, znakovni nizovi predstavljeni su instancama ugražene klase String.<br />

Ova klasa integrirana je u sam jezik i postoje jezi£ne konstrukcije posebno


POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 67<br />

za nju:<br />

• Doslovno navoženje niza unutar dvostrukih navodnika, npr. "znakovni<br />

niz". Rezultat ovog izraza je instanca klase String s navedenim tekstom<br />

kao sadrºajem. Znakovne nizove, dakle, ne kreira se uobi£ajenim<br />

konstruktorom: new String("znakovni niz"). Ovo dodu²e radi,<br />

ali se sasvim nepotrebno stvara objekt vi²ka sam niz pod navodnicima<br />

je jedan objekt, a dodatni se stvara operatorom new.<br />

• Operator ulan£avanja, npr. "znakovni" + " " + "niz". Rezultat je novi<br />

String sadrºaja "znakovni niz". U skladu s ovime deniran je i sloºeni<br />

operator dodjele +=. Znak + koristi se dakle i za zbrajanje i za ulan-<br />

£avanje. Odluka o tome koji je smisao danog znaka donosi se prema<br />

vrsti lijevog operanda: ako je to String, operacija je ulan£avanje. U<br />

sloºenijim izrazima ne mora biti sasvim o£ito ²to je lijevi operand pa<br />

treba biti na oprezu (npr. ²to je lijevi operand drugog plusa u izrazu<br />

"a" + 2 + 3? to je izraz "a" + 2, vrste String). Ako se kao operand<br />

ulan£avanja pojavi ne²to drugo osim String-a, automatski se pretvara<br />

u odgovaraju¢i String. Ako se radi o objektu, poziva se njegova metoda<br />

toString, ako se radi o nul-referenci, pretvara se u niz "null", a i<br />

primitivni podaci se pretvaraju na smisleni na£in. Primjerice, rezultat<br />

izraza "a" + 2 + 3 je "a23". S druge strane, rezultat izraza 2 + 3 +<br />

"a" je prevoditeljeva pogre²ka jer se u ovom slu£aju radi o zbrajanju,<br />

a nizove se ne moºe pribrajati brojevima. Iz ovakve situacije moºemo<br />

se izvu¢i malim trikom: "" + 2 + 3 + "a" ¢e dati ºeljeni rezultat "23a".<br />

3.3.1 Provjera jednakosti znakovnih nizova<br />

Usporeživanje nizova nije na sli£an na£in elegantno integrirano u jezik i<br />

mora ih se usporeživati kao i ostale objekte, pozivom metode equals. Usporedba<br />

pomo¢u == samo ¢e usporediti reference, kao i ina£e. Kori²tenje<br />

pogre²nog na£ina usporedbe moºe izazvati vrlo neobi£no pona²anje ponekad<br />

ispravno, a ponekad neispravno. To je stoga ²to Java automatski<br />

optimizira kori²tenje instanci String-a pa moºe vi²estruko iskoristiti istu<br />

instancu na vi²e mjesta gdje se pojavljuje isti niz. Ako se to dogodi, provjera<br />

pomo¢u == ¢e pro¢i:<br />

Ispis 3.5: Posljedice pogre²nog kori²tenja == za usporedbu String-ova


68 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />

public static void main( String[] args )<br />

{<br />

String a = "a";<br />

String b = "b";<br />

String ab1 = "ab";<br />

String ab2 = "ab";<br />

String ab3 = a + b;<br />

}<br />

System.out.println( ab1 == ab2 ); // true<br />

System.out.println( ab1 == ab3 ); // false<br />

System.out.println( ab1.equals( ab2 ) ); // true<br />

System.out.println( ab1.equals( ab3 ) ); // true<br />

U primjeru je konstanta "ab" uspje²no iskori²tena dvaput i za ab1 i za<br />

ab2. Mežutim, kad taj isti niz sastavimo ulan£avanjem iz druga dva, kreira<br />

se novi objekt pa referenca na njega vi²e nije ista.<br />

3.3.2 Nepromjenjivost instanci String-a<br />

Klasa String dizajnirana je da bude nepromjenjiva (eng. immutable) njene<br />

instance nikad ne mijenjaju stanje. Ovo svojstvo uvijek je izrazito poºeljno<br />

za svaku klasu gdje je to mogu¢e. Takvi objekti imaju neka korisna svojstva<br />

koja ina£e imaju podaci primitivne vrste:<br />

• Ako se takav objekt proslijedi metodi, nema opasnosti da ¢e ga ona<br />

izmijeniti i tako neizravno i nepredviženo utjecati na funkciju pozivaju¢e<br />

metode.<br />

• Ako je neki od atributa objekta nepromjenjiv objekt, moºe ga se<br />

dati na raspolaganje vanjskom svijetu bez opasnosti da ga se izvana<br />

izmijeni i tako neizravno utje£e na stanje objekta koji ga sadrºi.<br />

• U vi²enitnom programu nema potrebe za sinkronizacijom pristupa<br />

nepromjenjivom objektu iz vi²e niti.<br />

Budu¢i da su String-ovi nepromjenjivi, jasno je da se pri svakoj promjeni<br />

vrijednosti neke String-ovske varijable stvara novi objekt. Npr. izraz<br />

s += "tri" ne¢e izmijeniti postoje¢i objekt pridijeljen varijabli s, ve¢ ¢e<br />

stvoriti novi i referencu na njega zapisati u varijablu. Ako u kodu imamo<br />

situaciju gdje se dogažaju brojne izmjene znakovnih nizova, moºemo radi<br />

²tednje radne memorije pribje¢i kori²tenju druge klase, StringBuffer, koja


POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 69<br />

modelira promjenjivi niz. U tom slu£aju, mežutim, ne¢emo se mo¢i koristiti<br />

elegantnim jezi£nim konstrukcijama kao kad radimo sa String-om.<br />

3.4 Klasa System<br />

Ova klasa ne sluºi za instanciranje, ve¢ sadrºi korisne stati£ke metode i<br />

atribute koji ostvaruju vezu s uslugama ra£unalske platforme na kojoj se<br />

izvr²ava Javin program. Pregled najvaºnijih £lanova klase:<br />

• atributi in, out i err: veza na standardni ulaz, izlaz i izlaz za pogre²ke.<br />

Vidjeti 7.3<br />

• metoda currentTimeMillis: trenutno sistemsko vrijeme<br />

• metoda exit( int ): prekid izvr²avanja programa. Prosliježeni broj je<br />

statusni kod s kojim program zavr²ava. Kod 0 zna£i da program zavr-<br />

²ava na redovan na£in; ostali kodovi signaliziraju neku vrstu pogre²ke.<br />

Uobi£ajeno je npr. koristiti kod -1 ako se prekida zbog pogre²ke.<br />

• metoda getenv: dohva¢anje sistemskih varijabli (environment variables)<br />

• metoda arrayCopy: ekasno kopiranje sadrºaja iz jednog polja u drugo<br />

Detalje o ovim i drugim £lanovima klase treba potraºiti u dokumentaciji<br />

[1].<br />

3.5 Klase-omota£i primitivnih vrsta, autoboxing<br />

Java razlikuje primitivne od referentnih vrsta podataka i stoga brojevi,<br />

logi£ke vrijednosti i znakovi nisu objekti. Mežutim, mnoga programska rje-<br />

²enja, kao npr. kolekcije podataka (vidjeti poglavlje 5), rade isklju£ivo s<br />

objektima. Da bi se ipak u takvim slu£ajevima moglo baratati i njima,<br />

u Javinu biblioteku klasa ugražene su posebne klase £ije instance predstavljaju<br />

podatke primitivnih vrsta. Radi se o sljede¢im klasama u paketu<br />

java.lang:<br />

1. Boolean<br />

2. Byte


70 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />

3. Character<br />

4. Short<br />

5. Integer<br />

6. Long<br />

7. Float<br />

8. Double<br />

Jedna instanca predstavlja jednu vrijednost. Instance su nepromjenjive,<br />

²to dodatno olak²ava njihovo tretiranje kao da su podaci primitivne vrste<br />

(vidjeti odjeljak 3.3.2). Na primjer, ako negdje moramo koristiti objekte,<br />

a podatak kojim trebamo baratati je vrste int, moºemo koristiti omota£<br />

Integer na sljede¢i na£in:<br />

int i = 1;<br />

Integer intObj = i; // omotavanje u objekt<br />

... radimo ne²to s omotanom vrijednosti ...<br />

int j = intObj; // razmotavanje natrag u primitivnu vrijednost<br />

Integer intObj2 = i + 1;<br />

int k = intObj + intObj2;<br />

Java dozvoljava implicitnu konverziju izmežu primitivnih vrijednosti i<br />

njihovih omota£a u izrazu dodjele vrijednosti. To jezi£no svojstvo naziva se<br />

autoboxing/unboxing. Dozvoljeno je i izvoditi ra£unske operacije nad omota£ima,<br />

kao ²to se vidi u posljednjem retku primjera.<br />

3.6 Konverzija izmežu podatka i njegovog znakovnog<br />

zapisa<br />

Klase omota£a primitivnih vrsta sadrºe i stati£ke metode koje parsiraju<br />

znakovne zapise vrijednosti. Primjeri:<br />

int i = Integer.parseInt( "-2" );<br />

double d = Double.parseDouble( "2.021e-2" );<br />

boolean b = Boolean.parseBoolean( "true" );<br />

O£ekivani ispis:<br />

i = -2, d = 0.02021, b = true


POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 71<br />

Konverzija u suprotnom smjeru, iz primitivne vrijednosti u znakovni<br />

niz, moºe se obaviti na dva na£ina. Jedna je memorijski ekonomi£nija, ali<br />

druga ima elegantniji zapis:<br />

double d = 2.02;<br />

String s1 = String.valueOf( d );<br />

String s2 = "" + d;<br />

U drugom retku koristi se stati£ka metoda String.valueOf. Metoda je<br />

preoptere¢ena i ima verziju za svaku primitivnu vrstu podataka. U tre¢em<br />

retku koristi se ve¢ prikazano svojstvo operacije ulan£avanja (u odjeljku<br />

3.3) da automatski pretvara sve primitivne podatke u nizove.


Poglavlje 4<br />

Iznimke<br />

U prakti£ki svakom programu dobar dio koda posve¢en je provjeri ispravnosti<br />

neke situacije i prijavljivanju pogre²aka ako se uo£i nepravilnost. Na<br />

primjer, tipi£no je provjeriti je li prosliježeni cijeli broj u potrebnom opsegu.<br />

U na²em slu£aju broja£a (ispis 2.13) bit ¢e potrebno npr. provjeriti<br />

da je ciklus brojanja prosliježen metodi setModulus pozitivan broj:<br />

package hr.fer.tel.jsem;<br />

public class ModuloCounter<br />

extends AbstractCounter<br />

{<br />

private int modulus = 10;<br />

Ispis 4.1: ModuloCounter s provjerom parametra<br />

public ModuloCounter( int initialState )<br />

{<br />

super( initialState );<br />

}<br />

public ModuloCounter()<br />

{<br />

}<br />

public void setModulus( int newModulus )<br />

{<br />

if ( newModulus < 0 )<br />

System.out.println( "Negativan ciklus brojanja!" );<br />

else<br />

this.modulus = newModulus;<br />

}<br />

void setState( int newState )<br />

{<br />

73


74 POGLAVLJE 4. IZNIMKE<br />

}<br />

}<br />

this.counterState = newState % this.modulus;<br />

Na prvi pogled ovo moºe izgledati kao savr²eno rje²enje ako je broj<br />

krivi, ispisuje se pogre²ka, ina£e se obavlja zatraºena akcija. Mežutim, u<br />

stvarnosti ovo je prili£no neupotrebljivo. Pozivaju¢a metoda uop¢e ne¢e<br />

imati informaciju o tome je li naredba za postavljanje ciklusa uspje²no izvr²ena.<br />

Program ¢e nastaviti dalje i, naravno, raditi neispravno. Programer,<br />

gledaju¢i ispis, mo¢i ¢e samo ustanoviti da je u jednom trenutku metoda<br />

setModulus pozvana s pogre²nom vrijedno²¢u. Tijekom izvr²avanja ta metoda<br />

se pozvala tko zna koliko puta i s kojih sve lokacija.<br />

Malo bolji pristup, koji se npr. koristi u jeziku C, je da metoda ne<br />

bude void, nego int i da broj koji vra¢a ovisi o uspje²nosti izvr²enja. Svaka<br />

pozivaju¢a metoda tada moºe provjeriti je li sve dobro zavr²ilo. Ovaj na£in<br />

opet ima niz mana npr. to zna£i da za svaki pojedini poziv bilo koje<br />

metode koja bi se mogla neuspje²no izvr²iti treba imati po jedan blok if<br />

za provjeru. Dodatni je problem ²to se sad povratna vrijednost ne moºe<br />

koristiti za regularne namjene mnoge metode nisu void pa da ih moºemo<br />

lako preurediti da vra¢aju int.<br />

4.1 Bacanje iznimke prijava iznimne situacije<br />

U jeziku Java za rje²avanje ovih problema postoji poseban mehanizam <br />

iznimke. Gornja metoda setModulus u Javi bi se izvela ovako:<br />

public class ModuloCounter<br />

{<br />

...<br />

public void setModulus( int newModulus )<br />

{<br />

if ( newModulus < 0 )<br />

throw new IllegalArgumentException( "Negativan ciklus brojanja" );<br />

}<br />

this.modulus = newModulus;<br />

...<br />

}


POGLAVLJE 4. IZNIMKE 75<br />

Primje¢ujemo klju£nu rije£ throw: njome se baca iznimku. Ovo je sasvim<br />

nov i neobi£an pojam i trebat ¢e malo vi²e vremena da legne po£etniku.<br />

Bacanjem iznimke prekida se izvr²avanje metode i ona, ako je i trebala<br />

vratiti neku povratnu vrijednost, u ovom slu£aju ne¢e ju vratiti, nego ¢e<br />

se u pozivaju¢oj metodi pojaviti ta ba£ena iznimka kao rezultat metode.<br />

Ako tamo nema nikakvog koda speci£nog za baratanje iznimkama, dogodit<br />

¢e se ne²to prili£no neo£ekivano: izvr²avanje te pozivaju¢e metode takožer<br />

¢e odmah biti prekinuto i ona ¢e ponovo baciti tu istu iznimku svojoj<br />

pozivaju¢oj metodi. Ako dakle imamo ovakav slu£aj:<br />

public class ModuloCounter<br />

{<br />

...<br />

Ispis 4.2: Bacanje i ponovno bacanje iznimke<br />

public void setModulus( int newModulus )<br />

{<br />

if ( newModulus < 0 )<br />

throw new IllegalArgumentException( "Negativan ciklus brojanja" );<br />

}<br />

this.modulus = newModulus;<br />

}<br />

...<br />

public class AppThatUsesTheCounter<br />

{<br />

private static boolean intermediateMethod( int i )<br />

{<br />

ModuloCounter mc = new ModuloCounter();<br />

mc.setModulus( i );<br />

System.out.println( "intermediateMethod uspje²no zavr²ava" );<br />

return true;<br />

}<br />

}<br />

public static void main( String[] args )<br />

{<br />

boolean b = intermediateMethod( -1 );<br />

System.out.println( "intermediateMethod je vratila " + b );<br />

}<br />

pokretanje programa rezultirat ¢e sljede¢im ispisom:<br />

Ispis 4.3: Izvje²taj o ba£enoj iznimci<br />

Exception in thread "main" java.lang.IllegalArgumentException: Negativan ciklus brojanja


76 POGLAVLJE 4. IZNIMKE<br />

at hr.fer.tel.jsem.AppThatUsesTheCounter.setModulus(AppThatUsesTheCounter.java:8)<br />

at hr.fer.tel.jsem.AppThatUsesTheCounter.intermediateMethod(AppThatUsesTheCounter.java:13)<br />

at hr.fer.tel.jsem.AppThatUsesTheCounter.main(AppThatUsesTheCounter.java:18)<br />

Primijetimo da se poruke intermediateMethod je vratila... i intermediateMethod<br />

uspje²no zavr²ava nisu ispisale. U intermediateMethod iznimka<br />

se pojavila u prvom retku i, u nedostatku koda koji ju obražuje, tu je metoda<br />

zavr²ila ponovnim bacanjem iste iznimke (eng. rethrow). Iznimka je<br />

tako stigla do metode main, takožer u njenom prvom retku. Budu¢i da od<br />

nje nema kamo dalje (to je metoda od koje je program zapo£eo), £itav<br />

program je prekinut uz prethodni ispis detaljnog izvje²taja o tome gdje<br />

je to£no do²lo do iznimke (cjelokupan redoslijed pozivanja metoda i to£no<br />

mjesto u kodu gdje se svaki poziv dogaža), koja je vrsta iznimke (Illegal-<br />

ArgumentException) i njen pobliºi opis (Negativan ciklus brojanja). Sve se<br />

to dogodilo automatski, bez ijednog retka koda vezanog uz obradu iznimke.<br />

Varijabli b u metodi main o£ito nije mogla biti dodijeljena nijedna vrijednost<br />

jer intermediateMethod nije zavr²ila vra¢anjem povratne vrijednosti.<br />

Iz toga je ve¢ jasno da se izvr²avanje ne moºe nastaviti normalnim putem<br />

jer je varijabla ostala neinicijalizirana, a ve¢ u sljede¢em retku se njena<br />

vrijednost treba ispisati.<br />

4.2 Hvatanje iznimke obrada iznimne situacije<br />

Prikazani na£in kori²tenja iznimke jo² uvijek nije previ²e svrsishodan jer ¢e<br />

uslijed svakog bacanja iznimke program pucati. U ve¢ini realnih situacija<br />

trebat ¢e se na odgovaraju¢i na£in pobrinuti za iznimnu situaciju i zatim<br />

nastaviti s izvr²avanjem programa. Klju£na je prednost ²to moºemo odabrati<br />

u kojoj metodi ¢emo napraviti provjeru to ne treba biti niti metoda<br />

gdje je do²lo do pogre²ke, niti metoda koja ju je izravno pozvala. Moºemo<br />

npr. sve iznimke obraživati u metodi main:<br />

Ispis 4.4: Hvatanje iznimke<br />

public static void main( String[] args )<br />

{<br />

boolean b;<br />

try<br />

{<br />

b = intermediateMethod( -1 );


POGLAVLJE 4. IZNIMKE 77<br />

System.out.println( "intermediateMethod je vratila " + b );<br />

}<br />

catch ( IllegalArgumentException e )<br />

{<br />

System.err.println( "Iznimka!");<br />

e.printStackTrace();<br />

}<br />

b = intermediateMethod( 1 );<br />

System.out.println(<br />

"Drugi poku²aj: intermediateMethod je vratila " + b );<br />

}<br />

Tu se pojavljuje nova konstrukcija, koja koristi i dvije nove klju£ne<br />

rije£i: try i catch. Pomo¢u try najavljuje se blok koda koji ¢e se poku²ati<br />

izvr²iti, ali u kojem bi moglo do¢i i do iznimke.<br />

U ovom trenutku potrebno je napomenuti par detalja u vezi s karakterom<br />

iznimke. U Javi to je jednostavno objekt kao i svaki drugi instanca<br />

klase java.lang.Exception ili bilo koje njene potklase. Iznimku se kreira na<br />

uobi£ajen na£in, operatorom new. Da je iznimka normalan objekt najjasnije<br />

se vidi po izjavi<br />

e.printStackTrace();<br />

Tu smo pozvali metodu nad objektom iznimke. Ovo je vrlo korisna metoda<br />

jer ona ispisuje izvje²taj o iznimci, kao ²to je pokazano u ispisu 4.3. Ranije<br />

je prikazan primjer u kojem se izvje²taj ispisuje automatski, prije prekida<br />

programa. Ovom metodom moºemo eksplicitno zatraºiti takav izvje²taj, a<br />

bez prekidanja programa. Izvje²taj se ²alje na standardni izlaz za pogre²ke<br />

(System.err).<br />

Iznimku se hvata klju£nom rije£ju catch. ƒitava konstrukcija je catch<br />

( VrstaIznimke imeParametra ) i vrlo je srodna konstrukciji za deklaraciju<br />

parametra metode. Sama iznimka koja ¢e biti uhva¢ena moºe biti instanca<br />

navedene klase ili bilo koje potklase isto vrijedi i za parametar metode.<br />

Nakon ove konstrukcije slijedi blok koda u kojem se imeParametra moºe<br />

koristiti kao i svaka druga varijabla. U gornjem primjeru nad njom se poziva<br />

metoda getMessage, kojom raspolaºe svaka iznimka i vra¢a poruku koja joj<br />

je dodijeljena prilikom bacanja.<br />

Ako je iznimka uspje²no uhva¢ena u odgovaraju¢em bloku catch, ne¢e<br />

se dalje bacati. Nakon izvr²enja ovog bloka program dalje nastavlja normalno,<br />

izvr²avanjem koda koji slijedi nakon £itave konstrukcije try...catch.<br />

Ostatak koda nakon pojave iznimke pa do kraja bloka try ne¢e se izvr²iti<br />

a to je i bio cilj. U protivnom bismo se ponovo susreli s problemom ²to


78 POGLAVLJE 4. IZNIMKE<br />

u£initi npr. s neinicijaliziranom varijablom b.<br />

S druge strane, ako ba£ena iznimka nije one vrste koja se hvata catchom,<br />

pona²anje ¢e biti kao da tog bloka i nema iznimka ¢e se proslijediti<br />

dalje, pozivaju¢oj metodi.<br />

4.3 Zavr²ne radnje prije bacanja iznimke blok<br />

finally<br />

Blok try moºemo popratiti i posebnim blokom koda koji ¢e se sigurno<br />

izvr²iti, bez obzira je li do²lo do iznimke. To je prvenstveno korisno za<br />

slu£aj kad dože do iznimke jer u tom bloku imamo ²ansu obaviti neku<br />

zavr²nu radnju npr. tipi£an slu£aj je zatvaranje datoteka, internetskih<br />

konekcija i sl. prije nego ²to se metoda prekine bacanjem iznimke. Za to<br />

sluºi klju£na rije£ finally:<br />

public class ModuloCounter<br />

{<br />

...<br />

Ispis 4.5: Blok finally<br />

private void setModulus( int newModulus )<br />

{<br />

if ( newModulus < 0 )<br />

throw new IllegalArgumentException( "Negativan ciklus brojanja" );<br />

}<br />

}<br />

...<br />

public class AppThatUsesTheCounter<br />

{<br />

private static boolean intermediateMethod( int i )<br />

{<br />

try<br />

{<br />

ModuloCounter mc = new ModuloCounter();<br />

mc.setModulus( i );<br />

}<br />

finally<br />

{<br />

System.out.println( "intermediateMethod zavr²ava" );<br />

}<br />

return true;<br />

}


POGLAVLJE 4. IZNIMKE 79<br />

}<br />

public static void main( String[] args )<br />

{<br />

boolean b;<br />

try<br />

{<br />

b = intermediateMethod( -1 );<br />

System.out.println( "intermediateMethod je vratila " + b );<br />

}<br />

catch ( IllegalArgumentException e )<br />

{<br />

System.out.println( "Iznimka: " + e.getMessage() );<br />

}<br />

b = intermediateMethod( 1 );<br />

System.out.println(<br />

"Drugi poku²aj: intermediateMethod je vratila " + b );<br />

}<br />

O£ekivan ispis za ovaj, ve¢ dosta sloºen slu£aj je:<br />

intermediateMethod zavr²ava<br />

Iznimka: Negativan ciklus brojanja<br />

intermediateMethod zavr²ava<br />

Drugi poku²aj: intermediateMethod je vratila true<br />

Vidimo kako se sada tekst intermediateMethod zavr²ava ispisuje u<br />

oba slu£aja i kad je do²lo do iznimke, i kad nije. U gornjem primjeru blok<br />

finally kori²ten je samostalno, kao i blok catch u metodi main, ali op¢enito<br />

oni se mogu i kombinirati. Jedino treba po²tivati pravilo da finally dože<br />

na kraj, iza catch.<br />

U primjeru je kori²tena jo² jedna metoda denirana za sve iznimke <br />

getMessage. Ona vra¢a znakovni niz koji je prosliježen konstruktoru prilikom<br />

bacanja iznimke.<br />

4.4 Vi²estruki blokovi catch<br />

Jo² jedna prednost mehanizma iznimki je u tome ²to moºemo imati podulji<br />

blok koda u kojem se mogu pojavljivati raznorazne iznimke bez potrebe da<br />

se svaka obražuje tamo gdje se pojavila. Da bi ta prednost bila u potpunosti<br />

iskoristiva, mogu¢e je nakon bloka try imati ne jedan, nego proizvoljno<br />

mnogo blokova catch, svaki za svoju vrstu iznimke. Od svih njih izvr²it ¢e<br />

najvi²e jedan, i to prvi £ija vrsta parametra odgovara vrsti iznimke. Realisti£an<br />

primjer u kojem je potrebno hvatati razne iznimke bio bi prili£no<br />

kompleksan pa ¢emo ovdje dati samo shematski primjer:


80 POGLAVLJE 4. IZNIMKE<br />

Ispis 4.6: Vi²estruki blokovi catch<br />

public int someMethod()<br />

{<br />

try<br />

{<br />

method1();<br />

method2();<br />

method3();<br />

return 0;<br />

}<br />

catch ( IllegalArgumentException e )<br />

{<br />

System.err.println( "Pogre²an argument" );<br />

}<br />

catch ( NullPointerException e )<br />

{<br />

System.err.println( "Nul-referenca" );<br />

}<br />

catch ( FileNotFoundException e )<br />

{<br />

System.err.println( "Datoteka ne postoji" );<br />

}<br />

catch ( Exception e )<br />

{<br />

System.err.println( "Nepredvižena iznimka" );<br />

}<br />

finally<br />

{<br />

System.err.println( "Blok try zavr²ava" );<br />

}<br />

return -1;<br />

}<br />

Uo£imo da se u posljednjem bloku catch hvata vrsta Exception. Taj blok<br />

je tu da uhvati bilo koju iznimku koju nije uhvatio nijedan prethodni blok.<br />

Vaºno je, naravno, da taj blok bude posljednji, u protivnom ¢e uvijek on<br />

hvatati iznimku.<br />

Potpuni redoslijed izvr²avanja je sljede¢i: prvo se normalno izvr²ava kod<br />

bloka try. Ako ne dože do iznimke, u posljednjem retku bloka try metoda<br />

treba uredno zavr²iti s povratnom vrijednosti 0. Mežutim, prije toga izvr-<br />

²ava se blok finally. Ako dože do iznimke, izvr²ava se odgovaraju¢i blok<br />

catch i nakon njega finally. U tom slu£aju nakon izvr²enja finally izvr²avanje<br />

nastavlja redovitim putem i nailazi se na return -1 pa metoda vra¢a<br />

vrijednost -1. Ako se pojavi iznimka koju se ne hvata, izvr²ava se blok<br />

finally i nakon toga metoda se prekida ponovnim bacanjem iste iznimke.<br />

Vidimo dakle da se blok finally izvr²ava apsolutno uvijek, bez obzira za-


POGLAVLJE 4. IZNIMKE 81<br />

vr²ava li blok try na redovan ili izniman na£in i bez obzira je li iznimka<br />

uhva¢ena ili nije.<br />

4.5 Najava iznimke; provjeravana i neprovjeravana<br />

iznimka<br />

Iznimke se u Javi dijele na dvije podvrste: provjeravane (eng. checked) i<br />

neprovjeravane (eng. unchecked). Sve neprovjeravane iznimke izvedene su iz<br />

RuntimeException; ostale su provjeravane. Sama RuntimeException je izravna<br />

potklasa spomenute klase Exception.<br />

Ako se moºe dogoditi da metoda baci provjeravanu iznimku, tada ju je<br />

potrebno najaviti klju£nom rije£ju throws:<br />

import java.io.*;<br />

...<br />

Ispis 4.7: Najava iznimke<br />

public static void fileMethod()<br />

throws FileNotFoundException<br />

{<br />

File f = new File( "/hr/fer/tel/file1.txt" );<br />

InputStream fis = new FileInputStream( f );<br />

...<br />

}<br />

U primjeru se koriste Javine ugražene klase za baratanje datotekama<br />

(iz paketa java.io). U konstruktoru klase FileInputStream najavljeno je da<br />

bi mogao baciti provjeravanu iznimku FileNotFoundException (ako zatraºena<br />

datoteka ne postoji). Budu¢i da tu iznimka ne hvata, na²a metoda takožer<br />

mora najaviti da bi mogla baciti ovu iznimku, u protivnom ¢e prevoditelj<br />

prijaviti pogre²ku da metoda moºe baciti doti£nu iznimku, ali to ne<br />

najavljuje.<br />

Iz navedenoga je jasno zna£enje pridjeva provjeravana: za takvu iznimku<br />

kompilator provjerava moºe li do¢i do njenog bacanja i inzistira da<br />

to bude najavljeno. Na² primjeri£ni kod ne sadrºi eksplicitnu izjavu throw<br />

new FileNotFoundException(), ali kompilatoru je dovoljna £injenica da se koristi<br />

konstruktor koji tu iznimku najavljuje. Op¢enito, spomenuti konstruktor<br />

moºe biti izveden i tako da nikad ne baci iznimku, ali kompilatoru je<br />

dovoljna sama najava. Isto tako, svaka metoda koja poziva na²u metodu


82 POGLAVLJE 4. IZNIMKE<br />

fileMethod morat ¢e se pobrinuti za ovu iznimku £ak i ako izbacimo redak<br />

u kojem se koristi sporni konstruktor.<br />

Najavljena iznimka korisna je za slu£ajeve kad do nje moºe do¢i tijekom<br />

normalnog, ispravnog izvr²avanja programa. Na primjer, u na²em<br />

slu£aju programer ne moºe unaprijed znati ho¢e li potrebna datoteka postojati<br />

svaki put kad se program izvr²ava. To op¢enito i ne mora zna£iti<br />

da je do²lo do pogre²ke radi se jednostavno o izvanrednoj situaciji koju<br />

se zahvaljuju¢i mehanizmu iznimaka moºe obraživati na mjestu gdje je to<br />

najprakti£nije. Kori²tenjem provjeravane iznimke posti¢i ¢emo to da programer<br />

ne moºe zabunom zaboraviti zapisati kod koji obražuje tu uvijek<br />

prisutnu mogu¢nost.<br />

S druge strane, neprovjeravana iznimka koristi se za slu£ajeve u kojima<br />

se prijavljuje neispravnost situacija do koje u ispravnom programu jednostavno<br />

ne smije do¢i. Takav je na² raniji primjer s metodom setModulus<br />

ona je dizajnirana da prima pozitivan broj i nijedan smislen poziv ne<br />

moºe biti s negativnim brojem. Dakle, mora da se radi o programerskoj<br />

pogre²ci. Mogu¢nosti za ovakve pogre²ke vrebaju na svakom koraku i kod<br />

bi bio neprihvatljivo nepregledan kad bi se inzistiralo na najavi ili obradi<br />

svake mogu¢e takve iznimke.<br />

U praksi uvijek postoje i rubni slu£ajevi, gdje je nejasno koju vrstu<br />

iznimke koristiti. Java dozvoljava da se i neprovjeravane iznimke najavljuju<br />

kao pomo¢ programeru koji koristi takvu metodu, da bude spreman na<br />

mogu¢nost pojave doti£ne iznimke, iako nije duºan i²ta poduzeti. Najava<br />

neprovjeravane iznimke nema nikakvog utjecaja na kompilator iznimka i<br />

dalje ostaje neprovjeravana.<br />

4.5.1 Najava vi²e iznimki<br />

Metoda moºe najaviti proizvoljan broj iznimki. Navodi ih se redom, odvojene<br />

zarezom, npr.<br />

public void throwingMethod()<br />

throws FileNotFoundException, ClassNotFoundException, NamingException<br />

{<br />

...<br />

}


POGLAVLJE 4. IZNIMKE 83<br />

4.6 Klasna hijerarhija iznimki<br />

Dosad je spomenuto nekoliko detalja o hijerarhiji klasa iznimki. Spomenuta<br />

je klasa Exception kao glavna klasa iz koje su izvedene druge iznimke. Mežutim,<br />

potpuna slika uklju£uje jo² nekoliko klasa. Prije svega tu je klasa<br />

Throwable, iz koje su izvedene to£no dvije klase: Error i Exception. Dakle,<br />

Throwable je u korijenu £itave klasne hijerarhije iznimki. Klasa Error i njene<br />

potklase rezervirane su za uporabu od strane Javinog virtualnog stroja i<br />

nisu namijenjene da ih se eksplicitno baca izjavom throw. Cjelokupna hijerarhija<br />

klasa £ije instance se mogu baciti prikazana je na slici 4.1. Dodatne<br />

klase iznimki koje uvode razvijatelji neizostavno moraju biti potklase od<br />

Exception.<br />

Throwable<br />

Error<br />

Exception<br />

pogreške koje<br />

signalizira JVM<br />

provjeravane<br />

iznimke<br />

RuntimeException<br />

neprovjeravane<br />

iznimke<br />

Slika 4.1: UML-ov dijagram klasa £ije instance se mogu baciti<br />

4.7 Kori²tenje ugraženih i vlastitih iznimki<br />

U Javinoj biblioteci postoji ve¢ mno²tvo iznimki, provjeravanih i neprovjeravanih.<br />

Nekoliko njih, neprovjeravanih, ima op¢enit karakter i u kodu ih<br />

treba koristiti gdje god zna£enje odgovara karakteru pogre²ke:


84 POGLAVLJE 4. IZNIMKE<br />

• NullPointerException baca se kad god se problem sastoji od toga da<br />

je neka referenca jednaka null umjesto da pokazuje na neki objekt.<br />

• IllegalArgumentException baca se kad se ustanovi da je metoda pozvana<br />

s pogre²nom vrijednosti nekog argumenta vrijednosti koja je<br />

protivna pravilima pozivanja doti£ne metode. U ovom poglavlju imali<br />

smo primjer pozivanja setModulus s negativnim brojem.<br />

• IllegalStateException baca se kad se neku metodu pozove dok je<br />

objekt u takvom stanju koje ne dopu²ta njezino pozivanje. Recimo da<br />

imamo klasu koja predstavlja broja£ koji odbroji do neke kona£ne vrijednosti<br />

i nakon toga ne moºe dalje, ve¢ se mora poni²titi. Ako u trenutku<br />

kad je broja£ ve¢ do²ao do gornje granice pozovemo increment,<br />

to bi bila situacija za bacanje ove iznimke.<br />

• ClassCastException baca se ako je metoda pozvana s instancom krive<br />

klase. To se dogaža tipi£no kad se radi o metodi koja je dio nekog<br />

su£elja pa formalno prima parametar op¢enitije vrste od one s kojom<br />

stvarno moºe raditi.<br />

• UnsupportedOperationException baca se ako je metoda pozvana nad<br />

objektom koji ju ne implementira. To se tipi£no dogaža kad objekt<br />

najavljuje da implementira neko su£elje, ali neka od metoda iz su£elja<br />

nije primjerena za takav objekt pa ju nema smisla implementirati.<br />

Za situacije speci£ne za softverski proizvod koji razvijamo treba denirati<br />

vlastitu klasu iznimke. Pravilo je da za £itav proizvod postoji samo<br />

jedna ili eventualno, za ve¢e projekte, nekoliko iznimki. Ako ih je vi²e, preporu£a<br />

se da sve budu izvedene iz jedne, op¢enite. Deniranje vlastite iznimke<br />

vrlo je jednostavno dovoljno je kreirati klasu izvedenu iz Exception<br />

za provjeravane odnosno RuntimeException za neprovjeravane iznimke. U<br />

klasi treba samo denirati konstruktore koji primaju iste parametre kao<br />

ve¢ postoje¢i konstruktori u natklasi i samo prosliježuju poziv na njih.<br />

Primjer:<br />

public class CounterException<br />

extends RuntimeException<br />

{<br />

public CounterException()<br />

Ispis 4.8: Deniranje vlastite iznimke


POGLAVLJE 4. IZNIMKE 85<br />

{<br />

}<br />

super();<br />

}<br />

public CounterException( String message )<br />

{<br />

super( message );<br />

}<br />

public CounterException( String message, Throwable cause )<br />

{<br />

super( message, cause );<br />

}<br />

public CounterException( Throwable cause )<br />

{<br />

super( cause );<br />

}<br />

Ovime je denirana neprovjeravana iznimka CounterException. Denirana<br />

su £etiri standardna konstruktora i sve ²to rade je da pozivaju isti takav<br />

konstruktor u natklasi.


Poglavlje 5<br />

Kolekcije objekata<br />

Jedna vrlo zna£ajna stvar u svakom programskom jeziku su kolekcije podataka.<br />

Kolekcija je jednostavno skupina objekata koju se moºe tretirati<br />

kao cjelinu. Osnovni primjeri su lista i skup, a Java podrºava jo² i repove.<br />

Struktura srodna kolekciji je i mapa (skup parova klju£-vrijednost).<br />

Jezik Java pruºa dobru podr²ku za kolekcije objekata. Kolekcije primitivnih<br />

podataka podrºane su neizravno putem posebnih objekata-omota£a<br />

primitivnih podataka. U biblioteci klasa, u paketu java.util, nalazi se pove¢i<br />

broj su£elja i klasa koje sa£injavaju sustav za manipulaciju kolekcija,<br />

tzv. Collections Framework.<br />

5.1 Su£elja Collection, List i Set<br />

Su£elje Collection jedno je od temeljnih su£elja u Collections Framework-u.<br />

Apstrahira mogu¢nosti zajedni£ke za sve kolekcije liste, skupove i repove.<br />

To su£elje je denirano ponajprije iz razloga koji je iznesen u odjeljku 1.7 <br />

da putem varijable (parametra) ove vrste moºemo baratati bilo kojom kolekcijom<br />

u opsegu funkcionalnosti koji je zajedni£ki za sve njih. Uz kolekcije<br />

veºu se i dosad neprikazana jezi£na svojstva Jave tzv. generics. Pogledajmo<br />

metodu u kojoj se demonstrira nekoliko osnovnih tehnika baratanja<br />

kolekcijom:<br />

Ispis 5.1: Osnovno baratanje kolekcijom<br />

static void manipulateCollection( Collection col )<br />

{<br />

String msg = "Poruka";<br />

boolean b = col.add( msg );<br />

87


88 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />

if ( b )<br />

System.out.println( "Poruka dodana u kolekciju" );<br />

System.out.println( "Sadrºi li kolekcija poruku? " +<br />

col.contains( msg ) );<br />

b = col.add( msg );<br />

if ( b )<br />

System.out.println( "Poruka dodana u kolekciju drugi put" );<br />

System.out.println( "Broj elemenata u kolekciji: " + col.size() );<br />

b = col.remove( msg );<br />

if ( b )<br />

System.out.println( "Poruka uklonjena iz kolekcije" );<br />

System.out.println( "Sadrºi li kolekcija poruku? " +<br />

col.contains( msg ) );<br />

}<br />

System.out.println( "Je li kolekcija prazna? " + col.isEmpty() );<br />

Sintaksu vezanu uz generics vidimo u prvom retku, u deklaraciji parametra<br />

metode: Collection col. Time nazna£ujemo da kolekcija sadrºi<br />

instance klase String. Vrsta Collection je tzv. parametrizirana<br />

vrsta parametar je vrsta objekata koji se u njoj nalaze. Kôd demonstrira<br />

kori²tenje metoda su£elja Collection: add, contains, isEmpty, remove, size.<br />

Ispis ¢e se razlikovati ovisno o tome koja konkretna implementacija kolekcije<br />

se proslijedi metodi. Razlika ¢e prije svega biti izmežu lista i skupova.<br />

Napravimo ovakvu metodu main da to ispitamo:<br />

Ispis 5.2: Lista i skup<br />

public static void main( String[] args )<br />

{<br />

List list1 = new ArrayList();<br />

List list2 = new LinkedList();<br />

}<br />

Set set1 = new HashSet();<br />

Set set2 = new TreeSet();<br />

System.out.println( "Poziv s ArrayList:" );<br />

manipulateCollection( list1 );<br />

System.out.println( "\nPoziv s LinkedList:" );<br />

manipulateCollection( list2 );<br />

System.out.println( "\nPoziv s HashSet:" );<br />

manipulateCollection( set1 );<br />

System.out.println( "\nPoziv s TreeSet:" );<br />

manipulateCollection( set2 );


POGLAVLJE 5. KOLEKCIJE OBJEKATA 89<br />

Ovdje je uveden £itav niz novih vrsta podataka. List i Set su su£elja<br />

izvedena iz su£elja Collection. Svako od njih dodaje metode speci£ne za<br />

listu odnosno skup. ArrayList i LinkedList su konkretne klase koje implementiraju<br />

listu. U praksi se rutinski koristi ArrayList koja elemente liste<br />

drºi u polju (eng. array), a LinkedList samo iznimno, kad neko od svojstava<br />

ArrayList ne zadovoljava (npr. ako ¢e postojati velike oscilacije u<br />

broju pospremljenih elemenata). HashSet i TreeSet su konkretne klase koje<br />

implementiraju skup. Rutinski se koristi HashSet, na bazi he²-tablice. Ovo je<br />

jedan primjer klase koja ne¢e ispravno raditi ako ju koristimo s objektima<br />

koji nemaju ispravno izvedene metode equals i hashCode (vidjeti odjeljak<br />

3.2). TreeSet je speci£an po tome ²to sortira svoje elemente (implementira<br />

i posebno su£elje za sortirane skupove SortedSet). Da bi sortiranje bilo<br />

mogu¢e, objekti trebaju imati deniran prirodni poredak (vidjeti odjeljak<br />

5.6.1). Svi hijerarhijski odnosi spominjanih referentnih vrsta prikazani su<br />

na slici 5.1.<br />

Za ArrayList i LinkedList dobit ¢emo sljede¢i ispis:<br />

Poruka dodana u kolekciju<br />

Sadrºi li kolekcija poruku? true<br />

Poruka dodana u kolekciju drugi put<br />

Broj elemenata u kolekciji: 2<br />

Poruka uklonjena iz kolekcije<br />

Sadrºi li kolekcija poruku? true<br />

Poruka uklonjena iz kolekcije drugi put<br />

Je li kolekcija prazna? true<br />

Za HashSet i TreeSet ispis ¢e izgledati druga£ije:<br />

Poruka dodana u kolekciju<br />

Sadrºi li kolekcija poruku? true<br />

Broj elemenata u kolekciji: 1<br />

Poruka uklonjena iz kolekcije<br />

Sadrºi li kolekcija poruku? false<br />

Je li kolekcija prazna? true<br />

Primijetimo da poruka nije dodana/uklonjena po drugi put jer skup po<br />

deniciji sadrºi elemente bez ponavljanja. Dodatna osobina skupa je da ne<br />

zadrºava poredak kojim su elementi dodavani u njega (za razliku od liste).<br />

U metodi main kori²ten je udoma¢eni pristup da se za vrstu varijable<br />

uvijek koristi su£elje tipi£no List ili Set, ovisno o namjeni a nikada<br />

konkretne vrste ArrayList ili HashSet. Same klase ne deniraju gotovo nijednu<br />

dodatnu metodu i gotovo nikad nema razloga koristiti ih za vrstu<br />

varijable (ili parametra, atributa itd.). Njihovim kori²tenjem samo onemogu¢ujemo<br />

kasniji jednostavan prelazak na druga£iju implementaciju.


90 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />

«interface»<br />

Collection<br />

«interface»<br />

SortedSet<br />

«interface»<br />

Set<br />

«interface»<br />

List<br />

TreeSet<br />

HashSet<br />

ArrayList<br />

LinkedList<br />

Slika 5.1: UML-ov dijagram klasa za Collection-Framework<br />

5.2 Prelazak preko svih £lanova kolekcije<br />

Naj£e²¢i na£in kori²tenja kolekcije je da se priježe po svakom njenom £lanu,<br />

jedan po jedan, i obavi neki posao. Za to postoji posebna jezi£na konstrukcija,<br />

tzv. petlja enhanced for 1 :<br />

Ispis 5.3: Prolaz po kolekciji petljom enhanced for<br />

static void iterateOverCollection( Collection col )<br />

{<br />

for ( String s : col )<br />

System.out.println( "ƒlan kolekcije: " + s );<br />

}<br />

public static void main( String[] args )<br />

{<br />

List list = new ArrayList();<br />

list.add( "a" );<br />

list.add( "b" );<br />

list.add( "c" );<br />

iterateOverCollection( list );<br />

}<br />

U metodi iterateOverCollection demonstrirana je konstrukcija enhanced<br />

for. Izraz u zagradama prije dvoto£ke je deklaracija varijable koja ¢e u<br />

svakom koraku petlje sadrºati po jedan element kolekcije. ƒlan iza dvoto£ke<br />

je kolekcija po kojoj treba obaviti prelazak.<br />

1 U praksi se jo² koriste i izrazi for each i for/in


POGLAVLJE 5. KOLEKCIJE OBJEKATA 91<br />

Ako metodu main prepravimo da koristi HashSet, mo¢i ¢emo uo£iti da se<br />

po elementima ne prolazi u istom poretku u kojem smo ih dodavali:<br />

public static void main( String[] args )<br />

{<br />

Set set = new HashSet();<br />

set.add( "a" );<br />

set.add( "b" );<br />

set.add( "c" );<br />

iterateOverCollection( set );<br />

}<br />

Ispis:<br />

ƒlan kolekcije: a<br />

ƒlan kolekcije: c<br />

ƒlan kolekcije: b<br />

5.3 Kolekcije i primitivne vrste podataka<br />

Podaci primitivne vrste ne mogu se spremati u kolekcije pa se koriste<br />

objekti-omota£i podataka primitivnih vrsta. Jezi£no svojstvo autoboxing/unboxing<br />

dozvoljava sintasku po kojoj izgleda kao da se spremaju sami podaci primitivne<br />

vrste. Primjer:<br />

Ispis 5.4: Kolekcija i primitivne vrste podataka<br />

public static void main( String[] args )<br />

{<br />

List intList = new ArrayList();<br />

intList.add( 2 );<br />

intList.add( 3 );<br />

intList.add( 4 );<br />

if ( intList.contains( 3 ) )<br />

System.out.println( "Lista sadrºi broj 3" );<br />

for ( int i : intList )<br />

System.out.println( "ƒlan liste: " + i );<br />

}<br />

List boolList = new ArrayList();<br />

boolList.add( true );<br />

boolList.add( 2 == 3 );<br />

List charList = new ArrayList();<br />

charList.add( 'a' );<br />

charList.add( 'b' );<br />

char c = charList.get( 0 ); // dohva¢a prvi element liste


92 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />

5.4 Napredni prelazak po £lanovima kolekcije<br />

Konstrukcija enhanced for prikazana u odjeljku 5.2 kra¢i je zapis za ovakav<br />

kod, koji se implicitno generira prilikom prevoženja te konstrukcije i koristi<br />

poznatu sintaksu petlje for:<br />

Ispis 5.5: Prolaz po kolekciji eksplicitnim kori²tenjem iteratora<br />

static void iterateOverCollection( Collection col )<br />

{<br />

for ( Iterator iter = col.iterator(); iter.hasNext(); )<br />

{<br />

String s = iter.next();<br />

System.out.println( "ƒlan kolekcije: " + s );<br />

}<br />

}<br />

(u realnom slu£aju ne koristi se ime varijable iter, koje bi tada bilo rezervirano,<br />

nego neko ime koje se ne moºe normalno pojaviti u kodu.)<br />

Za neke speci£ne namjene potrebno je koristiti upravo ovakvu sintaksu<br />

jer nam je tako eksplicitno dostupan poseban objekt iterator po kolekciji.<br />

Njega moºemo zamisliti kao pokaziva£ na trenutni objekt u kolekciji.<br />

Preusmjerujemo ga na sljede¢i element pozivom metode next, £ija povratna<br />

vrijednost je taj sljede¢i element. Prije prvog poziva te metode iterator jo²<br />

ne pokazuje ni na jedan element, tako da se prvim pozivom dobiva prvi<br />

element.<br />

Jedna od dodatnih mogu¢nosti je metoda remove kojom se trenutni<br />

objekt uklanja iz kolekcije:<br />

Ispis 5.6: Kori²tenje iteratora za mijenjanje liste<br />

public static void main( String[] args )<br />

{<br />

List intList = new ArrayList();<br />

intList.add( 2 );<br />

intList.add( 3 );<br />

intList.add( 4 );<br />

}<br />

for ( Iterator iter = intList.iterator(); iter.hasNext(); )<br />

{<br />

int i = iter.next();<br />

if ( i == 3 )<br />

iter.remove();<br />

}<br />

for ( int i : intList )<br />

System.out.println( "ƒlan kolekcije: " + i );


POGLAVLJE 5. KOLEKCIJE OBJEKATA 93<br />

Postoje i druge mogu¢nosti, o kojima se moºe informirati u dokumentaciji<br />

[1]. Za prolazak po listama postoji i specijalizirani ListIterator koji<br />

nudi jo² vi²e mogu¢nosti, speci£nih za liste.<br />

5.5 Mapa<br />

Pojam mapa odnosi se na skup preslikavanja izmežu nekih podataka (vrijednosti)<br />

i s njim povezanih identikacijskih podataka (njihovih klju£eva).<br />

Na primjer, ve¢ina baza podataka koje sadrºe podatke o drºavljanima RH<br />

kao klju£ koriste JMBG jer je to kratak podatak jedinstven za svakog gražana.<br />

Klju£ je dakle ²to kra¢i podatak na osnovu kojega se moºe nedvosmisleno<br />

identicirati neki ve¢i slog podataka.<br />

Mapa je korisna struktura podataka za velik broj namjena, npr. kad<br />

god imamo zbirku objekata koje je potrebno dohva¢ati po imenu ili nekom<br />

drugom ID-ju. U Javi je za mape denirano su£elje Map. Ovaj primjer<br />

demonstrira njegovu uporabu:<br />

Ispis 5.7: Klasa Student koja demonstrira mapu<br />

public class Student<br />

{<br />

private String id;<br />

private String firstName;<br />

private String lastName;<br />

public Student( String id, String firstName, String lastName )<br />

{<br />

this.id = id;<br />

this.firstName = firstName;<br />

this.lastName = lastName;<br />

}<br />

... metode getId, getFirstName, getLastName ...<br />

public String toString()<br />

{<br />

return this.firstName + " " + this.lastName;<br />

}<br />

public static void main( String[] args )<br />

{<br />

Student stud1 = new Student( "id1", "Bill", "McGill" );<br />

Student stud2 = new Student( "id2", "John", "Pepper" );<br />

Map students = new HashMap();<br />

students.put( stud1.id, stud1 );


94 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />

students.put( stud2.id, stud2 );<br />

}<br />

}<br />

Student fetchedStudent = students.get( "id2" );<br />

System.out.println( "Dohva¢eni student: " + fetchedStudent );<br />

Denirana je mala klasa koja sadrºi podatke o studentu. Instance te<br />

klase spremaju se u mapu tako da se kao klju£ koristi atribut id. Op¢enito<br />

nije nuºno da klju£ bude dio objekta, ali je uobi£ajeno. I za klju£ i<br />

za vrijednost moºe se koristiti bilo koji objekt, bilo koje vrste. Vrsta Map je<br />

parametrizirana s dva parametra prvi je vrsta klju£a, a drugi vrsta vrijednosti.<br />

Isto se, naravno, odnosi i na HashMap. Ovo je, ina£e, jo² jedna klasa<br />

koja se oslanja na ispravno funkcioniranje metoda equals i hashCode u objektima<br />

koji se koriste kao klju£. Klasa String ima uredno implementirane ove<br />

metode pa ne¢e biti problema.<br />

Iz primjera mogu se i²£itati dvije najvaºnije metode su£elja Map: put(<br />

key, value ) i get( key ). Za ostale raspoloºive metode treba konzultirati<br />

dokumentaciju [1].<br />

5.6 ƒesto kori²teni algoritmi nad kolekcijama<br />

U paketu java.util postoji klasa Collections koja ne sluºi za instanciranje,<br />

ve¢ sadrºi pove¢i broj stati£kih metoda koje provode £esto kori²tene<br />

algoritme za obradu kolekcija. Postoje npr. shuffle, reverse, sort, min, max<br />

itd. Za ove algoritme kaºe se da su polimorfni jer, sluºe¢i se Javinim polimorzmom,<br />

mogu raditi s velikim brojem razli£itih vrsta kolekcija. Za<br />

mnoge algoritme (prije svega sort) potrebno je da elementi kolekcije budu<br />

usporedivi, ²to tehni£ki zna£i da moraju ispravno implementirati su£elje<br />

Comparable.<br />

5.6.1 Implementiranje su£elja java.lang.Comparable<br />

Su£elje Comparable je parametrizirano parametar je neka vrsta T. U<br />

na£elu se kao parametar navodi upravo klasa u kojoj implementiramo to<br />

su£elje. Time kaºemo kompilatoru da se objekte na²e klase moºe usporeživati<br />

samo s drugim objektima iste klase. Op¢enito, tu bi se mogla staviti<br />

bilo koja klasa, ali druge vrijednosti ne¢e imati smisla osim eventualno


POGLAVLJE 5. KOLEKCIJE OBJEKATA 95<br />

neke natklase trenutne klase, u slu£aju da imamo vi²e sli£nih klasa £ije<br />

instance se mogu mežusobno usporeživati.<br />

Ovo su£elje sadrºi samo jednu metodu: compareTo( T ). Povratna vrijednost<br />

je int koji mora biti nula za jednake objekte, negativan ako je<br />

trenutni objekt manji od prosliježenog i pozitivan ako je trenutni objekt<br />

ve¢i od prosliježenog. Zna£enje pojmova manji odnosno ve¢i ovisi o<br />

vrsti objekta. Poredak objekata u kojem se nijedan objekt ne pojavljuje<br />

prije nekog drugog koji je od njega manji naziva se prirodnim poretkom<br />

tih objekata. Metoda compareTo, dakle, odrežuje prirodni poredak instanci<br />

klase u kojoj je implementirana. Na primjer, u klasi Student iz ispisa 5.7<br />

mogli bismo tu metodu implementirati ovako:<br />

public class Student<br />

implements Comparable<br />

{<br />

... isti kod kao ranije ...<br />

}<br />

Ispis 5.8: Metoda compareTo<br />

public int compareTo( Student that )<br />

{<br />

return this.lastName.compareTo( that.lastName );<br />

}<br />

Klasa sad najavljuje da implementira su£elje Comparable. Metoda compareTo,<br />

oslanjaju¢i se na String-ovu implementaciju iste metode, usporežuje studente<br />

po prezimenu.<br />

5.6.2 Poredak razli£it od prirodnog su£elje<br />

java.util.Comparator<br />

Sve metode u klasi Collections koje se oslanjaju na prirodni poredak objekata<br />

imaju i verzije koje se oslanjaju na poseban objekt-komparator, koji<br />

moºe nametnuti proizvoljan poredak nad objektima. Takav objekt mora<br />

implementirati su£elje java.util.Comparator. Evo primjera komparatora<br />

za studente koji name¢e poredak po ID-ju:<br />

Ispis 5.9: Komparator za studente koji name¢e poredak po ID-ju<br />

public class StudentComparatorById<br />

implements java.util.Comparator<br />

{


96 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />

}<br />

public int compare( Student left, Student right )<br />

{<br />

return left.getId().compareTo( right.getId() );<br />

}<br />

5.6.3 Primjer kori²tenja metode Collections.sort<br />

public static void main( String[] args )<br />

{<br />

List students = Arrays.asList( new Student[]<br />

{<br />

new Student( "005", "Joshua", "Bloch" ),<br />

new Student( "003", "Paul", "Graham" ),<br />

new Student( "004", "Richard", "Dawkins" ),<br />

new Student( "002", "Rick", "Martinoff" ),<br />

}<br />

);<br />

}<br />

System.out.println( "Po£etni popis:" );<br />

for ( Student student : students )<br />

System.out.println( student );<br />

Collections.sort( students );<br />

System.out.println( "\nStudenti sortirani po prezimenu:" );<br />

for ( Student student : students )<br />

System.out.println( student );<br />

Collections.sort( students, new StudentComparatorById() );<br />

System.out.println( "\nStudenti sortirani po ID-ju:" );<br />

for ( Student student : students )<br />

System.out.println( student );<br />

O£ekivani ispis:<br />

Po£etni popis:<br />

005 Joshua Bloch<br />

003 Paul Graham<br />

004 Richard Dawkins<br />

002 Rick Martinoff<br />

Studenti sortirani po prezimenu:<br />

005 Joshua Bloch<br />

004 Richard Dawkins<br />

003 Paul Graham<br />

002 Rick Martinoff<br />

Studenti sortirani po ID-ju:<br />

002 Rick Martinoff<br />

003 Paul Graham<br />

004 Richard Dawkins


POGLAVLJE 5. KOLEKCIJE OBJEKATA 97<br />

005 Joshua Bloch


Poglavlje 6<br />

Polja<br />

Polje je blok u memoriji u koji je spremljen niz podataka iste vrste. U<br />

Javi polje je izvedeno kao objekt, ali posebnog karaktera. Polja su objekti<br />

posebne referentne vrste polja i njima se barata putem referenci, kao i<br />

ostalim objektima. Ovo treba uvijek imati na umu jer vrijedi sve re£eno za<br />

referentne varijable u odjeljku 2.1.2.<br />

Sva polja u osnovi su jednodimenzionalna. Dvodimenzionalno polje izvedeno<br />

je kao jednodimenzionalno polje referenci na druga jednodimenzionalna<br />

polja.<br />

6.1 Osnovni elementi sintakse<br />

Jednodimenzionalna polja:<br />

Ispis 6.1: Rad s jednodimenzionalim poljem<br />

int[] intArray; // deklaracija varijable polja<br />

intArray = new int[3]; // kreiranje praznog polja (popunjeno nulama)<br />

int i = intArray[0]; // pristup £lanu polja<br />

intArray = { 1, 2, 3 }; // kreiranje uz popunu<br />

// navedenim vrijednostima<br />

Uo£imo sintaksu u posljednjem retku: na desnoj strani ne treba koristiti<br />

operator new i to£nu vrstu podataka, dovoljan je samo doslovan popis £lanova.<br />

Java ¢e automatski stvoriti polje one vrste koja je deklarirana na<br />

lijevoj strani.<br />

ƒlanovi polja inicijaliziraju se po istim pravilima kao i atributi objekta<br />

brojevi na nulu, logi£ke vrijednosti na false, a reference na null.<br />

Dvodimenzionalna polja (analogno i za vi²edimenzionalna):<br />

99


100 POGLAVLJE 6. POLJA<br />

Ispis 6.2: Rad s dvodimenzionalnim poljem<br />

int[][] intArray;<br />

intArray = new int[2][3];<br />

int i = intArray[0][0];<br />

intArray = { {1, 2}, {3, 4}, {5, 6} };<br />

Petlja enhanced for za prolaz po svim £lanovima polja:<br />

Ispis 6.3: Prolaz po svim £lanovima polja petljom enhanced for<br />

public static void main( String[] args )<br />

{<br />

int[] intArray = { 1, 2, 3, 4, 5 };<br />

for ( int i : intArray )<br />

System.out.print( " " + i );<br />

}<br />

System.out.println();<br />

Dvodimenzionalno polje ne mora nuºno biti pravokutno, ²to slijedi iz<br />

re£enog da je to ustvari polje polja. Sljede¢i primjer demonstrira ovo:<br />

Ispis 6.4: Trokutasto dvodimenzionalno polje<br />

public static void main( String[] args )<br />

{<br />

int[][] int2Array = { {11}, {21, 22}, {31, 32, 33} };<br />

for ( int[] intArray : int2Array )<br />

{<br />

for ( int i : intArray )<br />

System.out.print( " " + i );<br />

}<br />

}<br />

System.out.println();<br />

U prvom retku koda kreira se polje polja tako da je njegov prvi £lan polje<br />

duljine jedan, drugi £lan polje duljine dva i tre¢i £lan polje duljine tri.<br />

Zatim slijedi dvostruka petlja u kojoj se ispisuje cjelokupan sadrºaj stvorene<br />

strukture. Time se vizualizira trokutasti izgled strukture. Ispis je ovakav:<br />

11<br />

21 22<br />

31 32 33<br />

Polja mogu sadrºavati i reference na objekte. U deklaraciji se navodi<br />

referentna vrsta, npr. String[] stringArray.


POGLAVLJE 6. POLJA 101<br />

6.2 Polje kao objekt<br />

Polje, iako nije u pravom smislu instanca neke klase, svejedno je objekt<br />

koji je instanceof java.lang.Object, pa raspolaºe i svim odgovaraju¢im metodama.<br />

Svako polje ima i cjelobrojni atribut length, jednak njegovom kapacitetu.<br />

Atribut je final jer se kapacitet polja ne moºe naknadno mijenjati.<br />

Koristi ga se npr. za sloºenije primjere prolaska po svim £lanovima polja:<br />

Ispis 6.5: Prolaz po polju kori²tenjem eksplicitnog indeksa<br />

int[] intArray = new int[3];<br />

for ( int i = 0; i < intArray.length; i++ )<br />

{<br />

int j = intArray[i];<br />

...<br />

}<br />

6.3 Interakcija s kolekcijama<br />

Kolekcije su eksibilnije i op¢enito korisnije strukture od polja. Mežutim,<br />

polja su siroviji oblik strukture i stoga ra£unski i memorijski ekasnija.<br />

Prednost je i ²to se za rad s poljima moºe koristiti elegantnija sintaksa.<br />

Javina biblioteka podrºava laku transformaciju izmežu kolekcija i polja:<br />

Ispis 6.6: Pretvorbe izmežu polja i liste<br />

String[] stringArray = { "Harry", "Moe", "Joe" };<br />

// konverzija polja u listu:<br />

List stringList = Arrays.asList( stringArray );<br />

// konverzija liste u polje:<br />

String[] stringArray2 = stringList.toArray( new String[0] );<br />

Metodi toArray mora se proslijediti makar prazno polje jer je potrebna informacija<br />

o tome koju vrstu polja treba stvoriti. Ovdje, naºalost ne pomaºu<br />

generics i to se ne dogaža automatski. Ako je prosliježeno polje dovoljno<br />

veliko, iskoristit ¢e se; u protivnom se stvara novo.<br />

Metoda asList moºe se iskoristiti i za jednostavno stvaranje liste popunjene<br />

doslovno navedenim vrijednostima:<br />

List stringList = Arrays.asList( "Harry", "Moe", "Joe" );<br />

Dobivena lista ima ksiran sadrºaj zabranjeno je pozivati metode add,<br />

remove i ostale koje bi ga mijenjale.


Poglavlje 7<br />

Komunikacija s okolinom<br />

Svaka aplikacija treba komunicirati s okolinom. To se prije svega odnosi na<br />

korisni£ko su£elje (tipkovnica i zaslon), sustav datoteka i ra£unalsku mreºu.<br />

Ovdje se prikazuje na koji na£in se u jeziku Java komunicira s okolinom.<br />

7.1 Tokovi podataka<br />

Osnovna paradigma koja se u Javi koristi za razmjenu podataka s okolinom<br />

je tok podataka (eng. data stream). Postoje ulazni i izlazni tokovi i predstavljeni<br />

su apstraktnim klasama InputStream odnosno OutputStream. Nalaze<br />

se u paketu posve¢enom komunikaciji, java.io. Za svaku vrstu resursa s kojim<br />

se moºe komunicirati (zaslon, datoteka, udaljeno ra£unalo itd.) postoje<br />

konkretne klase izvedene iz njih. Apstraktne klase deklariraju osnovne metode<br />

za rad s tokom available, read, reset i close za InputStream odnosno<br />

write, flush i close za OutputStream. Sljede¢i primjer demonstrira kori²tenje<br />

izlaznog toka:<br />

Ispis 7.1: Rad s izlaznim tokom<br />

import java.io.OutputStream;<br />

import java.io.IOException;<br />

...<br />

static void sendToOutput( OutputStream os )<br />

throws IOException<br />

{<br />

for ( int i = 0; i < 256; i++ )<br />

os.write( i );<br />

os.flush();<br />

103


104 POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM<br />

... daljnje operacije nad tokom ...<br />

}<br />

os.close();<br />

Ova metoda moºe raditi s bilo kakvim izlaznim tokom. U nju ¢e zapisati<br />

niz okteta od nule do 255 i nakon toga ju zatvoriti. Metoda flush poziva se<br />

kad se ºeli osigurati da podaci budu odmah isporu£eni na odredi²te. U protivnom<br />

¢e se najvjerojatnije gomilati u mežuspremniku u radnoj memoriji<br />

dok ih se ne skupi dovoljno za masovno slanje. Kori²tenje mežuspremnika<br />

op¢enito zna£ajno podiºe ekasnost komunikacije jer se ²alju ve¢i blokovi<br />

podataka, tako da flush ne treba koristiti ako to nije zaista potrebno.<br />

Ovaj primjer demonstrira kori²tenje ulaznog toka:<br />

Ispis 7.2: Rad s ulaznim tokom<br />

import java.io.InputStream;<br />

import java.io.IOException;<br />

...<br />

static void receiveInput( InputStream is )<br />

throws IOException<br />

{<br />

while ( true )<br />

{<br />

int i = is.read();<br />

if ( i == -1 )<br />

break;<br />

System.out.println( i );<br />

}<br />

is.close();<br />

}<br />

Metoda read vra¢a pro£itani oktet. Ako vrati vrijednost -1, to zna£i da je<br />

tok do²ao do kraja. Op¢enito se ne moºe samo ispitati je li tok do²ao do<br />

kraja bez poku²aja £itanja pa ne postoji posebna metoda za to koju bi se<br />

moglo koristiti u uvjetu petlje while. Metoda available vra¢a broj okteta<br />

raspoloºivih odmah, bez £ekanja. Ako vrati nulu, to ne mora zna£iti da je<br />

tok do²ao do kraja, ve¢ samo da trenutno nema novih podataka.<br />

7.2 Pisa£i i £ita£i<br />

Za komunikaciju tekstualnim podacima postoje posebni omota£i sirovih<br />

tokova okteta koji ih pretvaraju u tokove znakova. U Javinoj terminologiji


POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM 105<br />

to su pisa£i (osnovna klasa Writer) i £ita£i (osnovna klasa Reader). Koriste<br />

se sli£no kao sirove tokove, ali barataju podacima vrste char. Jo² je<br />

prakti£nije koristiti klase s ugraženim mežuspremnikom (BufferedReader i<br />

PrintWriter) koje omogu¢uju komunikaciju redak po redak umjesto znak<br />

po znak. Ovo je prikazano u primjerima:<br />

Ispis 7.3: Pisa£ i £ita£<br />

import java.io.Writer;<br />

import java.io.PrintWriter;<br />

import java.io.Reader;<br />

import java.io.BufferedReader;<br />

import java.io.IOException;<br />

...<br />

static void writeOutput( Writer writer )<br />

throws IOException<br />

{<br />

PrintWriter pw = new PrintWriter( writer );<br />

pw.println( "Prvi redak" );<br />

pw.println( "Drugi redak" );<br />

pw.flush();<br />

pw.close();<br />

}<br />

static void readInput( Reader reader )<br />

throws IOException<br />

{<br />

BufferedReader br = new BufferedReader( reader );<br />

while ( true )<br />

{<br />

String line = br.readLine();<br />

if ( line == null || line.length() == 0 )<br />

break;<br />

System.out.println( line );<br />

}<br />

br.close();<br />

}<br />

Metoda readInput zavr²it ¢e ako naiže na kraj ulaznog toka (line == null)<br />

ili ako pro£ita prazan redak (line.length() == 0).<br />

Pisa£ i £ita£ rade tako da se povezuju sa sirovim tokom okteta i obavljaju<br />

potrebne konverzije. Povezuje ih se pri instanciranju proslježivanjem<br />

objekta toka konstruktoru. Sljede¢i primjer prikazuje metode koje pozivaju<br />

metode iz ispisa 7.3 s odgovaraju¢e omotanim sirovim tokovima:<br />

import java.io.OutputStream;<br />

Ispis 7.4: Omota£i podatkovnih tokova


106 POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM<br />

import java.io.OutputStreamWriter;<br />

import java.io.InputStream;<br />

import java.io.InputStreamReader;<br />

import java.io.IOException;<br />

...<br />

static void writeOutput( OutputStream os )<br />

throws IOException<br />

{<br />

writeOutput( new OutputStreamWriter( os ) );<br />

}<br />

static void readInput( InputStream is )<br />

throws IOException<br />

{<br />

readInput( new InputStreamReader( is ) );<br />

}<br />

7.3 Komunikacija s korisnikom<br />

Osnovna tekstualna komunikacija s korisnikom ostvaruje se putem standardnog<br />

ulaz/izlaza koji pruºa svaka ra£unalska platforma. U Javi se s<br />

njima povezuje putem stati£kih atributa u klasi System. Ispis na standardni<br />

izlaz sreli smo ve¢ mnogo puta u kodu, svaki put kad se koristio izraz<br />

System.out.println( ... ). Stati£ki atribut out sadrºi referencu na objekt<br />

vrste PrintStream, koja je vrlo srodna prikazanoj vrsti PrintWriter i denira<br />

mnoge identi£ne metode. Za prakti£no kori²tenje standardnog ulaza<br />

potrebno je eksplicitno dodati omota£e, kao u ispisu 7.4. Kod iz ispisa 7.3<br />

i 7.4 moºemo isprobati dodavanjem sljede¢e metode main:<br />

import java.io.IOException;<br />

...<br />

public static void main( String[] args )<br />

throws IOException<br />

{<br />

System.out.println( "Utipkaj ne²to!" );<br />

readInput( System.in );<br />

}<br />

U konzolu treba utipkati tekst i pritisnuti Enter. Program ¢e ponoviti<br />

utipkano. Program dovr²avamo utipkavanjem praznog retka. Atribut<br />

System.in je vrste InputStream pa ¢e se pozvati metoda readInput( InputStream<br />

) iz ispisa 7.4, koja ¢e onda proslijediti poziv metodi readInput( BufferedReader<br />

) iz ispisa 7.3.


POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM 107<br />

7.4 Rad s datotekama<br />

Datotekama se u Javi barata putem objekata vrste File. To su tzv. handleobjekti<br />

jer postojanje objekta ne zna£i da postoji i datoteka koju on predstavlja.<br />

Objektu se zada putanja do datoteke i zatim ga se moºe pitati postoji<br />

li datoteka, je li u pitanju prava datoteka ili kazalo itd. Ako datoteka<br />

ne postoji, moºe ju se stvoriti. Sljede¢i kod demonstrira rad s datotekama.<br />

I on se oslanja na metode iz ispisa 7.3 i 7.4.<br />

import java.io.IOException;<br />

import java.io.FileOutputStream;<br />

import java.io.FileInputStream;<br />

import java.io.File;<br />

...<br />

public static void main( String[] args )<br />

throws IOException<br />

{<br />

File file = new File( "Text.txt" );<br />

file.createNewFile();<br />

System.out.println( "Zapisujem u datoteku..." );<br />

writeOutput( new FileOutputStream( file ) );<br />

System.out.println( "ƒitam iz datoteke..." );<br />

readInput( new FileInputStream( file ) );<br />

}<br />

Moºemo vidjeti da se datoteku otvara za pisanje instanciranjem klase<br />

FileOutputStream kojoj se proslježuje instanca klase File. ƒim se taj objekt<br />

kreira, datoteka je ve¢ otvorena i spremna za zapisivanje. Analogno se i<br />

£ita iz datoteke.<br />

7.5 Rad s internetskim konekcijama<br />

Za dohvat sadrºaja s nekog URL-a dovoljna su samo dva retka koda (uz<br />

ve¢ postoje¢i kod iz ispisa (7.3 i 7.4):<br />

java.net.URL url = new java.net.URL( "http://www.google.com" );<br />

readInput( url.openStream() );<br />

Klasa URL predstavlja lokaciju na Internetu identiciranu URL-om (uniform<br />

resource locator). Pozivom metode openStream automatski se obavljaju<br />

sve radnje potrebne za pristup resursu pod tim URL-om kontaktira se<br />

DNS, otvara mreºna konekcija na potrebnom portu, ²alje HTTP-ov zahtjev<br />

za dostavom resursa i kreira objekt toka podataka koji ¢e isporu£ivati<br />

podatke koje posluºitelj ²alje kao odgovor.


Poglavlje 8<br />

Uzorci u dizajnu koda<br />

U praksi, pri izradi programskih rje²enja mnoge vrste problema koje treba<br />

rje²avati opetovano se pojavljuju bez obzira na konkretni zadatak. S vremenom<br />

su sazrijela za svaki takav tipi£an problem najbolja rje²enja, koja<br />

pokrivaju sve suptilnije nijanse problema 1 . Razvijatelj si moºe drasti£no<br />

olak²ati ºivot ako prou£i ta rje²enja tako da ih moºe primijeniti kad zatreba.<br />

Takožer, baratanje vokabularom vezanim uz uzorke (prije svega prepoznavanje<br />

uzoraka po imenu) bitno olak²ava komunikaciju izmežu razvijatelja,<br />

koji tako mogu puno ekasnije raspravljati o svojim programskim rje²enjima.<br />

Ideja uzoraka u dizajnu koda doºivjela je najve¢i proboj 1995. godine,<br />

kad je objavljena kultna knjiga Design Patterns [3] £etvorice autora popularno<br />

zvanih The Gang of Four ili GoF, kako se naj£e²¢e oslovljava i<br />

samu knjigu. U ovom poglavlju bit ¢e kao primjer ove koncepcije prikazano<br />

nekoliko najjednostavnijih uzoraka: Template method, Observer, Adapter i<br />

Decorator.<br />

8.1 Template method<br />

Ovaj uzorak jednostavan je za razumjeti i ima vrlo ²iroku primjenu. Ustvari,<br />

primijenili smo ga jo² u prvom poglavlju, prilikom refaktorizacije u<br />

odjeljku 1.11. Drugo ime za Template method je polimorfna metoda. Sli£an<br />

izraz, polimorfni algoritam, spominjao se ve¢ u poglavlju o kolekcijama, u<br />

odjeljku 5.6. Dakle, radi se naprosto o metodi koja u svojem kodu sadrºi<br />

1 U ovoj skripti se, mežutim, ne¢e ulaziti u te nijanse. Ovdje se daje samo najosnovniji<br />

uvod u koncepciju uzoraka.<br />

109


110 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />

pozive metoda koje podlijeºu polimorzmu odnosno dinami£kom povezivanju.<br />

Metoda na apstraktnoj razini implementira neki algoritam, gdje se<br />

pojedini koraci deniraju samo na razini ²to a ne i kako. U prvom poglavlju<br />

imali smo sljede¢i trivijalan primjer polimorfne metode:<br />

abstract class AbstractCounter<br />

implements Counter<br />

{<br />

...<br />

public final void increment()<br />

{<br />

setState( getState() + 1 );<br />

}<br />

}<br />

abstract void setState( int newState );<br />

Metoda increment je polimorfna jer se oslanja na apstraktnu metodu<br />

setState. Dakle, odrežuje da se mora postaviti novo stanje, ali ne propisuje<br />

to£no kako se postavlja novo stanje. To ¢e ovisiti o konkretnoj implementaciji<br />

metode setState. U na²em primjeru imali smo dvije varijante<br />

te metode: u klasi SimpleCounter jednostavno se prosliježeni int zapisao u<br />

atribut, dok se u klasi ModuloCounter zapisao ostatak dijeljenja s modulom.<br />

8.2 Observer<br />

Recimo da razvijamo softver za simulaciju rada mreºe TCP/IP. Svaki komunikacijski<br />

£vor moºe primiti neki paket namijenjen bilo njemu, bilo nekom<br />

daljnjem £voru i treba ga u skladu s time proslijediti dalje ili konzumirati.<br />

šelimo da na²em objektu koji predstavlja komunikacijski £vor<br />

moºemo dodavati procedure za obradu dogažaja paket stigao u £vor X.<br />

Na primjer, u najjednostavnijem slu£aju trebat ¢e ispisati tekstualnu poruku<br />

na standardni izlaz. U malo naprednijem slu£aju trebat ¢e prikazati<br />

poruku u prozoru gra£kog su£elja. Za o£ekivati je i da ¢emo imati gra£ki<br />

prikaz mreºe na kojem treba vizualizirati putovanje paketa od £vora do<br />

£vora. U tom slu£aju bit ¢e potrebno kad paket stigne u £vor promijeniti<br />

neki detalj u prikazu, npr. boju £vora. Sve te mogu¢nosti ºelimo imati na<br />

raspolaganju kao korisnik klase koja predstavlja mreºu TCP/IP, ²to zna£i<br />

bez ikakvih intervencija u izvorni kod te klase dapa£e, bez posjedovanja


POGLAVLJE 8. UZORCI U DIZAJNU KODA 111<br />

njenog izvornog koda. O£ito je da ¢e klasa £vora morati imati ugraženu<br />

podr²ku za to. Ta podr²ka ugražuje se kori²tenjem uzorka Observer.<br />

Prvo ²to treba uvesti je objekt-oslu²kiva£ (to je observer, ali u kontekstu<br />

Jave uobi£ajen je izraz listener). On ima metodu koju ¢e objekt £vora<br />

pozvati i time signalizirati stigao je paket. Oslu²kiva£a kreiramo i prijavimo<br />

£voru pozivanjem £vorove metode npr. addReceiveListener. Svaki<br />

put kad primi paket, £vor mora pro¢i po listi svih prijavljenih oslu²kiva£a<br />

i pozvati njihovu metodu npr. packetReceived. U toj metodi nalazi se kod<br />

koji obražuje dogažaj primitka paketa.<br />

Za oslu²kiva£a prvo treba denirati su£elje, ReceiveListener. Njega onda<br />

mogu implementirati razli£ite verzije oslu²kiva£a.<br />

Ispis 8.5: Su£elje oslu²kiva£a dolaznih paketa<br />

package hr.fer.tel.jsem.observer;<br />

public interface ReceiveListener<br />

{<br />

void packetReceived( Packet packet, Node receivingNode );<br />

}<br />

Pokaºimo odmah i jednu jednostavnu implementaciju oslu²kiva£a:<br />

Ispis 8.6: Jednostavna implementacija oslu²kiva£a dolaznih paketa<br />

package hr.fer.tel.jsem.observer;<br />

public class SysoutReceiveListenerHr<br />

implements ReceiveListener<br />

{<br />

public void packetReceived( Packet packet, Node receivingNode )<br />

{<br />

System.out.println(<br />

"ƒvor " + receivingNode.getName() + " je primio paket: "<br />

+ packet.getData() );<br />

}<br />

}<br />

Klasa komunikacijskog £vora s podr²kom za oslu²kiva£e:<br />

Ispis 8.7: Komunikacijski £vor s podr²kom za oslu²kiva£e<br />

package hr.fer.tel.jsem.observer;<br />

import java.util.HashSet;<br />

import java.util.Set;


112 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />

public class Node<br />

{<br />

private final Set recListeners =<br />

new HashSet();<br />

}<br />

private final String name;<br />

public Node( String name )<br />

{<br />

this.name = name;<br />

}<br />

public void addReceiveListener( ReceiveListener listener )<br />

{<br />

this.recListeners.add( listener );<br />

}<br />

public void receivePacket( Packet packet )<br />

{<br />

for ( ReceiveListener listener : this.recListeners )<br />

listener.packetReceived( packet, this );<br />

}<br />

public String getName()<br />

{<br />

return this.name;<br />

}<br />

Napokon, pogledajmo primjer kako se sve ovo koristi. Gore je prikazana<br />

klasa oslu²kiva£a koja ispisuje poruku na hrvatskom; u primjeru koji slijedi<br />

pojavit ¢e se i klasa koja ispisuje poruku na engleskom. Cilj je prikazati<br />

situaciju s vi²e od jednog oslu²kiva£a.<br />

Ispis 8.8: Kori²tenje sustava<br />

public static void main( String[] args )<br />

{<br />

Node node1 = new Node( "Node1" );<br />

node1.addReceiveListener( new SysoutReceiveListenerEn() );<br />

node1.addReceiveListener( new SysoutReceiveListenerHr() );<br />

}<br />

Ispis:<br />

System.out.println( "’aljemo paket £voru..." );<br />

node1.receivePacket( new Packet( "Testing Testing 1-2-3" ) );<br />

’aljemo paket £voru...<br />

Node Node1 received packet: Testing Testing 1-2-3<br />

ƒvor Node1 je primio paket: Testing Testing 1-2-3


POGLAVLJE 8. UZORCI U DIZAJNU KODA 113<br />

8.3 Adapter<br />

S primjerima uzoraka Adapter i Decorator susreli smo se u poglavlju o<br />

komunikaciji s okolinom, to£nije u odjeljku 7.2 o pisa£ima i £ita£ima. Podsjetimo<br />

se situacije koju smo imali. U ispisu 7.4 imamo metodu writeOutput(<br />

OutputStream ). Njen parametar, objekt vrste OutputStream, predstavlja odlazni<br />

tok podataka. To je je tzv. sirovi tok podataka jer se preko njega<br />

²alju £isti okteti, a ne npr. neki apstraktniji podaci kao ²to su znakovi.<br />

Mežutim, nama u implementaciji te metode upravo treba tok podataka u<br />

koji se mogu slati znakovi. To je zato ²to moramo pozvati metodu iz ispisa<br />

7.3, writeOutput( Writer ), koja o£ekuje da ju se pozove s pisa£em, ²to je<br />

Javin izraz za odlazni znakovni tok podataka.<br />

U praksi ¢emo tipi£no imati ve¢ gotove komponente koje rade sa znakovnim<br />

tokovima i ne znaju raditi druga£ije. Dakle, zamislimo da je metoda<br />

writeOutput( Writer ) ugražena u neku klasu za koju nemamo izvorni<br />

kod jer je ona dio biblioteke klasa koju samo koristimo, a ne razvijamo.<br />

Dovedeni smo u situaciju koja se £esto u praksi, u analogiji s elektrotehnikom,<br />

naziva impedance mismatch (neprilagoženost impedancija). S jedne<br />

strane imamo metodu koja o£ekuje znakovni tok, a s druge strane imamo<br />

sirovi tok u koji ta metoda treba slati podatke. Problem ¢emo rije²iti<br />

primjenom uzorka Adapter on ¢e nam adaptirati, tj. prilagoditi su£elje<br />

objekta koji imamo (sirovi tok) na su£elje objekta koji o£ekuje metoda<br />

iz biblioteke (znakovni tok). Moramo napraviti klasu adaptera sa sirovog<br />

na znakovni tok. U primjeru s ispisa 7.4 koristi se Javin adapter, klasa<br />

OutputStreamWriter. Pogledajmo kako bi ona mogla biti realizirana:<br />

Ispis 8.9: Pokazna izvedba adaptera na znakovni tok<br />

package hr.fer.tel.jsem;<br />

import java.io.IOException;<br />

import java.io.OutputStream;<br />

import java.io.Writer;<br />

public class OutputStreamWriter<br />

extends Writer<br />

{<br />

private OutputStream os;<br />

public OutputStreamWriter( OutputStream os )<br />

{<br />

this.os = os;


114 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />

}<br />

}<br />

@Override<br />

public void write( char[] cbuf, int off, int len )<br />

throws IOException<br />

{<br />

for ( int i = off; i < off+len; i++ )<br />

{<br />

char c = cbuf[i];<br />

if ( c < 0x80 )<br />

os.write( c );<br />

else<br />

os.write( '#' );<br />

}<br />

}<br />

@Override<br />

public void flush()<br />

throws IOException<br />

{<br />

os.flush();<br />

}<br />

@Override<br />

public void close()<br />

throws IOException<br />

{<br />

os.close();<br />

}<br />

Ova jednostavna izvedba u sirovi tok propu²ta samo znakove iz podskupa<br />

Unicode-a zvanog Basic Latin, koji odgovara ASCII-ju, a umjesto<br />

svih ostalih znakova ²alje znak #. Primijetimo da su nadja£ane samo tri<br />

metode, dok klasa Writer posjeduje znatno ve¢i broj njih. To je zahvaljuju¢i<br />

tome ²to su sve ostale metode u toj klasi paºljivo izvedene tako da se<br />

oslanjaju na osnovnu funkcionalnost koju pruºaju samo ove tri. Na² adapter<br />

moºemo isprobati na primjerima iz 7. poglavlja tako da u na² paket dodamo<br />

klasu adaptera, a izbacimo izjavu import java.io.OutputStreamWriter.<br />

Alternativno, moºemo koristiti ovakvu metodu main, koju moºemo ubaciti<br />

u klasu OutputStreamWriter:<br />

public static void main( String[] args )<br />

throws IOException<br />

{<br />

Writer osw = new OutputStreamWriter( System.out );<br />

osw.write( "Probni ispis\n" );<br />

osw.close();<br />

}


POGLAVLJE 8. UZORCI U DIZAJNU KODA 115<br />

U drugom retku poziva se metoda write( String ), koju nismo eksplicitno<br />

nadja£ali, ali koja se oslanja na na²u metodu write( char[], int, int<br />

) 2 .<br />

Uzorak Adapter moºemo dosta pregledno vizualizirati UML-om. Na²a<br />

situacija prikazana je na slici 8.1.<br />

Client<br />

+writeOutput()<br />

Writer<br />

+write()<br />

+flush()<br />

+close()<br />

Client očekuje ovakav objekt<br />

Adapter<br />

Ovakav objekt<br />

imamo<br />

OutputStreamWriter<br />

OutputStream<br />

+write()<br />

+flush()<br />

+close()<br />

+write()<br />

+flush()<br />

+close()<br />

Slika 8.1: UML-ov dijagram uzorka Adapter<br />

8.4 Decorator<br />

Osim ²to £esto imamo situaciju koju rije²avamo Adapter-om, dakle nesuglasje<br />

izmežu su£elja koje nam treba i su£elja koje imamo na raspolaganju,<br />

£esto se pojavljuje i sli£na situacija u kojoj imamo objekt £ije su£elje odgovara,<br />

ali mu nedostaje jo² neka dodatna funkcionalnost. Dakle, nisu nam<br />

potrebne druga£ije metode (kao u slu£aju uzorka Adapter), ve¢ samo izmijenjeno<br />

pona²anje postoje¢ih metoda. Za takve situacije primjenjujemo<br />

uzorak Decorator. Iz re£enog ve¢ moºemo naslutiti da je ovaj uzorak dosta<br />

blizak uzorku Adapter dapa£e, njegova shema prikazana UML-om je identi£na.<br />

U poglavlju o komunikaciji imamo situaciju na kojoj moºemo prikazati<br />

ovaj uzorak. Tamo se pojavljuje klasa Reader koja predstavlja neki dolazni<br />

znakovni tok. Problem koji sad ºelimo rije²iti je sljede¢i: ve¢ raspolaºemo<br />

2 Metoda write( String ) je stoga jo² jedan primjer primjene uzorka Template method.


116 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />

nekim objektom £ita£a, a ºelimo mu dodati funkciju korisnu prilikom tra-<br />

ºenja pogre²aka u programu da sve ²to pro£ita iz dolazne struje ispi²e i na<br />

zaslon radi kontrole. Naglasimo: ne kaºe se da raspolaºemo klasom £ita£a,<br />

£iji kod bismo mogli izmijeniti izravno, nego njenom instancom, dok samim<br />

izvornim kodom klase ne rapolaºemo.<br />

Kako ¢emo rije²iti ovaj problem? Denirat ¢emo novu klasu izvedenu<br />

iz klase Reader. Klasa, kao i kod Adapter-a, ima karakter omota£a. U ovom<br />

konkretnom slu£aju instanca na²e nove klase omota£ je za neki objekt vrste<br />

Reader. Dakle, u uzorku Decorator (hrv. ukra²iva£) klasa koju ukra²avamo<br />

pojavljuje se u dva konteksta odjednom: i kao vrsta ugraženog objekta<br />

(koji biva ukra²en), i kao natklasa ukra²iva£a. Najbolje je da prou£imo<br />

implementaciju na²eg ukra²iva£a:<br />

package hr.fer.tel.jsem;<br />

import java.io.IOException;<br />

import java.io.Reader;<br />

public class DebugReader<br />

extends Reader<br />

{<br />

private Reader rdr;<br />

Ispis 8.10: Primjer primjene uzorka Decorator<br />

public DebugReader( Reader rdr )<br />

{<br />

this.rdr = rdr;<br />

}<br />

@Override<br />

public int read( char[] cbuf, int off, int len )<br />

throws IOException<br />

{<br />

// prvo prosliježujemo poziv na omotani objekt:<br />

int charsRead = this.rdr.read( cbuf, off, len );<br />

// zatim obavljamo dodatne radnje:<br />

if ( charsRead != -1 )<br />

System.out.println( "Reading " +<br />

String.valueOf( cbuf, off, charsRead ) );<br />

return charsRead;<br />

}<br />

@Override<br />

public void close()<br />

throws IOException<br />

{<br />

// samo delegiramo na omotani objekt:


POGLAVLJE 8. UZORCI U DIZAJNU KODA 117<br />

}<br />

}<br />

this.rdr.close();<br />

Odmah moºemo uo£iti veliku sli£nost s primjerom za uzorak Adapter.<br />

Kod Adapter-a u igri su dvije razli£ite klase jer jednu prilagožavamo drugoj,<br />

a ovdje samo dodajemo funkcionalnost jednoj klasi, koja se stoga pojavljuje<br />

u dvostrukoj ulozi.<br />

Na²u klasu DebugReader moºemo isprobati na primjerima iz 5. poglavlja,<br />

tako da prepravimo ovu metodu iz ispisa 7.4:<br />

static void readInput( InputStream is )<br />

throws IOException<br />

{<br />

readInput( new InputStreamReader( is ) );<br />

}<br />

Treba samo ubaciti jo² jedan omota£, na² ukra²iva£:<br />

static void readInput( InputStream is )<br />

throws IOException<br />

{<br />

readInput( new DebugReader( new InputStreamReader( is ) ) );<br />

}<br />

Nakon toga moºemo isprobati primjere iz odjeljaka 7.37.5.<br />

Uzorak Decorator, kao ²to je najavljeno, u UML-ovom dijagramu izgleda<br />

vrlo sli£no kao Adapter. To se o£ituje na slici 8.2, na kojoj je jasno istaknuta<br />

dvojaka uloga ukra²avane klase 3 .<br />

Kao primjer kori²tenja uzorka Decorator u Javinoj biblioteci moºemo<br />

izdvojiti klase koje se pojavljuju u poglavlju o komunikaciji s okolinom:<br />

PrintWriter i BufferedReader. Te klase su i posebno zanimljive jer one istovremeno<br />

implementiraju i Adapter i Decorator: npr. klasa BufferedReader,<br />

osim ²to dodaje funkcionalnost mežuspremnika svim postoje¢im metodama<br />

(ukra²avanje), ima i dodatnu, vrlo zna£ajnu metodu readLine, koje nema<br />

u obi£nom Reader-u. Iz perspektive nekog koda koji o£ekuje da ¢e £ita£i<br />

imati upravo tu metodu, ona obavlja prilagodbu s obi£nog £ita£a na £ita£<br />

s metodom readLine.<br />

3 Dijagram sa slike 8.2 nije sasvim korektan jer se ista klasa ne bi smjela crtati dvaput;<br />

mežutim, ispravan crteº ne bi bio tako jasan.


118 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />

Client očekuje ovakav objekt<br />

Client<br />

+readInput()<br />

Reader<br />

+read()<br />

+close()<br />

Decorator<br />

Ovakav objekt<br />

imamo<br />

DebugReader<br />

Reader<br />

+read()<br />

+close()<br />

+read()<br />

+close()<br />

Slika 8.2: UML-ov dijagram uzorka Decorator


Poglavlje 9<br />

Savjeti za rad u Eclipse-u<br />

9.1 Automatizacija rada na kodu<br />

Razvojno okruºje Eclipse ima dosta bogatu zbirku alata koji automatiziraju<br />

mnoge aktivnosti na razvoju koda. Njihovim kori²tenjem kod se kreira<br />

brºe (puno manje tipkanja) i pouzdanije (Eclipse ne radi tipfelere). Preporu£a<br />

se detaljno prou£avanje menija Edit, Source i Refactor. Ovdje ¢e se<br />

spomenuti nekoliko najvaºnijih alata.<br />

Quick x aktivira se kombinacijom tipaka Ctrl-1 ili iz menija Edit. Pojavit<br />

¢e se mali prozor£i¢ u blizini kursora i ponudit ¢e razli£ite mogu¢nosti<br />

prepravljanja koda, i dodatno, ako se kursor nalazi na mjestu gdje je otkrivena<br />

pogre²ka, standardne na£ine njenog otklanjanja. Treba imati na umu<br />

ovo: Quick x ne zna bolje od vas! Student mora ve¢ znati kako treba<br />

ukloniti pogre²ku, Quick x je tu samo da taj postupak automatizira. Ako<br />

nasumice slu²ate savjete Quick x-a, najvjerojatnije je da ¢ete napraviti<br />

kaos u svom kodu.<br />

Rename in le je jedna od opcija koje nudi Quick x kad smo s kursorom<br />

unutar nekog identikatora. Opcija je dostupna i izravno pomo¢u Ctrl-<br />

2, R. Opciju koristimo uvijek kad ºelimo promijeniti ime nekoj lokalnoj<br />

varijabli, privatnom atributu/metodi i op¢enito bilo kojem identikatoru<br />

koji se koristi samo u trenutnoj datoteci.<br />

Content assist aktivira se pomo¢u Ctrl-Space ili iz menija Edit. Koristi<br />

se npr. na mjestu gdje ºelimo otipkati ime metode koju pozivamo. Ponudit<br />

¢e se sve metode denirane za objekt lijevo od to£ke. Npr. imamo varijablu<br />

String s i zapo£injemo novi redak koda tipkaju¢i<br />

s.<br />

119


120 POGLAVLJE 9. SAVJETI ZA RAD U ECLIPSE-U<br />

U ovom trenutku koristimo Ctrl-Space i uz kursor pojavljuje se popis svih<br />

metoda deniranih za String. Odaberemo pravu i njeno ime se ubacuje<br />

u kod. Ako je Eclipse odgovaraju¢e pode²en, izbornik Content Assist-a<br />

pojavit ¢e se i sam od sebe ako otipkamo to£ku i pri£ekomo koju sekundu<br />

(ovisno o brzini ra£unala).<br />

Content assist koristi se i za ekspanziju kodnih predloºaka: npr. mo-<br />

ºemo natipkati eq, pritisnuti Ctrl-space i odabrati equals. Time se ubacuje<br />

deklaracija metode equals. Isto radi i za toString, hashCode, main. Posebno<br />

koristan je predloºak sysout koji ubacuje £esto kori²tenu a duga£ku<br />

konstrukciju System.out.println(); Takožer su vrlo korisni predlo²ci for <br />

standardne petlje za prolazak po elementima polja ili kolekcije. Popis svih<br />

predloºaka moºe se pregledati i editirati pod Window, Preferences, Java,<br />

Editor, Templates.<br />

Organize imports aktivira se pomo¢u Ctrl-Shift-O ili iz menija Source.<br />

Kad god se pojavi pogre²ka da je neka vrsta podatka nepoznata, to je najvjerojatnije<br />

stoga ²to doti£na vrsta nije uvezena. Ovom naredbom automatski<br />

se pronalaze i uvoze sve potrebne vrste. Ako postoji vi²e vrsta istog<br />

imena, pojavit ¢e se prozor u kojemu treba odabrati pravu. Ova komanda<br />

takožer i uklanja nepotrebne uvoze, koji se trenutno ne koriste u kodu.<br />

Pomo¢u Rename moºemo promijeniti ime klasi, paketu, javnoj metodi i<br />

op¢enito bilo kojem identikatoru koji se koristi u vi²e datoteka. Dostupno<br />

iz menija Refactor ili pomo¢u Alt-Shift-R.<br />

Pomo¢u Move moºemo klasu preseliti u drugi paket i metodu/atribut u<br />

drugu klasu. Eclipse ¢e se pobrinuti da sva pozivanja na ono ²to se seli budu<br />

izmijenjena na odgovaraju¢i na£in, odnosno upozorit ¢e nas ako postoji<br />

prepreka preseljenju. Dostupno iz menija Refactor ili pomo¢u Alt-Shift-V.<br />

9.2 Povijest datoteke<br />

Ako shvatimo da smo krenuli s razvojem klase u pogre²nom smjeru, moºemo<br />

se vratiti na ranije stanje datoteke. U Package Explorer-u kliknemo desnim<br />

gumbom na na²u datoteku i odaberemo Replace with, Local history. Pojavit<br />

¢e se popis svih ranijih verzija datoteke koje su zapam¢ene.


POGLAVLJE 9. SAVJETI ZA RAD U ECLIPSE-U 121<br />

9.3 Seljenje projekta s ra£unala na ra£unalo<br />

9.3.1 Tipi£an problem prilikom selidbe<br />

Studenti se redovito susre¢u s potrebom da svoj projekt razvijaju na vi²e<br />

ra£unala u labosu, na svom laptopu, na ku¢nom ra£unalu itd. Projekt je<br />

prili£no jednostavno preseliti, ali jo² je jednostavnije to izvesti pogre²no,<br />

pogotovo stoga ²to je na raspolaganju £itav niz postupaka. Problemi nastaju<br />

zbog toga ²to postupak izvoza projekta nije usklažen s postupkom<br />

uvoza. Posebice, uo£ena je sljede¢a tipi£na pogre²ka:<br />

1. Projekt se ispravno izveze tako da glavno kazalo projekta sadrºi kazalo<br />

src koje je korijensko za datoteke s izvornim kodom. Ono ¢e<br />

tipi£no biti bez datoteka, samo s potkazalom hr.<br />

2. Na odredi²nom ra£unalu projekt se uveze tako da se kazalo src ne progla²ava<br />

korijenskim, ve¢ glavno kazalo projekta. Tako ispada da je src<br />

ustvari prvi segment imena paketa. Eclipse se sada ºali da deklaracija<br />

paketa u datoteci (npr. hr.fer.tel ²to je ispravno) ne odgovara<br />

kazalu u kojem se nalazi (src/hr/fer/tel ²to je neispravno!).<br />

3. Student odlazi na Quick x koji mu nudi izmjenu imena paketa u<br />

src.hr.fer.tel i time malu pogre²ku pretvara u puno ve¢u. To ponavlja<br />

za svaku datoteku.<br />

Ono ²to je zaista trebalo napraviti je popraviti korijenski direktorij izvornog<br />

koda. To se obavlja u Project, Properties, Java Build Path, Source.<br />

Tamo se nalazi okvir naslovljen Source folders on build path i unutra bi<br />

trebao biti navedeno kazalo src. Ako nije, dodajemo ga pomo¢u tipke Add<br />

folder...<br />

9.3.2 Savjeti za uspje²nu selidbu<br />

Sve datoteke projekta nalaze se na jednom mjestu. Potrebno je samo znati<br />

koje kazalo Eclipse koristi kao svoj Workspace. To se moºe doznati putem<br />

File, Switch Workspace. U tom kazalu za svaki projekt postoji po jedno<br />

potkazalo istog imena kao projekt. Op¢enito je dovoljno kazalo projekta<br />

preseliti na drugo ra£unalo, ponovo u kazalo tamo²njeg Workspace-a, pokrenuti<br />

Eclipse i pomo¢u File, New, Project, Java Project zatraºiti stvaranje


122 POGLAVLJE 9. SAVJETI ZA RAD U ECLIPSE-U<br />

projekta istog imena. Eclipse ¢e vas obavijestiti (u dnu dijalo²kog prozora)<br />

da kazalo tog imena ve¢ postoji i da ¢e sve postavke projekta biti preuzete.<br />

Eclipse nudi i izravnu podr²ku za selidbu projekta, kori²tenjem naredbi<br />

Export/Import. Na izvori²nom ra£unalu radimo ovo:<br />

1. U Package Explorer-u koristimo desni klik nad na²im projektom, zatim<br />

Export, Archive le<br />

2. Kad pritisnemo Next, nudi nam se mogu¢nost detaljnog odabira datoteka<br />

za izvoz. Datoteke .classpath i .project obvezno zadrºimo (tu<br />

su postavke projekta). Ako je bitno da arhiva bude ²to manja (mejl<br />

i sl.), moºemo izbaciti sva potkazala osim src.<br />

3. Niºe u dijalo²kom prozoru pomo¢u Browse odaberemo to£nu lokaciju<br />

i ime arhive koja ¢e se stvoriti. Provjerimo da su odabrane opcije Save<br />

in zip format i Create directory structure for les.<br />

4. Pritiskom na Finish izdajemo naredbu za stvaranje arhive.<br />

Stvorenu arhivu dopremamo do odredi²nog ra£unala na kojem pokre¢emo<br />

Eclipse i zatim:<br />

1. File, Import, Existing projects into Workspace. U dijalo²kom prozoru<br />

odabiremo opciju Select archive le i pomo¢u Browse nalazimo svoju<br />

arhivu.<br />

2. U dijalo²kom prozoru pojavljuje se popis svih projekata naženih u<br />

arhivi (trebo bi biti samo jedan). Provjerimo da je ime projekta ispravno<br />

i ozna£eno kva£icom.<br />

3. Pritiskom na Finish izdajemo naredbu za uvoz projekta.


Literatura<br />

[1] Java 2 Platform Standard Edition 5.0 API Specication.<br />

http://java.sun.com/j2se/1.5.0/docs/api.<br />

[2] Mary Campione, Kathy Walrath, and Alison Huml. The Java<br />

Tutorial. http://java.sun.com/docs/books/tutorial.<br />

[3] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.<br />

Design Patterns. Addison-Wesley Professional, 1995.<br />

[4] James Gosling, Bill Joy, Guy Steele, and Gilad Bracha. The Java<br />

Language Specication.<br />

http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html,<br />

2005.<br />

[5] James Gosling and Henry McGilton. The Java Language<br />

Environment, chapter 6: Security in Java.<br />

http://java.sun.com/docs/white/langenv/Security.doc.html.<br />

123

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!