Professional Documents
Culture Documents
Java PSZNR
Java PSZNR
Kako se postie platformska nezavisnost? Izvorni kod u Java programskom jeziku kreira se u tekstualnim datotekama sa ekstenzijom .java. Korienjem Java kompajlera, od izvornih .java datoteka nastaju .class datoteke, koje se mogu posmatrati kao ekvivalent .exe datoteka koje nastaju kao rezultat kompajliranja C programa. Me utim, za razliku od .exe datoteka, u kojima se nalazi binarni kod koji se izvrava samo na jednoj platformi, u .class datotekama se nalazi bajt-kod (eng. bytecode), koji se moe izvravati na Java Virtuelnoj Maini (eng. Java Virtual Machine ili Java VM). Poto Java VM implementacije postoje za sve znaajnije plaforme, tako se isti java bajt-kod moe izvravati na gotovo svakoj plarformi. Proces kreiranja i izvravanja platformski nezavisne Java aplikacije prikazan je na slici 1. Jasno je da Java VM ima ulogu prevo enja bajt-koda u mainski kod platforme na kojoj se aplikacija izvrava, to se najee odvija u toku samog izvrenja aplikacije. Ovakav pristup izvravanju aplikacija moe dovesti do pogoranja performansi. Me utim, novije Java VM vre razliite vrste optimizacije u toku rada aplikacije, efikasno minimizujui gubitak performansi nastao usled uvo enja jo jednog sloja izme u aplikacije i platforme na kojoj se ona izvrava.
Java platforma
Platforma se definie kao hardversko-softversko okruenje u kome se aplikacija izvrava. Prethodno su pomenute popularne platforme, kao to su Windows, Linux, Solaris, koje se mogu opisati kao kombinacija operativnog sistema i hardvera na kome se on izvrava. Za Java platformu je karakteristino da je to isto softverska platforma, koja postoji kao deo neke druge platforme bazirane na hardveru. Java platformu sainjavaju dve komponente:
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Java Virtuelna Maina Java API (eng. Application Programming Interface) Kao to je poznato, Java VM slui za izvravanje bajt-koda na raznim platformama. API je kolekcija korisnih programskih komponenti koje programerima pruaju mogunost za brz i lak razvoj aplikacija. Ove komponente su grupisane u biblioteke srodnih klasa i interfejsa, u Java terminologiji poznate kao paketi. Broj paketa koje nudi java je ogroman, i varira od najosnovnijih, pa sve do paketa koji podravaju mreno programiranje, rad sa bazama podataka ili rad sa XML-om.
Java aplikacija API Java virtuelna maina
Platforma bazirana na hardveru
Java platforma
Podeavanje sistema
Da bi alati koji se pozivaju iz komandne linije mogli da budu na eni pod Windows operativnim sistemom, potrebno je putanju do njih dodati u sistemsku promenljivu PATH. To je mogue uraditi privremeno ili trajno. Privremeno ubacivanje moe se izvriti iz komandne linije, kao to je prikazano na slici 3.
Nedostatak ovakvog pristupa je u tome to je ovako kreirana putanja vidljiva samo u DOS prozoru u kome je uneta i posle zatvaranja prozora prestaje da bude vidljiva. Trajno podeavanje sistemske putanje mogue je izvriti na sledei nain. Otvori se kontrolni panel i izabere stavka System. U okviru sekcije Advanced klikom na dugme Environment Variables (slika 4a) otvara se prozor za podeavanje sistemskih promenljivih. U listi sistemskih promenljivih se prona e promenljiva Path, oznai se i klikom na dugme Edit otvara se prozor prikazan na slici 4b. U tekst-polju Variable value se na kraj doda putanja do bin foldera instalacionog foldera JDK-a. U konkretnom primeru prikazanom na slici to je putanja:
c:\Program Files\Java\jdk1.6.0_04\bin\
Podrazumeva se da ova putanja zavisi od verzije JDK-a koja je instalirana u konkretnom sluaju. Da bi promene sistemske putanje postale vidljive operativnom sistemu, potrebno je da se posle objanjenog postupka izloguje i ponovo uloguje na sistem.
Prilikom kreiranja izvornog koda aplikacije voditi rauna o sledeem: Java programski jezik pravi razliku izme u velikih i malih slova, pa stoga izvorni kod aplikacije prepisati vodei rauna o velikim i malim slovima Preporuuje se da ime .java datoteke bude isto kao i ime klase koja se u njoj nalazi.
Ovim se poziva java kompajler javac, kome se nalae da prevede java datoteku datu kao argument u komandnoj liniji u bajt-kod. Rezultat prevo enja bie nova datoteka, HelloWorldApp.class, koja u sebi sadri bajt-kod. Navedeni primer predstavlja najjednostavniji primer poziva java kompajlera. Java kompajler se moe pozivati uz korienje dodatnih opcija, iji se spisak moe dobiti pozivanjem kompajlera bez ikakvih parametara. Jedan od najbitnijih parametara komandne linije kompajlera je classpath, i njegova funkcija je da kompajleru ukae na putanje na kojima se nalaze paketi i klase koje se koriste u aplikaciji koja se kompajlira. Pored postavljanja promenljive classpath iz komandne linije, nju je mogue postaviti i kao sistemsku promenljivu, pri emu je postupak identian onome koji je opisan u poglavlju Podeavanje sistema.
Pokretanje aplikacije
Kada je aplikacija kompajlirana i bajt-kod je na raspolaganju, mogue ju je pokrenuti korienjem aplikacije za pokretanje (eng. launcher tool) java. Za pokretanje aplikacije iz prethodno opisanog primera komandna linija data je ispod:
java HelloWorldApp
Kao i kompajler, i aplikacija za pokretanje ima vie parametara komandne linije, me u kojima je i classpath, koji ima isto znaenje kao i kod kompajlera. Stoga je radi
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave lakeg rada iz komandne linije poeljno ovaj parametar podesiti kao sistemsku promenljivu.
U izvornom kodu aplikacije koja je data kao primer moe se zapaziti neto drugaiji otvarajui komentar, /**. Od strane kompajlera, ovaj komentar se tretira kao obian otvarajui blok komentar sa simbolom * iza, koji kompajler potpuno ignorie. Ova vrsta komentara je znaajna za javadoc alat, koji na osnovu ovakvih komentara automatski generie dokumentaciju. Zato se ovakvi komentari nazivaju dokumentacioni komentari (eng. documentation comment). Druga vrsta komentara u Javi su linijski komentari, kod kojih se ignorie sadraj od znaka za komentar (//), pa do kraja linije. Primer linijskog komentara je dat ispod:
//ovo je linijski komentar
Definicija klase Aplikacija data u primeru sastoji se iz samo jedne klase, HelloWorldApp. Kao to se vidi iz primera, opti nain definisanja klase u javi je sledei:
class ImeKlase { // Telo klase }
Slubena re class oznaava poetak definisanja klase, iza nje sledi ime klase, iza koga se u vitiastim zagradama definie telo klase. Detalji vezani za klase bie objanjeni u narednim poglavljima. Main metoda U Java programskom jeziku, svaka aplikacija mora sadrati main metod koji se definie kao:
public static void main(String[] args)
Modifikatori public i static mogu biti napisani u proizvoljnom redosledu, ali je uobiajena praksa da se koriste u redosledu koji je dat u primeru. Metoda main preuzima parametre komandne linije u vidu niza stringova args, koji u optem sluaju moe biti nazvan proizvoljnim imenom. Ova metoda je ulazna taka u Java aplikaciju, i od nje uvek kree izvravanje Java aplikacije.
Kao to se vidi, za ispis se koristi funkcija klase System. Ova klasa sadri niz korisnih funkcija za rad sa standardnim ulazom (System.in.*), izlazom (System.out.*), kao i funkcije za rad sa standardnim izlaznim strimom za greke (System.err.*). Pored toga, klasa System sadri i funkcije za rad sa sistemskim promenljivima, garbage collectorom, sistemskim vremenom i druge korisne funkcije. Klasa System se ne moe instancirati, pa se njenim metodama i metodama klasa lanica pristupa kao u sluaju ispisa.
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Pri tome se tip odnosi na tip promenljive, i moe biti neki od prethodno navedenih primitivnih tipova, string ili ime klase. Deklaracija promenljive se moe zavriti i imenom promenljive, bez potrebe da se promenljiva postavlja na inicijalnu vrednost. U tom sluaju bi se odmah iza imena promenljive stavio znak ; . Sledei primer pokazuje deklarisanje nekoliko promenljivih, sa i bez inicijalizacije.
int decimalniBroj=15; int heksadecimalniInteger=0x2f; boolean brojUnet = true; byte neinicijalizovaniBajt; char slovoC= 'C'; short neinicijalizovaniShortTipPromenljive;
U sluaju da promenljive nisu eksplicitno inicijalizovane, Java kompajler ih postavlja na poznate predefinisane vrednosti, kao to je prikazano u tabeli 1.
Tip podataka short int long float double char String ili bilo koji objekt boolean Inicijalna vrednost 0 0 0L 0.0f 0.0d '\u0000' null false
Nizovi
Niz predstavlja objekat koji sadri fiksan broj promenljivih istog tipa. Veliina niza se odre uje prilikom kreiranja niza. Jedan od naina kreiranja niza je pomou new operatora:
tip[] imeNiza = new tip[dimenzijaNiza];
U prethodnoj liniji, tip predstavlja tip podataka, koji moe biti bilo jedan od primitivnih tipova ili klasa. imeNiza je ime promenljive koja uva niz, a dimenzijaNiza je broj elemenata niza. Niz je mogue u istom izrazu kreirati i inicijalizovati, kao to je prikazano u sledeoj liniji:
tip[] imeNiza = {vrednost1, vrednost2, ,vrednostN};
U prethodnoj liniji se kreira niz imeNiza elemenata tipa tip, i istovremeno se postavlja N inicijalnih vrednosti elemenata. Istovemeno je brojem elemenata (N) koji se nalaze u vitiastoj zagradi odre ena i dimenzija niza. Sledi nekoliko primera kreiranja nizova, sa i bez inicijalizacije.
int[] triIntegera = new int[3]; String[] daniNedelje={pon,uto,sre,cet,pet,sub,ned};
Svakom objektu koji predstavlja niz pridruena je lanica promenljiva length, koja sadri veliinu niza. Na primer, broj dana u nedelji iz prethodnog primera je daniNedelje.length. Kopiranje elemenata nizova iz jednog niza u drugi moe se vriti korienjem metode arrayCopy klase System. Ova funkcija je definisana na sledei nain:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) // // // // // izvorni niz poetni indeks odredini niz poetni indeks u odreditu broj elemenata za kopiranje
Poto sve klase u Javi nasle uju klasu Object, tako se ova funkcija moe koristiti za kopiranje nizova proizvoljnog tipa.
Operatori
Operatori su specijalni simboli koji obavljaju odre ene operacije nad jednim, dva ili tri operanda i vraaju rezultat izvrene operacije. U Javi nemaju svi operatori jednak prioritet, i u sloenijim izrazima bitno je poznavati redosled izvravanja operacija. Java operatori pore ani po prioritetu i grupisani po tipovima su dati u tabeli 2. Operatori pore ani prema redosledu izvravanja Tip operatora postfix unary multiplicative additive shift relational equality bitwise AND
expr++ expr-++expr --expr +expr -expr ~ ! * / % + << >> >>> < > <= >= instanceof == != &
Redosled
Unarni operatori + plus operator; oznaava pozitivnu vrednost (iako se moe izostaviti) minus operator; vri negaciju izraza ++ operator inkrementiranja; poveava vrednost promenljive za 1 -- operator dekrementiranja; smanjuje vrednost promenljive za 1 ! operator logikog komplementa; vri invertovanje boolean promenljivih
int result = +1; // result postaje 1 System.out.println(result); result--; // result postaje 0 System.out.println(result); result++; // result postaje 1 System.out.println(result); result = -result; // result postaje -1
10
Operatori jednakosti i relacioni operatori == je jednako != nije jednako > je vee >= je vee ili jednako < je manje <= je manje ili jednako
int value1 = 1; int value2 = 2; if(value1 == value2) System.out.println("value1 == value2"); if(value1 != value2) System.out.println("value1 != value2"); if(value1 > value2) System.out.println("value1 > value2"); if(value1 < value2) System.out.println("value1 < value2"); if(value1 <= value2) System.out.println("value1 <= value2");
Uslovni operatori && Uslovno-I || Uslovno ILI ?: Terinarni operator (skraenica za if-then-else konstrukciju)
int value1 = 1; int value2 = 2; int result; if((value1 == 1) && (value2 == 2)) System.out.println("value1 je 1 I value2 je 2"); if((value1 == 1) || (value2 == 1)) System.out.println("value1 je 1 ILI value2 je 1"); boolean someCondition = true; result = someCondition ? value1 : value2; System.out.println(result);
Operator pore enja tipova instanceof Odre ivanje da li je objekat instanca neke klase
class InstanceofDemo { public static void main(String[] args) {
11
Bitski operatori i operatori pomeranja ~ unarni bitski komplement << oznaeno pomeranje u desno >> oznaeno pomeranje u levo >>> neoznaeno pomeranje u levo & bitska I operacija ^ bitska ekskluzivno ILI operacija | bitska ILI operacija
int bitmask = 0x000F; int val = 0x2222; System.out.println(val & bitmask); // ispisuje "2"
12
Iskaz if-then-else
Opti format ovog iskaza je dat u primeru koji sledi. Tok programa objanjen je u komentarima.
if (prviUslov) { // Deo koda koji se izvrava ako je ispunjen prviUslov } else if (drugiUslov) { // Razmatra se samo ako prviUslov nije ispunjen // Izvrava se ako je drugiUslov ispunjen } else if (uslovN) { // Ako ni jedan od prethodnih uslova nije ispunjen, a ovaj jeste // onda e se ovaj deo koda izvriti } else { // Ovaj deo koda se izvrava samo u sluaju kad ni jedan prethodni // uslov nije ispunjen. // Broj else grana nije ogranien u Javi }
Treba napomenuti da je mogue da vie uslova istovremeno moe biti zadovoljeno. U tom sluaju, izvrie se deo koda koji se nalazi unutar prvog bloka koji ispunjava dati uslov, dok ostali uslovi nee ni biti razmatrani.
Iskaz switch-case
Switch-case iskaz radi sa byte,short,char i int primitivnim tipovima podataka, sa enumerisanim tipovima, kao i sa nekoliko specijalnih klasa koje su kreirane oko nekih od primitivnih klasa: Character, Byte, Short i Integer. Primer dat ispod demonstrira korienje switch-case iskaza.
class SwitchDemo { public static void main(String[] args) { int mesec = 8; switch (mesec) { case 1: System.out.println("Januar"); break; case 2: System.out.println("Februar"); break; case 3: System.out.println("Mart"); break; case 4: System.out.println("April"); break; case 5: System.out.println("Maj"); break; case 6: System.out.println("Jun"); break; case 7: System.out.println("Jul"); break; case 8: System.out.println("Avgust"); break; case 9: System.out.println("Septembar"); break; case 10: System.out.println("OKtobar"); break; case 11: System.out.println("Novembar"); break; case 12: System.out.println("Decembar"); break; default: System.out.println("Pogrean unos.");break;
13
Petlja while se izvrava sve dok je uslov ispunjen. Uslov moe biti bilo kakav izraz iji je rezultat tipa boolean. Iskaz do-while je slian while iskazu:
do { // Telo iskaza koje se mora izvriti barem jednom, dok tok // izvravanja ne dostigne while po prvi put } while(uslov);
Razlika izme u while i do-while iskaza je u tome to se telo do-while iskaza mora izvriti makar jednom, to nije sluaj sa while iskazom.
Iskaz for
Iskaz for je pogodan za iteraciju kroz opsege vrednosti. Opti oblik ovog iskaza je:
for (inicijalizacija; terminacija; inkrement) { // telo petlje }
Kod korienja for iskaza, treba znati sledee: Izraz inicijalizacija se izvrava samo jednom, kada se petlja prvi put izvrava Kada izraz terminacija dobije vrednost false, prekida se izvravanje petlje Izraz inkrement se izvrava u svakoj iteraciji petlje, i moe obavljati bilo kakvu operaciju (operacija moe i izostati) Jednostavan primer korienja for iskaza:
class ForDemo { public static void main(String[] args){ for(int i=1; i<11; i++){ System.out.println("broja je: " + i); } } }
U primeru treba primetiti da se deklaracija i inicijalizacija brojake promenljive i obavlja u for iskazu, ime se obezbe uje da ona bude vaea samo u telu petlje. Sva tri izraza u definiciji for petlje su proizvoljna. Njihovim izostavljanjem dobija se beskonana petlja:
for ( ; ; ) { // beskonana petlja // telo petlje }
14
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Pored uobiajene forme for petlje, postoji i unapre ena (eng. enhanced) for petlja, koja je pogodna za iteraciju elemenata kolekcija i nizova. U sledeem primeru dat je nain korienja unapre ene for petlje:
class EnhancedForDemo { public static void main(String[] args){ int[] brojevi = {1,2,3,4,5,6,7,8,9,10}; for (int broj : brojevi) { System.out.println("Broj je: " + broj); } } }
Break iskaz
Pored korienja break iskaza u switch-case iskazu, on se moe koristiti i za prekidanje izvrenja for, while ili do-while petlje. Sledei primer demonstrira primenu break iskaza za promenu toka programa:
class BreakDemo { public static void main(String[] args) { int[] nizIntova = { 32, 87, 3, 589, 12, 1076, 2000, 8, 622, 127 }; int trazeniBroj = 12; int i; boolean nasaoSam = false; for (i = 0; i < nizIntova.length; i++) { if (nizIntova [i] == trazeniBroj) { nasaoSam = true; break; } } if (nasaoSam) { System.out.println("Nasao sam " + trazeniBroj + " na poziciji " + i); } else { System.out.println(trazeniBroj + " nije u nizu"); } } }
Uobiajeno ponaanje break iskaza je da prekida izvravanje prve petlje u kojoj se nalazi. Korienjem labele u break iskazu, mogue je prekinuti i spoljnu petlju oznaenu labelom, kao to je prikazano u sledeem primeru.
class BreakWithLabelDemo { public static void main(String[] args) { int[][] nizIntova = { { 32, 87, 3, 589 }, { 12, 1076, 2000, 8 }, { 622, 127, 77, 955 } }; int trazeniBroj = 12; int i; int j = 0;
15
Vano je napomenuti da ovaj tip break iskaza ne upuuje tok programa na liniju oznaenu labelom, ve prekida petlju koja je oznaena labelom.
Iskaz continue
Iskaz continue koristi se u for, while i do-while petljama. Ovaj iskaz navodi program da preskoi deo koda petlje ispod continue i da tok izvravanja programa vrati na izraunavanje logikog izraza koji kontrolie izvravanje petlje. Kao i kod break iskaza, i continue se odnosi na unutranju petlju u kojoj se nalazi.
class ContinueDemo { public static void main(String[] args) { String zaPretragu = "petar popusta pod pritiscima"; int max = zaPretragu.length(); int brojPova = 0; for (int i = 0; i < max; i++) { //interested only in p's if (zaPretragu.charAt(i) != 'p') continue; //process p's brojPova++; } System.out.println("Nasao sam " + brojPova + " slova p u stringu."); } }
Isto kao i kod break iskaza, continue iskaz moe koristiti labele za preskakanje izvrenja spoljanje petlje. I ovde vai napomena da se labela koristi za odre ivanje na koju petlju se odnosi continue iskaz, a ne za preusmeravanje toka programa.
16
Iskaz return
Ovaj iskaz prekida izvrenje trenutne metode i vraa se u metodu iz koje je pozvana. Iskaz return ima dve forme jednu koja vraa vrednost i drugu koja ne vraa vrednost. U sluaju kada return vraa vrednost, treba voditi rauna da se tip podatka koji se vraa poklapa sa tipom koji je naveden kod deklaracije funkcije. Opti oblik return iskaza je:
return povratnaVrednost;
Kod funkcija kojima je kao povratni tip naznaen void, povratnaVrednost se izostavlja.
17
Poto se sa pozdravnog ekrana odabere krui Workbench, prikazuje se radno okruenje kao na slici 4.
Izborom opcije File->New pokree se proces kreiranja novog projekta u nekoliko koraka. Odmah po izboru opcije otvara se dijalog prikazan na slici 5. Poto se funkcionalnost Eclipse platforme moe proirivati pomou plug-inova, lista ponu enih wizarda u dijalogu moe se razlikovati od sistema do sistema. Svrha svakog od wizarda je da vodi korisnika kroz proces kreiranja odre ene vrste projekta. Za Java aplikaciju potrebno je izabrati iz ponu ene liste Java Project i prei na sledei korak klikom na dugme Next.
18
U prozoru na slici 5 koji se pojavljuje u sledeem koraku uneti ime projekata i izabrati opciju Create project from existing source, ime se obezbe uje da se bira lokacija na koju se projekat i datoteke vezane za njega smetaju. Korienjem dugmeta Browse izabrati lokaciju na kojoj e kreirani projekat biti smeten. Dugmetom Next moe se prei na sledei korak u kreiranju projekta, ukoliko je potrebno izvriti dodatna podeavanja radnog okruenja. Ukoliko dodatna podeavanja nisu potrebna, klikom na dugme Finish proces kreiranja projekta se zavrava.
19
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Za podeavanje parametara Java okruenja, kao to su putanje do biblioteka, nain organizacije izvornog koda i class fajlova i slino koristi se poslednji u nizu dijaloga. Poto za ovde opisane primere nisu potrebna dodatna podeavanja, klikom na Finish zavrava se proces kreiranja novog Java projekta.
20
U dijalogu sa slike 8 u polje name uneti HelloWorldApp, i selektovati opcije public static void main(String[] args) i Generate comments. Klikom na dugme Finish, bie kreiran Java fajl koji ima isto ime kao klasa, i bie dodat u projekat u default paket (to inae nije dobra praksa). U editoru koji zauzima najvei deo ekrana se moe videti automatski generisan kod, koji u sebi sadri i main metodu. Za potrebe demonstriranja pokretanja aplikacije iz Eclipse okruenja potrebno je uneti liniju za ispis teksta Hello World! kao to je to pokazano u prvoj vebi. Tokom kucanja, Eclipse nudi opcije za automatsko dopunjavanje koda, kao to je prikazano na slici 9. Tako e, u toku unosa izvornog koda Eclipse vri sintaksnu proveru izvornog koda i neispravne (ili nekompletne) linije podvlai crvenom bojom, nudei objanjenje greke u vidu hinta na ikonici greke koja se pojavljuje uz levu ivicu prozora editora.
21
Pokretanje aplikacije vri se izborom opcije Run as->Java Application iz menija Run ili izborom iste opcije iz kontekst-menija projekta. Po pokretanju aplikacije, izlaz e biti ispisan u pogledu console koji e se pojaviti u donjoj polovini prozora aplikacije.
Debagovanje programa
U Java aplikacije mogu se postavljati prekidne take (eng. breakpoint) koje zaustavljaju izvrenje aplikacije na odre enoj liniji programa i omoguavaju programeru da analizira stanje promenljivih u tom trenutku izvrenja aplikacije. Prekidne take se postavljaju dvostrukim klikom na levu ivicu prozora editora, pre poetka linije na kojoj se eli zaustaviti izvrenje aplikacije, ili izborom opcije Toggle Breakpoint iz kontekst menija. Pokretanje programa u debag modu se vri opcijom Debug as->Java Application koja se nalazi u Run meniju. Po pokretanju, Eclipse pita korisnika da li eli da promeni perspektivu na Debug, na ta treba potvrdno odgovoriti. Perspektiva se potom menja i program se izvrava sve dok ne nai e na prvu prekidnu taku, gde se izvravanje prekida i kontrola preputa korisniku. Korisnik tada moe aplikaciju izvravati korak po korak komandama iz menija Run. Izgled prozora Eclipse aplikacije u Debug perspektivi vidi se na slici 10. Stanje promenljivih u trenutku prekida moe se pratiti u pogledu Variables, a na sredini prozora aplikacije nalazi se prozor editora u kome je posebno oznaena linija programa na kojoj je prekinuto izvrenje.
22
23
Prethodni kod predstavlja definiciju (deklaraciju) klase. Telo klase se nalazi u prostoru izme u zagrada i ono sadri definiciju polja klase (promenljivih), zatim konstruktora (za inicijalizaciju novih objekata klase) i na kraju metoda koje implementiraju ponaanje objekata klase. Prethodna definicija klase predstavlja osnovnu definiciju klase. U optem sluaju klase u Javi mogu nasle ivati druge klase i implementirati interfejse. Sledi primer definicije klase koja nasle uje drugu klasu i implementira interfejs:
class MountainBicycle extends Bicycle implements MountainInterface { //polja, konstruktor, i metode }
Ovaj primer opisuje klasu MountainBicycle koja nasle uje klasu Bicycle, i implementira interfejs MountainInterface (koncepti nasle ivanja i implementiranja interfejsa dati su u sledeim poglavljima). Tako e na poetku deklaracije klase mogue je naglasiti dali je ona public ili private i na taj nain odrediti dali i druge klase mogu da pristupaju datoj klasi. Uopteno deklaracija klase moe sadrati: Modifikatore public, private. Naziv klase (po konvenciji prvo slovo naziva je veliko slovo). Kljunu re extends posle koje sledi naziv klase koju elimo da nasledimo (klasa moe naslediti samo jednu klasu). Kljunu re implements iza koje sledi naziv interfejsa koji elimo da implementiramo. Klasa moe implementirati vie od jednog interfejsa, a tada se lista implementiranih interfejsa razdvaja zarezom. Telo klase, uokvireno vitiastim zagradama
24
Definicija promenljive klase ima tri dela: Nijedan ili vie modifikatora promenljive (public, private, protected, final, static, const) Tip promenljive (osnovni tip int, float, boolean ili tip koji se prenosi po referenci string, objekt, niz) Naziv promeljive (cadence u klasi Bicycle npr.) Modifikatori public, private i protected omoguavaju kontrolisanje pristupa promeljivama klase. Kontrola pristupa promenljivama bie opisana kasnije. Modifikator final ispred tipa promeljive oznaava promeljvu koja je konstanta. Vrednost ovakve promenljive zadaje se jednom i kasnije se ne moe menjati. Primer ovakve promeljive je broj Pi. Modifikator static oznaava promeljive koje su deljene me u svim instancama date klase u kojoj se nalazi takva promeljiva (postoji samo jedna instanca date promenljive). Na ovaj nain mogu se oznaavati globalne promeljive ili globalne konstante. Da bi se pristupalo datoj static promeljivoj nije potrebno praviti objekte klase koja sadri datu promeljivu. Dovoljno je staviti naziv klase ispred naziva date promeljive npr:
Bycicle.vehicleName = Bicycle_1;
Modifikator const oznaava konstante vidljive u okviru jednog objekta date klase. Za razliku od final promeljivih, const promeljive mogu biti zadane, ali im se vrednost ipak moe naknadno promeniti.
Deklarisanje metoda
Sledi primer uobianje deklaracije metoda klase:
public double calculateAnswer(double wingSpan, int numberOfEngines, double length, double grossTons) { //uraditi izraunavanje ovde }
Neophodni elementi deklaracije metode su: povratna vrednost metode, naziv metode, lista parametara izme u obinih zagrada () kao i telo metode izme u vitiastih zagrada {}. Uopteno deklaracija metode moe sadrati sledeih est delova: Modifikatore public, private (modifikatori pristupa e biti objanjeni kasnije). Tip povradne vrednosti funkcije npr. int, boolean... Tip void se stavlja ako metoda nema povratne vrednosti. Naziv metode.
25
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Listu parametara metode, razdvojenih zarezima. Parametri moraju imati tip ispred naziva. Listu izuzetaka (o izuzecima e biti vie rei u narednim poglavljima). Telo metode (nalazi se u okviru vitiastih zagarada). Po konvenciji naziv metoda poinje malim slovom, a ako se naziv metode sastoji iz vie rei svaka sledea re u nazivu poinje velikim slovom. Npr: getFinalData() .
26
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Ovakav nain inicijalizacije je dobar ako je data promenljiva prosta, tj. ako se njena inicijalizacija moe obaviti u jednoj liniji koda. U sluaju kada treba inicijalizovati kompleksne promeljive tipa niza, koje zahtevaju prolazak kroz for petlju da bi bile inicijalizovane, ovakav nain inicijalizacije je nepogodan. Kompleksne promeljive mogu biti inicijalizovane kroz konstruktor klase, pri stvaranju svakog objekta date klase. Sledi primer konstruktora klase Bicycle koji se koristi za incijalizacju promeljivih date klase (pri stvaranju objekata klase):
public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; }
Iz primera se vidi da konstruktor klase ima oblik metode klase. Razlika je u tome to konstruktor ima isti naziv kao i sama klasa, a pri tome ne poseduje povratnu vrednost. U sluaju da se eli inicijalizovati static promenljiva klase, to se moe uiniti upotrebom static blokova za inicijalizaciju promeljivih. Static blok za inicijalizaciju promeljivih poinje kljunom reju static iza koje slede zagrade {}.U njima se nalazi kod koji se koristi za inicijalizaciju static promeljivih. Primer static bloka:
static { // kod za inicijalizaciju static promenljivih }
Klasa moe da ima proizvoljan broj static blokova za inicijalizaciju, i oni se mogu nalaziti bilo gde u telu klase. Ono to je bitno jeste da se izvavanje datih blokova za inicijalizaciju odvija u redosledu u kom se pojavljuju u telu klase. Alternativno, moe se umesto static blokova za iniciajlizaciju promeljivih napraviti private static metoda:
class Whatever { public static varType myVar = initializeClassVariable(); private static varType initializeClassVariable() { // kod za inicijalizaciju static promenljive } }
Prednost pravljenja private static metoda jeste to one mogu biti iskoriene kasnije, kako bi se reinicijalizovala neka static promeljiva (npr. neki niz globalnih brojaa).
Ugnjedene klase
Java programski jezik omoguuje definisanje klase u okviru neke druge klase. Takva klasa se naziva ugnjedena klasa. Primer ugnjedene klase:
class OuterClass { ... class NestedClass { ... } }
27
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Ugnjedena klasa je lanica klase koja je obuhvata (spoljne klase), i kao takva ima pristup svim promenljivama spoljne klase. Ugnjedena klasa moe biti deklarisana kao private, public ili protected. Tako e ugnjedene klase mogu biti i static klase:
class OuterClass { ... static class StaticNestedClass { ... } class InnerClass { ... } }
Ugnjedene klase se koriste zbog: Logikog grupisanja klasa koje se koriste samo na jednom mestu (ako se jedna klasa koristi samo na zahtev neke druge klase, onda je logino da ta druga klasa obuhvati klasu koju koristi) Poveavanja enkapsulacije (nain da ugnjedena klasa pristupa svim promeljivama spoljne klase, a da pri tom ugnjedena klasa ostane skrivena) Poveavanja razumljivosti koda, kao i lakeg odravanja koda Razlikujemo dva tipa ugnjedenih klasa: statike ugnjedene klase i unutranje klase. Statike ugnjedene klase Kao i metode i promeljive klase, statike ugnjedene klase su povezane sa svojom spoljom klasom. Statika ugnedena klasa ne moe da pristupa direktno promeljivama i metodama svoje spoljne klase, ve im moe pristupati jedino preko objekta spoljne klase. Statikim ugnjedenim klasama se moe pristupiti preko imena njene spoljne klase:
OuterClass.StaticNestedClass
Unutranje klase Kao metode i promeljive instance neke klase, tako su i ugnjedene klase povezane sa instancom svoje spoljne klase. Kao takve, ugnjedene klase mogu pristupati metodama i poljima objekta svoje spoljne klase. Poto su povezane sa instancom spoljne klase, ugnjedene klase ne mogu imati statikih polja u sebi. Objekti koji su instance ugnjedene klase postoje u okviru instance spoljne klase. Za ilustraciju - u prethodnom primeru smo imali sledee klase:
28
OuterClass,
Instanca klase InnerClass moe postojati samo u okviru instance klase i data instanca ugnjedene klase ima pristup poljima i metodama spoljne instance. Sledea slika opisuje odnos spoljne i ugnjedene klase:
Da bi se instancirao objekat unutranje klase, mora se prvo instancirati objekat spoljne klase. Tada se tek moe napraviti objekat unutranje klase na sledei nain:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
29
Na vrhu hijerarhije nalazi se osnovna klasa Object. Neke druge klase su direktno izvedene iz klase Object, a neke dalje klase su izvedene iz tih izvedenih klasa itd... Nasle ivnje uvodi mogunost ponovnog korienja prethodno napisanih klasa. Ovo je veoma bitno jer se pri stvaranju nove klase moe se naslediti prethodno napisana klasa i na taj nain se mogu iskoristiti polja i metode koje ona poseduje. Subklasa nasle uje sve metode, polja i ugnjedene klase superklase. Jedino to ne nasle uje jeste konstruktor superklase. Ipak ovaj konstruktor se moe pozvati kljunom reju super. Sledi primer implementacije superklase i subklase. Superklasa e u ovom sluaju biti ranije spomenuta klasa Bicycle , a klasa izvedena iz nje e biti klasa MountainBike:
public class Bicycle { // klasa Bicycle ima tri polja public int cadence; public int gear; public int speed; // klasa Bicycle ima jedan konstruktor public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed;
30
Subklasa nasle uje sve public i protected lanove superklase. Tako e ako je u istom paketu sa superklasom, subklasa nasle uje iz superklase i polja koja nemaju modifikator pristupa (public, private, protected). Subklasa omoguuje sledee: Nasle ena polja se mogu koristiti direktno Subklasa moe deklarisati polja istog naziva kao i polja u superklasi, i na taj nain skriti polja superklase (nije preporuljivo) Subklasa moe deklarisati nova polja koja nisu u superklasi Nasle ene metode mogu biti koriene direktno Subklasa moe definisati novu metodu koja ima istu signaturu (isti naziv, tip povratne vrednosti, broj i tip parametara).kao metoda u superklasi. Na ovaj nain se vri override (prepis) metode iz superklase.
31
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Subklasa moe definisati novu static metodu koja ima istu signaturu kao metoda u superklasi. Na ovaj nain se skriva metoda iz superklase. Mogu se definisati metode koje nisu u superklasi. Moe se napisati konstruktor subklase koji poziva konstruktor superklase korienjem kljune rei super.
Override metode iz superklase vri se definisanjem metode sa istom signaturom (isti nazivom, tipom povratne vrednosti, brojem i tipom parametara) u subklasi. Overrideom metode u subklasi omoguuje se modifikovanje date metode superklase. Skrivanje metode iz superklase vri se definisanjem static metode u subklasi koja ima istu signaturu kao i metoda u superklasi. Razlika izme u skrivane metode i override ovane metode jeste to ako se poziva override ovana metoda, onda se poziva metoda subklase. Sa druge strane ako se poziva skrivena metoda, funkcionalnost zivisi od toga dali se metoda pozvala iz superklase ili subklase. Sledi primer poziva skrivenih metoda dat kroz dve klase: Animal i Cat:
public class Animal { public static void testClassMethod() { System.out.println("The class method in Animal."); } public void testInstanceMethod() { System.out.println("The instance method in Animal."); } }
Subklasa Cat:
public class Cat extends Animal { public static void testClassMethod() { System.out.println("The class method in Cat."); } public void testInstanceMethod() { System.out.println("The instance method in Cat."); } public static void main(String[] args) { Cat myCat = new Cat(); Animal myAnimal = myCat; Animal.testClassMethod(); myAnimal.testInstanceMethod(); } }
Klasa Cat vri override metode testInstanceMethod iz klase Animal, a tako e vri i skrivanje metode testClassMethod iz iste klase (Animal). Pri pozivu main metode iz klase Cat dobija se sledei ispis:
The class method in Animal. The instance method in Cat.
32
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Kao to se moe videti poziv skrivene metode jeste poziv metode superklase (jer se pozvala statika metoda superklase), a poziv override-ovane metode jeste poziv metode subklase, to je u skladu sa pravilima skrivanja odnosno override-ovanja metoda u nasle ivanju.
Public modifikator znai da interfejs moe koristiti bilo koja klasa u bilo kom paketu. Ako se ne specificira da je interfejs public tipa , dati interfejs e biti vidljiv samo klasama u okviru istog paketa. Kljuna re extends u deklaraciji interfejsa znai da interfejs nasle uje neke druge interfejse (u ovom sluaju Interface1, Inteface2). Treba primetiti da za razliku od klasa - interfejs moe da nasledi vie od jednog interfejsa. Sve metode deklarisane u interfejsu su impicitno public tipa, tako da se ovaj modifikator ne mora stavljati ispred naziva metode. Sve konstante deklarisane u interfejsu su implicitno public static final , tako da se ovaj modifikator moe izostaviti ispred naziva konstante. Da bi se koristio interfejs, pie se klasa koja implementira dati interfejs (definie tela metoda deklarisanih u interfejsu). Npr:
public class RectanglePlus implements Relatable { public int width = 0; public int height = 0; public Point origin; // four constructors public RectanglePlus() { origin = new Point(0, 0); } public RectanglePlus(Point p) { origin = p; } public RectanglePlus(int w, int h) { origin = new Point(0, 0); width = w; height = h; }
33
Jedna klasa moe da implementira vie od jednog interfejsa. Tada je lista interfejsa koji se implementiraju odvojena zarezom. Kada se definie novi interfejs, definie se novi referentni tip podataka. Nazivi interfejsa se mogu koristiti gde god se koriste imena bilo kog drugog tipa podataka (naziv interfejsa postaje novi tip podataka). Ako se definie promeljiva iji je tip interfejs, objekat koji se dodeljuje datoj promeljivoj mora biti objekat klase koja implementira dati interfejs. Na primer:
public Object findLargest(Object object1, Object object2) { //object1 i object2 moraju biti instance klase koja implementira //interfejs Relatable Relatable obj1 = (Relatable)object1; Relatable obj2 = (Relatable)object2; if ( (obj1).isLargerThan(obj2) > 0) return object1; else return object2; }
Ako se eli promena strukture interfejsa dodavanjem neke nove metode, ta promena se moe izvriti nasle ivanjem interfejsa, i dodavanjem nove metode u nasle eni interfejs:
34
Dodavanje nove metode u stari interfejs koji je ve implementiran u nekim klasama dovee do kompajlerske greke (jer klase koje implementiraju dati interfejs moraju implementirati sve njegove metode pa i novo ubaenu):
public interface DoIt { void doSomething(int i, double x); int doSomethingElse(String s); boolean didItWork(int i, double x, String s); }
Poziv konstruktora superklase mora biti prva linija u konstruktoru subklase. Sintaksa poziva konstruktora superklase zavisi dali konstruktor superklase nema parametara ili ima. Npr:
super(); --ili-super(parameter list);
Ako konstruktor subklase ne pozove eksplicinto konstruktor superklase, Java kompajler e pozvati implicitno konstruktor superklase koji nema argumenata. Ako takav konstruktor ne postoji, kompajler e ispisati greku. Pozivi konstruktora superklasa se nadovezuju jedan za drugim u skladu sa redosledom nasle ivanja klasa, a proteu se sve do osnovne klase Object (ova osobina je poznata kao ulanavanje konstruktora).
35
Ako klasa sadri apstraktne metode, ona sama mora biti deklarisana apsktraktnom:
public abstract class GraphicObject { // deklaracija polja // deklaracija ne-apstraktnih metoda abstract void draw(); }
Kada se apstraktna klasa nasle uje, njena subklasa obino sadri implementaciju svih njenih apstraktnih metoda. Ukoliko subklasa apstraktne klase ne sadri implementaciju njenih apstraktnih metoda , onda se i ta subklasa mora deklarisati apstraktnom. Sve metode interfejsa su implicitno apstraktne, jer ne sadre implementaciju. Za razliku od interfejsa apstraktne klase mogu posedovati polja (promenljive), koje nisu static final promeljive, a tako e mogu posedovati i implementirane metode. Apstraktne klase lie na interfejse, a razlika je u tome to sadre deliminu implementaciju metoda, ostavljajui subklasama da kompletiraju implementaciju. Ako apstraktna klasa sadri samo definicije metoda, onda ona treba biti preimenovana u interfejs. Dobar primer korienja apstraktne klase jeste grafika aplikacija u kojoj mogu da se crtaju razliiti oblici - kvadrati, krugovi, linije itd Svi ovi objekti imaju sledee zajednike osobine: stanja (poziciju, orjentaciju, boju linije ) ponaanja ( pomeraj na poziciju, rotacija, promena veliine)
Neka od ovih stanja i ponaanja su ista za sve grafike objekte na primer: pozicija, boja linije i funkcija pomeraj na poziciju. Druga stanja i ponaanja su razliita i zahtevaju drugaije implementacije na primer: promena veliine i rotacija. Svi grafiki objekti moraju znati kako da promene veliinu ili da se rotiraju, jedino to je razliito jeste na koji nain to rade. Ovo je mesto gde se moe iskoristiti apstraktna superklasa, koja e objednjavati iste osobine svih grafikih objekata kao to je ilustrovano na slici:
36
Implementacija primera sa slike prvo zahteva deklaraciju apstraktne klase GraphicObject koja e sadrati polja i metode koje su zajednike za sve njene subklase (Rectangle, Line, Bezier i Circle). Zajedniko polje je na primer pozicija, a zajednika metoda je - pomeraj na poziciju. GraphicObject tako e deklarie apstraktne metode, na primer iscrtaj i promeni veliinu. Ove metode e biti implementirane od strane subklasa na raziliite naine. Primer klase GraphicObject:
abstract class GraphicObject { int x, y; ... void moveTo(int newX, int newY) { ... } abstract void draw(); abstract void resize(); }
Svaka ne-apstraktna subklasa klase GraphicObject implementaciju apstraktnih metoda draw i resize:
class Circle extends GraphicObject { void draw() { ... } void resize() { ... } } class Rectangle extends GraphicObject { void draw() { ... } void resize() { ... } }
mora
sadrati
Apstraktna klasa moe tako e implementirati i interfejs, ali za razilku od obine klase ne mora implementirati sve metode iz interfejsa. Na primer:
abstract class X implements Y { // implementira sve, osim jedne metode iz interfejsa Y } class XX extends X { // implementira preostali metod interfejsa Y }
Apstraktna klasa moe sadriati static polja i static metode, a njima se moe pristupiti kao to im se pristupa u okviru obine klase:
AbstractClass.staticMethod()
37
Sve ove klase i interfejs bi trebali da se stave u jedan paket zbog sledeih razloga: Lako se moe utvrditi da su date klase u relaciji Drugi korisnici mogu odrediti gde da trae funkcije koje su u relaciji sa grafikom Imena klasa u okviru jednog paketa nisu u koliziji sa imenima klasa u drugim paketima jer se date klase nalaze u razliitim paketima Klase u okviru jednog paketa mogu imati neogranien pristup drugim klasama u okviru svog paketa, a da pri tom pristup klasama iz drugih paketa bude zabranjen.
Kreiranje paketa vri se stavljanjem kljune rei package na sam poetak datoteke. Nakon kljune rei package sledi naziv paketa. Na taj nain datoteka i sve 38
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave njene klase i interfejsi postaju deo datog paketa.Kljuna re package mora biti prva re u datoteci, a jedna datoteka moe biti lan samo jednog paketa. Formiranje paketa klasa i interfejsa iz prethodnog primera izlgledalo bi ovako:
//u fajlu Draggable.java package graphics; public interface Draggable { . . . } //u fajlu Graphic.java package graphics; public abstract class Graphic { . . . } //u fajlu Circle.java package graphics; public class Circle extends Graphic implements Draggable { . . . } //u fajlu Rectangle.java package graphics; public class Rectangle extends Graphic implements Draggable { . . . } //u fajlu Point.java package graphics; public class Point extends Graphic implements Draggable { . . . } //u fajlu Line.java package graphics; public class Line extends Graphic implements Draggable { . . . }
Ako se ne naglasi kom paketu pripada datoteka, ona e se nai u neimenovanom paketu. Uopteno gledajui neimenovani paketi su prihvatljivi samo kod malih i privremenih aplikacija. U suprotnom klase i interfejse je potrebno staviti u imenovane pakete. Da bi se koristile klase ili interfejsi (lanovi paketa) iz odre enog paketa potrebno je uraditi jedno od sledeeg: Pristupiti lanu paketa preko njegovog punog imena Ukljuiti dati lan u datoteku Ukljuiti u datoteku itav paket u kome se nalazi dati lan
39
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Ako se lanu paketa eli pristupiti iz nekog drugog paketa, a da pri tom paket datog lana nije ukljuen, pristup se moe izvriti preko punog imena lana (koje ukljuuje i naziv paketa). Puno ime klase Rectangle iz paketa graphics bi bilo:
graphics.Rectangle
Ukljuivanje odre enog lana paketa vri se upotrebom kljune rei import na poetku datoteke, iza ega sledi naziv lana paketa. Ukljuivanje lanova paketa se vri neposredno iza deklaracije paketa trenutne datoteke(ako ga ima) , a u suprotnom se nalazi na prvom mestu u datoteci. Primer ukljuivanja lana paketa:
import graphics.Rectangle;
Ovaj pristup je dobar ako se koristi samo par klasa iz paketa, u suprotnom, ako se koristi vei broj klasa iz paketa, potrebno je ukljuiti ceo paket. Ukljuivanje svih lanova graphics paketa vri se na sledei nain:
import graphics.*;
Posle ovog ukljuivanja mogu je direktan pristup svim lanovima datog paketa:
Circle myCircle = new Circle(); Rectangle myRectangle = new Rectangle();
40
Izuzeci
Tokom izvravanja programa moe doi do neoekivanih situacija koje mogu izazvati prekid izvravanja. Java nudi mehanizam izuzetaka koji omoguava oporavak od greaka tokom izvravanja. Izuzetak moe biti generisan i obra en (eng. try-catch mechanism). Generisanje izuzetka signalizira pojavu neoekivane greke. Obrada izuzetka podrazumeva preduzimanje odre enih koraka koji e dovesti do nastavka rada programa. Izuzetak obra uje kd koji se naziva rukovalac izuzetkom (eng. exception handler). Obrada izuzetka se obino ne vri u okviru istog konteksta u kom je izuzetak generisan. JVM uvek izvrava nekoliko niti: sakuplja otpada, glavnu nit u kojoj se izvrava program, itd. Svaka nit ima svoj stek izvravanja (eng. runtime stack, call stack, invocation stack) koji obra uje izuzetke koji su generisani tokom izvravanja metoda. Element steka se naziva aktivacioni slog (eng. activation record, stack frame) i predstavlja poziv metoda. U sebi sadri podatke o metodi (lokalne promenljive npr.). Prilikom svakog poziva kreira se novi aktivacioni slog i smeta na vrh. Metod iji se aktivacioni slog nalazi na vrhu steka je onaj koji se trenutno izvrava. Kada metod zavri sa izvravanjem, njegov slog bie uklonjen sa steka. Izvravanje e se nastaviti u metodu iji je slog sada na vrhu steka. Metodi koji se nalaze na steku se nazivaju aktivnima, jer njihovo izvravanje nije zavreno. Svojom strukturom stek omoguava praenje toka izvravanja niti. Trag steka (eng. stack trace) predstavlja redosled izvravanja metoda.
public class Izuzetak { public static void pitajKorisnika(String[] args){ System.out.println("Korisnik je rekao " + pisi(args)); System.out.println("Kraj metoda pitajKorisnika"); } static String pisi(String[] args){ String s = args[0]; for(int i = 1; i < args.length; i++){ s = s + "" + args[i]; } return s; } public static void main(String[] args){ pitajKorisnika(args); System.out.println("Kraj main metoda"); } }
Ukoliko pri pokretanju programa Izuzetak ne navedemo ni jedan argument, JVM e prijaviti greku prilikom pokuaja da promenljivoj s u redu 9 dodeli vrednost prvog elementa nepostojeeg niza args. Ako se u tragu steka ne pojavljuju brojevi redova kao to je prikazano dole, potrebno je iskljuiti JIT (eng. Just-in-Time) opciju JVM u Java 2 SDK:
java Djava.compiler=NONE ImePrograma.
41
greku tako to generie izuzetak ArrayIndexOutOfBoundsException. Izvravanje metoda pisi() prekinuto je u redu 9 generisanjem izuzetka i return naredba iz reda 13 nikad nee biti izvrena. Kako metod ne poseduje kd koji bi obradio izuzetak, izvravanje metoda je nepovratno prekinuto, a njegov aktivacioni slog uklonjen sa steka. Izuzetak e biti prosle en sledeem metodu sa steka, u ovom sluaju pitajKorisnika(). Ni ovaj metod nema naina da obradi izuzetak, pa je i on prekinut u redu 4, a njegov aktivacioni slog skinut sa steka. Izuzetak se prosle uje metodi main(). Ni on ne moe da obradi izuzetak, pa e i on biti prekinut u redu 17. Kako izuzetak nije obradio ni jedan od aktivnih metoda, obradie ga osnovni rukovalac izuzecima glavne niti. Osnovni rukovalac izuzecima obino ispie ime izuzetka i objanjenje koje sledi redosled izvravanja aktivnih metoda u trenutku generisanja izuzetka. Neobra eni izuzetak izaziva smrt niti u kojoj se izuzetak desio. Ako se izuzetak desio prilikom izraunavanja levog operatora binarne operacije, desni operand se ne izraunava. Ukoliko se izuzetak desi prilikom izraunavanja jednog od parametara u listi stvarnih parametara, do izraunavanja ostatka liste nee doi.
JVM
prijavljuje
Vrste izuzetaka
Izuzeci su Java objekti. Svi izuzeci nasle uju klasu java.lang.Throwable. Dve najvanije podklase klase Throwable su Exception i Error.
Slika XY
42
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Klasa Throwable sadri promenljivu tipa znakovni niz (String) u koju mogu da piu potklase u cilju detaljnijeg obavetavanja korisnika o prirodi greke koja je dovela do generisanje izuzetka. Sve klase koje nasle uju Throwable definiu konstruktor od kojih je jedan argument znakovni niz u kom se nalazi poruka o greki. Najee koriteni metodi klase Throwable su: String getMessage() koji vraa poruku o greki. void printStackTrace() tampa trag steka na standardni izlaz za greku. Ovaj metod overload-uju jo dva metoda koji za argumente primaju objekte tipa PrintStream ili PrintWriter u zavisnosti od toga gde treba tampati trag steka. String toString() vraa kratak opis izuzetka zajedno sa porukom o greki. Klasom Exception predstavljeni su izuzeci kojih program treba da bude svestan tokom izvravanja. Klasa RuntimeException i sve njene potklase predstavljaju izuzetke koji se generiu tokom izvravanja programa, a usled greki u programiranju. Objekte ove klase ne bi trebalo obra ivati, ve ih treba smatrati grekama u dizajnu programa koje treba otkloniti. Klasa Error signalizira greke pri uvezivanju dokumenta, smrti niti, greke virtuelne maine itd. Izuzeci ove vrste predstavljaju probleme od kojih gotovo da nije mogu oporavak, pa ih ne treba ni obra ivati. Provereni izuzeci (eng. checked exceptions) su svi izuzeci koji ne pripadaju RuntimeException, Error i njihovim potklasama. Prevodilac garantuje da metod koji direktno ili indirektno moe generisati izuzetak, mora eksplicitno ili da ga obradi, ili da ga prosledi dalje. Metodi ne moraju obraati panju na neproverene izuzetke (eng. unchecked exceptions), jer od njih ili nije mogue oporaviti se (Error) ili su u pitanju greke u dizajnu (RuntimeException). Novi izuzeci se definiu u cilju bolje kontrole greaka. Obino nasle uju neki od proverenih izuzetaka.
package JavaVezbe2008; public class JaveVezbeException extends Exception{ public JaveVezbeException (String s){ super(s); } }
Obrada izuzetaka
Izuzeci se obra uju pomou konsrukcije try-catch-finally. Izuzeci koje generie kd koji se nalazi unutar bloka try mogu biti obra eni u odgovarajuem bloku catch. Blok finally se izvrava uvek, bez obzira na to da li je do izuzetka dolo. Konstrukcija try-catch-finally zahteva upotrebu blokova, kako je pokazano u donjem primeru. Svaki blok try zahteva nula ili vie blokova tipa catch i nula ili jedan blok finally u navedenom redosledu. Blok try blok mora slediti bar jedan blok tipa catch ili finally. Blok catch u svom zaglavlju uvek sadri tano jedan argument onog tipa izuzetka (mora nasle ivati klasu Throwable ili jednu od njenih potklasa) koji blok
43
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave treba da obradi. Svaki od blokova moe sadrati bilo kakve naredbe, to znai da je mogue ugnedavanje konstrukcija try-catch-finally.
import java.io.*; public class TryCatchFinally { static int podeli(String s1, String s2){ int i1, i2; try{ i1 = Integer.parseInt(s1); i2 = Integer.parseInt(s2); return deli(i1, i2); }catch(NumberFormatException nfe){ nfe.printStackTrace(); System.out.println("Izuzetak obradjen u podeli() metodu"); return 0; } } static int deli (int a, int b){ return a / b; } public static void main(String[] args) { try{ BufferedReader ulaz = new BufferedReader (new InputStreamReader (System.in)); System.out.println("Unesite delilac"); String s = ulaz.readLine(); int i = podeli(args[0], s); System.out.println("Rezultat deljenja je " + i); }catch (IOException ioe){ ioe.printStackTrace(); System.out.println("Izuzetak obradjen u main() metodu"); }catch (ArrayIndexOutOfBoundsException ie){ ie.printStackTrace(); System.out.println("Izuzetak obradjen u main() metodu"); }finally{ System.out.println("finally izvrsen u main() metodu"); } System.out.println("kraj main() metoda"); } }
Prilikom rada sa izuzecima moe se desiti jedna od sledeih situacija: Metoda u kom se izuzetak generie nema blok try-catch-finally izuzetak e biti prosle en sledeoj metodi sa steka, izvravanje tekue metode prekinuto i njen aktivacioni slog bie uklonjen sa steka.
44
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Metoda u kom se izuzetak generie ima blok try-catch-finally i ima blok koji moe da obradi dati izuzetak izuzetak e biti obra en izvravanjem bloka catch, zatim e biti izvren blok finally (ukoliko postoji), nakon ega se izvravanje nastavlja normalno. Uvek se izvrava samo blok catch sa ijim je argumentom dati izuzetak kompatibilan. Moe se izvriti najvie jedan blok catch; svi drugi blokovi catch se preskau. Metoda u kom se izuzetak generie ima blok try-catch-finally ali nema blok catch koji moe da obradi dati izuzetak bie izvren blok finally (ukoliko postoji), metoda e biti prekinuta, a izuzetak e biti prosle en sledeoj metodi na steku.
catch
Ukoliko se program pokrene uz navo enje 56 u komandnoj liniji i ako se unese 2 kao delilac, program e se izvriti bez prekida koje izaziva generisanje izuzetaka i ispisati:
Unesite delilac 2 Rezultat deljenja je 28 finally izvrsen u main() metodu kraj main() metoda
Kako je blok try izvren bez prekida, svi blokovi catch bie preskoeni. Izvrie se blok finally i preostali kd metode. Ukoliko prilikom izvravanja do e do pojave izuzetka i ako taj izuzetak moe biti obra en, program nastavlja sa normalnim radom. Pretpostavimo da u komandnoj liniji nije uneen znakovni niz. Prilikom pristupanja elementu niza koji ne postoji u redu 28 bie generisan izuzetak:
Unesite delilac 9 java.lang.ArrayIndexOutOfBoundsException: 0 at TryCatchFinally.main(TryCatchFinally.java:28) Izuzetak obradjen u main() metodu finally izvrsen u main() metodu kraj main() metoda
Red 29 se nee izvriti. Poto blok catch sadri rukovalac za izuzetak tipa ArrayIndexOutOfBoundsException, izuzetak e biti obra en, kao to se vidi i iz rezultata izvravanja programa Izuzetak obradjen u main() metodu. Nakon izvravanja odgovarajueg bloka catch, bie izvren i blok finally. Poto je izuzetak obra en, program e nastaviti sa normalnim izvravanjem i izvrie naredbu u redu 38. Ukoliko se za delilac prosledi 0, doi e do deljenja sa 0 u metodi deli(). Metoda deli() nema blok try-catch-finally, pa e izuzetak ArithmeticException biti prosle en sledeoj metodi na steku, u ovom sluaju metodi podeli(). Metoda podeli() ima blok try-catch-finally, ali ne poseduje blok catch sa ijim argumentom je izuzetak komaptibilan. Metoda podeli() e prekinuti izvravanje, a kako nema blok finally, samo e proslediti izuzetak sledeoj metodi sa steka izvravanja. Ni metoda main() nema odgovarajui blok catch, tako da e se izvriti samo blok finally, nakon ega e izvravanje metode biti prekinuto. Ova metoda je bila poslednja na steku izvravanja, pa e obrada izuzetka biti predata osnovnom rukovaocu izuzecima glavne niti. 45
Oblast vanosti argumenta bloka catch je sam blok. Da bi izuzetak bio obra en, tip izuzetka mora biti kompatibilan sa tipom argumenta bloka catch. Ukoliko postoji blok catch iji je argument je tipa superklase neke od klasa argumenata u blokovima catch koji se hronoloki nalaze iza njega, prevodilac e prijaviti greku, jer se blok catch potklase nikada nee izvriti.
} catch(Exception e){ ... } catch(IOException ioe){ // Kompajler e se poaliti da je ovaj deo koda nedostupan ... }
46
Osnovni U/I
U/I tokovi predstavljaju razliite vrste dolaznih ili odlaznih izvora podataka kao to su datoteke na disku, spoljni ure aji, drugi programi i memorijski blokovi. Tokovi podravaju razliite vrste podataka ukljiujui bajtove, osnovne tipove, lokalizovane karaktere i objekte. Bez obzira na vrstu, svi tokovi predstavljaju jednostavan model ka programu koji ih koristi. Tok je niz podataka. Program koristi ulazne tokove da bi itao podatke sa izvora:
itanje podataka sa ulaznog toka Koristei izlazne tokove program moe da pie podatke na izlaz:
Pisanje podataka na izlazni tok Java U/I paket sadri mnoge klase koje se koriste za itanje i pisanje podataka. Veina realizovanih klasa su sekvencijalni tokovi. Sekvencijalni tokovi se mogu podeliti u dve grupe: prva koja ita i pie binarne podatke i drugu koja koja ita i pie Unicode znakove. Svaka od realizovanih klasa ima svoje specijalnosti kao to su itanje iz ili pisanje u datoteku, filtriranje podataka koji se itaju ili piu ili serijalizacija objekata. Tokovi se mogu podeliti u nekoliko grupa:
47
Tokovi bajtova koji rukuju binarnim podacima. Tokovi karaktera rukuju karakterim, automatski vrei prilago avanje lokalnim podeavanjima. Tokovi blokova optimizuju ulaz i izlaz smanjujui broj poziva osnovnih operacija. Pretraga i formatiranje omoguuju programu da ita i pie formatizovani tekst. U/I sa komandne linije opisuje standardne tokove i konzolni objekat. Tokovi tipova podataka(eng. Data Streams) obra uju binarni U/I osnovnih tipova podataka i znakovnih nizova (eng. Strings). Tokovi objekata slue za binarni U/I objekata.
Tokovi bajtova
Programi koriste tokove bajtova za ulaz i izlaz 8-bitnih podataka (bajtova). Postoje razliite vrste tokova bajtova ije je koritenje veoma slino a najvie se razlikuju u nainu na koji se kreiraju. Svi oni nasle uju osnovne klase InputStream i OutputStream. Primer koji sledi koristi datoteke kao U/I tokove pomou klasa FileInputStream i FileOutputStream. Program CopyBytes ita bajt po bajt iz ulaznog toka i kopira ga na izlaz u okviru petlje while. Iako je u pitanju bajt, U/I funkcija read smeta rezultat u promenljivu tipa int. Ovo omoguuje vraanje vrednosti (-1) kao znaka da je ulazni tok doao do kraja datoteke. Vrednost promenljive se uva u
donjih import import import 8 bita. java.io.FileInputStream; java.io.FileOutputStream; java.io.IOException;
public class CopyBytes { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("xanadu.txt"); out = new FileOutputStream("outagain.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } }
48
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Veoma je vano zatvoriti tokove nakon prestanka njihovog korienja . Korienje finally bloka garantuje da e se tokovi zatvoriti ak i u sluaju greke. Jedna od moguih greaka je neuspeno otvaranje jednog od tokova pri emu bi vrednost promenljive povezane sa njime ostala null. Iz tog razloga se pre zatvaranja toka vri provera vrednosti promenjivih in i out. Tokovi bajtova se koriste samo za najosnovniji U/I i svi ostali tipovi tokova su zasnovani na njemu.
Tokovi znakova
Java platforma za zapis karaktera koristi Unicode standard. Tokovi karaktera automatski prevode unutranji format na lokalni set olakavajui programeru rukovanje. U veini aplikacija U/I sa tokovima karaktera nije mnogo komplikovaniji od rada sa tokovima bajtova. Svi tokovi karaktera nasle uju klase Reader i Writer. Kao primer korienja bie prokazani tokovi karaktera koji rade sa datotekama FileReader i FileWriter.
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyCharacters { public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader("xanadu.txt"); outputStream = new FileWriter("characteroutput.txt"); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } }
Najvea razlika u odnosu na prethodni primer jeste korienje FileReader i FileWriter za ulaz i izlaz umesto FileInputStream and FileOutputStream.. Kao i CopyBytes, CopyCharacters koristi promenljivu tipa int za itanje i pisanje u tokove. Za razliku od CopyBytes u ovom primeru se vrednost promenljive uva u donjih 16 bita. Tokovi karaktera uglavnom koriste tokove bajtova za osnovne U/I operacije, dok sami vre konverziju karaktera. FileReader recimo koristi FileInputStream dok FileWriter koristi FileOutputStream. 49
public class CopyLines { public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader("xanadu.txt")); outputStream = new PrintWriter(new FileWriter("characteroutput.txt")); String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } }
Funkcija readLine ita liniju teksta koja se potom ispisuje na izlaz pomou metode println koja na kraj niza znakova dodaje terminator linije koji odgovara korienom operativnom sistemu i koji ne mora biti isti kao i terminator iz ulazne datoteke.
50
Baferovani tokovi
U prethodnim primerima koriten je uglavnom nebaferovani U/I. To znai da svaka operacija itanja i pisanja se direktno obraa operativnom sistemu. Ovo dosta usporava program jer svaki takav zahtev pokree pristup disku, mrenim resursima i sline zahtevne operacije. Da bi se to izbeglo Java platforma prua podrku za baferovani U/I. Ulazni tokovi blokova itaju podatke iz memorijskih blokova (bafera), a operativni sistem se poziva samo kada je taj blok prazan. Slino tome, izlazni baferovani tokovi piu u memorijski blok, a operativni sistem se poziva samo kada je blok pun. Program moe da pretvori nebaferovani tok u baferovani tako to e proslediti nebaferovani tok kao argument konstruktoru klase baferovanog toka. Primer CopyCharacters se moe na sledei nain modifikovati da bi koristio baferovani tok:
inputStream = new BufferedReader(new FileReader("xanadu.txt"));
Klase baferovanih tokova koje se koriste za gne enje nebaferovanih tokova su BufferedInputStream i BufferedOutputStream za binarne tokove, dok se za znakovne tokove koriste BufferedReader i BufferedWriter. U odre enim situacijama ima smisla vriti ispis iz bafera u kritinim trenucima bez ekanja da bude pun. Ovaj postupak se naziva pranjenje bafera. Neke klase tokova blokova podravaju automatsko pranjenje, koje se aktivira argumentima konstruktora. U tom sluaju odgovarajui doga aji e pokrenuti pranjenje bafera. Pranjenje se tako e moe pokrenuti i pozivom metode flush. Ovu metodu imaju svi tokovi ali ona ima efekta samo kod tokova blokova.
51
Tokovi tipova podataka podravaju binarni U/I osnovnih tipova podataka (boolean, char, byte, short, int, long, float, i double) kao i promenljivih tipa znakovni niz. Sve klase tokova tipova podataka realizuju ili DataInput ili DataOutput ablone. U primeru koji sledi(DataStreams) bie prikazane najee koritene implementacije, DataInputStream i DataOutputStream. Potrebno je znati da tokovi tipova podataka umesto negativne povratne vrednosti kao signal za zavretak datoteke generie izuzetak EOFException. Tako e i sve realizacije DataInput metoda koriste umesto EOFException povratnih vrednosti.
Tokovi objekata
Tokovi objekata pruaju podrku za U/I objekata. Veina standardnih klasa podrava serijalizaciju svojih objekata implementirajui ablon Serializable. Klase tokova objekata su ObjectInputStream i ObjectOutputStream koje realizuju ObjectInput i ObjectOutput, naslednike DataInput and DataOutput.To znai da su sve osnovne metode iz tokova podataka raspoloive i za tokove objekata, stoga tok objekata moe biti meavina osnovnih tipova i objekata. Ovo je prikazano u primeru ObjectStreams. Ukoliko readObject() ne vrati objekat oekivanog tipa, pokuaj njegovog pretvaranja moe dovesti do generisanja ClassNotFoundException izuzetka. U ovom primeru to se ne moe dogoditi, tako da se ne pokuava njegovo hvatanje i obrada. Umesto toga, dodajui ClassNotFoundException u list throws metode main, dajemo do znanja prevodiocu da smo svesni te mogunosti. Metode writeObject i readObject su jednostavne za koritenje, ali one sadre sofisticirane mogunosti koritenja objekata. U klasi Calendar koja sadri samo osnovne tipove, ovo nema naroitog znaaja, ali ima kod sloenijih objekata koji mogu sadrati reference na druge objekte u sebi. Ukoliko sa readObject itamo objekat iz toka moramo biti u mogunosti da proitamo ispravno i sve objekte koje je originalni objekat referencirao. Referenciran objekti tako e mogu sadrati reference na druge objekte. U ovakvoj situaciji writeObject zapisuje itavu mreu objekata u tok. To je prikazano na sledeoj slici, gde je writeObject pozvana za upisivanje objekta a. Ovaj objekat sadri reference na objekte b i c, pri emu b sadri reference na objekte d i e. writeObject ne zapisuje samo a ve i sve ostale objekte neophodne za njegovu potpunu rekonstrukciju. Kada se a proita pomou readObject i ostala etiri objekta se tako e itaju iz toka.
52
53
Definisanje niti Prilikom definisanja niti, neophodno je odrediti koji kod e se izvravati u datoj niti. Dva naina za to su: 1. Obezbedite objekat koji realizuje suelje Runnable. public class HelloRunnable implements Runnable {
public void run() { System.out.println("Zdravo! Javljam se iz niti!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
2. Nasledite klasu Thread. Ova klasa ve realizuje suelje Runnable, ali njena metoda run ne radi nita.
public class HelloThread extends Thread { public void run() { System.out.println("Zdravo! Javljam se iz niti!"); } public static void main(String args[]) { (new HelloThread()).start(); } }
54
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Prekidi Prekid je indikacija kojom se odre enoj niti kae da bi trebala da prekine sa izvravanjem trenutnog toka programa, te da pone da radi neto drugo. Da bi ovaj mehanizam radio pravilno, nit kojoj se upuuju prekidi mora da podrava ove prekide.
for (int i = 0; i < importantInfo.length; i++) { //Pause for 4 seconds try { Thread.sleep(4000); } catch (InterruptedException e) { //We've been interrupted: no more messages. return; } //Print a message System.out.println(importantInfo[i]); }
ta treba raditi ukoliko nit troi puno vremena na pozive koji ne generiu izuzetke tipa InterruptedException? U tom sluaju, neophodno je periodino pozivati funkciju Thread.interrupted, koja moe da kae da li je u me uvremenu neko poslao prekid datoj niti.
for (int i = 0; i < inputs.length; i++) { heavyCrunch(inputs[i]); if (Thread.interrupted()) { //We've been interrupted: no more crunching. return; } }
Kako proveriti da li je dolo do prekida? Za ovu svrhu, postoji statusna zastavica(eng. flag) koja nosi ovu informaciju. Funkcije koje je koriste su: - Thread.interrupt (postavlja zastavicu) - Thread.interrupted (statika funkcija koja vraa informaciju i brie vrednost) - Thread.isInterrupted (nestatika funkcija koja vraa informaciju ali ne brie vrednost) - Sve funkcije koja bacaju izuzetak tipa InterruptedException (one istovremeno i briu datu vrednost) Spojevi Spojevi su naini kojima se izvavanje jedne niti odlae do zavretka druge niti. Ukoliko jedna nit eli da odloi nastavak svog rada dok se druga nit t ne zavri, treba koristiti sledei poziv:
t.join();
55
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Imajte na umu da i ova funkcija generie izuzetke tipa InterruptedException. Primer: SimpleThreads Glavna nit kreira novu nit MessageLoop, i eka da se ova zavri. Ako je vreme potrebno da se nova nit zavri suvie dugo, glavna nit je prekida. to se tie niti MessageLoop, ona ispisuje niz poruka. Ako je neko prekine pre nego to je ispisala sve poruke, ona ispisuje poruku kojom to obelodanjuje i gasi se.
//SimpleThreads.java public class SimpleThreads { //Display a message, preceded by the name of the current thread static void threadMessage(String message) { String threadName = Thread.currentThread().getName(); System.out.format("%s: %s%n", threadName, message); } private static class MessageLoop implements Runnable { public void run() { String importantInfo[] = { "Anders Niklas Andersson - Lead vocals & guitars", "Jens Robert Dahlqvist - Guitars", "Kenny Hakansson - Bass", "Matz Robert Eriksson - Drums & vocals", "Anders Lindstrom - Piano" }; try { for (int i = 0; i < importantInfo.length; i++) { //Pause for 4 seconds Thread.sleep(4000); //Print a message threadMessage(importantInfo[i]); } } catch (InterruptedException e) { threadMessage("I wasn't done!"); } } } public static void main(String args[]) throws InterruptedException { //Delay, in milliseconds before we interrupt MessageLoop //thread (default one hour). long patience = 1000 * 60 * 60; //If command line argument present, gives patience in seconds. if (args.length > 0) { try { patience = Long.parseLong(args[0]) * 1000; } catch (NumberFormatException e) { System.err.println("Argument must be an integer."); System.exit(1); } } threadMessage("Starting MessageLoop thread"); long startTime = System.currentTimeMillis(); Thread t = new Thread(new MessageLoop()); t.start(); threadMessage("Waiting for MessageLoop thread to finish"); //loop until MessageLoop thread exits while (t.isAlive()) { threadMessage("Still waiting..."); //Wait maximum of 1 second for MessageLoop thread to //finish. t.join(1000); if (((System.currentTimeMillis() - startTime) > patience) &&
56
Sinhronizacija Kompleksnije aplikacije najee imaju vie niti, od kojih svaka ima svoj zadatak (npr. rad sa datotekama, prihvatanje unosa, ispisivanje rezultata, itd).
Komunikacija me u ovim blokovima se najee vri preko deljenih resursa, i neophodno je da se usaglasi pristup nad tim deljenim resursima. Klasian primer kod kojeg nam treba sinhronizacija je broja.
class Counter { private int c = 0; public void increment() { c++; } public void decrement() { c--; } public int value() { return c; } }
Jedno mogue reenje je pravljenje sinhronizovanih metoda. To se radi dodavanjem kljune rei synchronized.
public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--;
57
Konstruktori ne mogu da imaju atribut synchronized. Ugra eni mehanizam za blokiranje Svaki objekat ima ugra en mehanizam za blokiranje. Celokupan sistem sinhronizacije je naslonjen na ovaj mehanizam. Pored sinhronizovanih metoda, imamo i sinhronizovane naredbe. Kod njih je neophodno odrediti nad kojim objektom e se vriti blokada, odnosno iji mehanizam za blokiranje e biti korien.
public void addName(String name) { synchronized(this) { lastName = name; nameCount++; } nameList.add(name); }
Atomski pristup i atomske promenjive Atomske akcije su one akcije kod kojih se promena deava odjednom. Sledee akcije se nazivaju atomskim: - akcije itanja i pisanja promenjivih koje sadre referencu, kao i akcije itanja i pisanja nad primitivnim promenjivama (svi tipovi izuzev long i double) - akcije itanja i pisanja za sve promenjive koje su deklarisane sa volatile.
58
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave to se tie atomskih promenjivih, njih koristimo kad hoemo da imamo sinhronizovan pristup promenjivim, bez potrebe da metodi u kojoj se pristupa toj promenjivoj dodelimo atribut synchronized. Kako se to realizuje na primeru ve pomenutog brojaa? Jednostavnom zamenom tipa promenjive c: iz int u AtomicInteger.
import java.util.concurrent.atomic.AtomicInteger; class AtomicCounter { private AtomicInteger c = new AtomicInteger(0); public void increment() { c.incrementAndGet(); } public void decrement() { c.decrementAndGet(); } public int value() { return c.get(); } }
Problemi koji se javljaju kod sinhronizacije: Mrtva petlja (sluaj kad su dve niti zablokirane zauvek) Izgladnjivanje (sluaj kada jedna nit dui period nije u mogunosti da pristupi deljenim resursima zbog pohlepe druge niti koja pristupa istim resursima) iva petlja (sluaj kada dve niti blokiraju jedna drugu jer akcija jedne niti prouzrokuje akciju druge niti, i obrnuto)
uvane oblasti Upotreba while petlje u jednoj niti u cilju da registrujemo da je dolo do promene neke vrednosti od strane druge niti ili procesa, krajnje je nepoeljna. Takav primer je:
public void guardedJoy() { //Simple loop guard. Wastes processor time. Don't do this! while(!joy) {} System.out.println("Joy has been achieved!"); }
Mnogo efikasniji nain je da se kae niti da se zaustavi dok ne do e do odre ene promene, odnosno do trenutka kad je neka druga nit obavesti da je dolo do promene. Ovo se postie primenom funkcije wait.
public synchronized guardedJoy() { //This guard only loops once for each special event, which may not //be the event we're waiting for. while(!joy) { try {
59
Neka druga nit je u ovom sluaju zaduena da obavesti o promeni nakon to promeni vrednost joy. To se ini funkcijom notifyAll().
public synchronized notifyJoy() { joy = true; notifyAll(); }
Postoji mogunosti i korienja funkcije notify, ali se ona primenjuje kada je potrebno obavestiti samo jednu nit o promeni, i kad nije bitno koja je to nit me u onima koje ekaju. ivotni ciklus jedne niti
60
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Primer: Producer-Consumer U ovom primeru, podaci se dele izme u dve niti: jedna je proizvo aka (Producer) koja generie podatke, dok je druga potroaka (Consumer), koja ih upotrebljava. Ove dve niti komuniciraju jedna sa drugom preko deljenog objekta (Drop). Koordinacija je kljuna: potroa ne sme da pokua da preuzme podatke pre nego to ih proizvo a isporui, dok proizvo a ne sme da pokua da isporui nove podake dok potroa nije preuzeo prethodne.
//Drop.java public class Drop { //Message sent from producer to consumer. private String message; //True if consumer should wait for producer to send message, false //if producer should wait for consumer to retrieve message. private boolean empty = true; public synchronized String take() { //Wait until message is available. while (empty) { try { wait(); } catch (InterruptedException e) {} } //Toggle status. empty = true; //Notify producer that status has changed. notifyAll(); return message; } public synchronized void put(String message) { //Wait until message has been retrieved. while (!empty) { try { wait(); } catch (InterruptedException e) {} } //Toggle status. empty = false; //Store message. this.message = message; //Notify consumer that status has changed. notifyAll(); } }
//Producer.java import java.util.Random; public class Producer implements Runnable { private Drop drop; public Producer(Drop drop) { this.drop = drop; } public void run() { String importantInfo[] = { "Anders Niklas Andersson - Lead vocals & guitars", "Jens Robert Dahlqvist - Guitars", "Kenny Hakansson - Bass",
61
//Consumer.java import java.util.Random; public class Consumer implements Runnable { private Drop drop; public Consumer(Drop drop) { this.drop = drop; } public void run() { Random random = new Random(); for (String message = drop.take(); ! message.equals("DONE"); message = drop.take()) { System.out.format("MESSAGE RECEIVED: %s%n", message); try { Thread.sleep(random.nextInt(5000)); } catch (InterruptedException e) {} } } }
//ProducerConsumerExample.java public class ProducerConsumerExample { public static void main(String[] args) { Drop drop = new Drop(); (new Thread(new Producer(drop))).start(); (new Thread(new Consumer(drop))).start(); } }
Vremenske kontrole Da biste uspeno koristili vremensku kontrolu, potrebno je da sledite sledee korake: 1. Realizujte potklasu od klase TimerTask. Njena metoda run e sadrati kod koji e se izvriti nakon isteka definisanog vremena. 2. Napravite jednu vremensku kontrolu, odn. klasu Timer. 3. Napravite jedan objekat tipa TimerTask. 4. Podesite kontrolu na vreme koje je potrebno da istekne pre nego to se izvri zadati kod.
62
Vremensku kontrolu prekidamo putem funkcije cancel. U ovom primeru se prilikom podeavanja vremena isteka kontrole, koristi broj milisekundi koje treba da isteknu pre nego to se aktivira zadati kod. Mogue je tako e koristiti i tano vreme kada neto treba da se dogodi.
//Get the Date corresponding to 11:01:00 pm today. Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 1); calendar.set(Calendar.SECOND, 0); Date time = calendar.getTime(); timer = new Timer(); timer.schedule(new RemindTask(), time);
Ukoliko je potrebno da se data vremenska kontrola periodino poziva, na raspolaganju stoje razliite realizacije funkcije schedule.
schedule(TimerTask task, long schedule(TimerTask task, Date scheduleAtFixedRate(TimerTask scheduleAtFixedRate(TimerTask delay, long period) time, long period) task, long delay, long period) task, Date firstTime, long period)
// AnnoyingBeep.java import java.util.Timer; import java.util.TimerTask; import java.awt.Toolkit; /** * Schedule a task that executes once every second. */
63
Java Native
Ukoliko postoji potreba da vaa aplikacija koristi funkcionalnost u nekoj biblioteci na samom OS, to je mogue uraditi sledeim postupkom. Postoji est koraka u tom postupku, i prikazaemo ih na primeru gde se trai od sistemske biblioteke trai da ispie "Hello world!". Korak 1: Napiite kod za Javu.
//HelloWorld.java class HelloWorld { public native void displayHelloWorld(); static { System.loadLibrary("hello"); } public static void main(String[] args) { new HelloWorld().displayHelloWorld(); } }
64
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Korak 3: Kreirajte zaglavlje (.h).
javah -jni HelloWorld
Ime funkcije koju treba realizovati u izvornom okruenju se automatski kreira po sledeem ablonu:
Korak 5: Kreirajte deljenu biblioteku. U terminologiji Windows operativnih sistema, pod deljenim bibliotekama se smatraju dinamike (DLL) biblioteke. Korak 6: Pokrenite program
java HelloWorld
65
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Grafiki prikaz ovih koraka je:
66
Java GUI
JFC (Java Foundation Classes)
Za izradu Java aplikacija sa grafikim korisnikom spregom koriste se JFC klase. U ovu grupu spadaju klase iz sledeih biblioteka: AWT (Abstract Window Toolkit), Swing i Java2D. Klase iz ovih paketa se koriste za razvoj grafikih aplikacija u Javi bez obzira na operativni sistem na kojem se izvrava JVM (Java Virtual Machine), Windows, Linux ili Mac OS X.
AWT
Klase iz ove biblioteke su prve koriene za izradu korisnikih sprega od nastanka Java programskog jezika. Programeru daju na raspolaganje standardne grafike kontrole poput dugmeta (Button), klizaa (Slider), itd. AWT obezbe uje reakciju na poruke i doga aje od strane korisnika (GUI Event Subsystem). AWT biblioteka za izradu korisnike sprege koristi resurse operativnog sistema ispod JVM. Tako, na primer, za iscrtavanje dugmeta i implementaciju reakcije na doga aje, koriste se rutine operativnog sistema. To je jedna od veih mana ovih klasa, jer jedna aplikacija ne izgleda potpuno isto na svim platformama, tj. operativnim sistemima. Osnova svake GUI aplikacije je okvir (Frame), koji ima svoj naslov i podrava osnovne operacije za uveavanje, smanjivanje i zatvaranje prozora. U okvir se dodaje panel (Panel) u koji e dalje da se dodaju sve ostale komponente, kao to su dugme, labela, Potrebno je, tako e, registrovati svaku AWT aplikaciju za doga aje koji stiu sa tastature, to je objanjeno u sledeem primeru. Sledi jednostavan primer kreiranja prozora pomou klasa iz AWT biblioteke, sa komentarima. Datoteka: AWTExample.java
import java.awt.*; import java.awt.event.*; /** * Ova klasa predstavlja jednostavan primer kreiranja AWT okvira koji * dugme i labelu koja ispisuje koliko puta je pritisnuto dugme. */ public class AWTExample{ private Button button private Frame frame = private Label label = private static String private int numClicks = new Button("Dugme"); new Frame("Jednostavan AWT primer"); new Label(labelPrefix + "0 "); labelPrefix = "Broj pritisaka na dugme: "; = 0;
sadrzi jedno
/** * Javni konstruktor bez argumenata */ public AWTExample() { // podesavanje velicine prozora
67
Zadatak 1: a) Prouiti izvorni kod i pokrenuti aplikaciju. b) Dodati jo jedno dugme sa nazivom Odustani, na iji klik e da se zatvori prozor.
Napomena: metoda koja zatvara okvir (Frame) je dispose(). Vreme za izradu zadatka je 15 min.
SWING
Swing biblioteka klasa, tako e, daje na raspolaganje veliki broj klasa za rad sa GUI aplikacijama u Javi. Koristi komponente, kao i AWT, dugme, klizae itd. Razlika u odnosu na AWT biblioteku, je u tome to Swing aplikacija izgleda isto na svim platformama, za razliku od AWT-a, jer ne mapira direktno funkcije operativnog sistema za rad sa grafikom (Rutine u Swing klasama same rade obradu slike, ne oslanjaju se u velikoj meri na rutine operativnog sistema). Glavni nedostatak Swing biblioteke je manja brzina izvravanja programa u odnosu na programe pisane pomou AWT biblioteke. Swing klase su proirenje AWT klasa, tj. njihove funkcionalnosti, i sve to pruaju AWT klase, imaju i Swing klase. Komponente Swing klasa se mogu prepoznati po slovu J ispred definicije komponente. Na primer, klasi Button iz AWT biblioteke odgovara klasa JButton u Swing biblioteci. Jedna od dobrih mogunosti prilikom kreiranja GUI aplikacija je postojanje predefinisanih izgleda, odnosno, rasporeda komponenti u prozoru, Layout. Za korienje 68
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave layout-a zaduene su LayoutManager klase, i one sve implementiraju interfejs LayoutManager. Mogu se nai u java.awt paketu klasa (GridLayout, FlowLayout, ). U narednom primeru se koristi FlowLayout, koji sve komponente koje se dodaju na panel postavlja sekvencijalno. Sledi primer jednostavne Swing aplikacije sa korisnim komentarima: Datoteka: SwingExample.java
import javax.swing.*; import java.awt.*; import java.awt.event.*; /** * * Ova klasa predstavlja jednostavan primer kreiranja Swing okvira koji sadrzi jedno * dugme i labelu koja ispisuje koliko puta je pritisnuto dugme. */ public class SwingExample implements ActionListener{ private private private private private String labelPrefix = "Broj pritisaka na dugme: "; int numClicks = 0; JLabel label = new JLabel(labelPrefix + "0 "); JButton button = new JButton("Ja sam Swing dugme"); JFrame frame = new JFrame("Swing primer");
/** * Javni konstruktor bez argumenata */ public SwingExample(){ /** * definise sta da radi prozor kada se inicira * komanda za zatvaranje prozora. */ frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE); /** * dugmetu dodeljuje odgovarajuci taster, koji u kombinaciji * sa "Alt" tasterom obavlja istu funkciju kao i pritisak misa * na dugme. U ovom slucaju je to taster "i". */ button.setMnemonic(KeyEvent.VK_I); /** * posto ova klasa implementira interfejs ActionListener * dugmetu se moze dodeliti "this" objekat koji osluskuje * spoljne dogadjaje */ button.addActionListener(this); /** * kreira panel na koji se dodaju komponente * koristi se FlowLayout */ JPanel pane = new JPanel(new FlowLayout()); pane.add(button); pane.add(label); //dodaje panel na frame frame.getContentPane().add(pane); //Prikazuje prozor frame.pack(); frame.setVisible(true); } //implementacija metode za obradu dogadjaja public void actionPerformed(ActionEvent e) { numClicks++; label.setText(labelPrefix + numClicks);
69
Zadatak 2: a) Prouiti izvorni kod i pokrenuti aplikaciju. b) Dodati proizvoljan broj komponenata na panel i promeniti layout.
Napomena: Vreme za izradu zadatka je 15 min.
2D grafika
Klasa Graphics se koristi za iscrtavanje najjednostavnijih grafikih elemenata, poput linije, etvorougla, kruga, ispisivanje karaktera itd. Svi ti elementi se crtaju na praznoj povrini koju definie klasa Canvas. Aplikacija mora da nasledi klasu Canvas kako bi imala osnovne funkcionalnosti i bila u stanju da dodaje razne grafike elemente na povrinu. Metoda paint iz klase Canvas se mora redefinisati, i u njoj se vre sva crtanja. Objekat klase Graphics se prosle uje metodi paint koja je tada u stanju da postavlja grafike elemente na povrinu za crtanje. Sledi jednostavan primer. Datoteka: GraphicsExample.java
import java.awt.*; import java.awt.event.*; /** * Jednostavan program koji demonstrira osnovne mehanizme * za koriscenje 2D grafike u Javi pomocu AWT biblioteke klasa. */ public class GraphicsExample { public static void main(String[] args) { Frame f = new Frame("2D Grafika"); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); f.add(new SmileyCanvas(Color.red, Color.orange), BorderLayout.CENTER); f.pack(); f.setVisible(true); } } /** * Canvas je klasa koja se koristi za iscrtavanja jednostavnih * grafickih elemenata. U ovom primeru, nasa klasa SmileyCanvas * nasledjuje klasu Canvas. Metoda "paint" se redefinise i poziva * se kada se vrsi iscrtavanje komponenata na ekran. */ class SmileyCanvas extends Canvas {
70
Zadatak 3: a) Prouiti izvorni kod i pokrenuti aplikaciju. b) Staviti smiley-a u pravougaoni okvir plave boje.
Napomena: Vreme za izradu zadatka je 15 min.
71
Izvor podataka (DataSource) predstavlja apstrakciju audio video podataka slino video kaseti, dok aparat za reprodukciju (Player) omoguava njihovo procesiranje i kontrolu. Za akviziciju i prezentovanje audio i video podataka potrebni su odre eni ure aji, kao to su mikorfoni, kamere ili zvunici. Izvor podataka (DataSource) i reprodukcija (Player) su integralni delovi JMF aplikativne sprege i oni predstavlaju vei nivo apstrakcije. Pored ovog nivoa, JMF prua aplikativnu spregu nie apstrakcije, tako da korisnik ima mogunost da implementira razna procesiranja nad ulaznim podacima. Slika 2 daje globalni prikaz JMF aplikativne sprege.
72
Vremenski Model
JMF meri vreme u nano sekundama. Odre eni momenat u vremenu se prestavlja preko Time objekta. Klase koje podravaju JMF vremeski model implementiraju Clock interfejs. Ovaj interfejs definie sve operacije vezane za vreme i sinhronizaciju koje su potrebne za kontrolu audio i video podataka.
Clock koristi TimeBase kako bi imao uvid i kontrolisao vreme reprodukcije audio i video podataka. TimeBase predstavlja sistemsko vreme. Kako bi kontrolisao vreme, Clock korisiti: TimeBaseStartTime - sistemsko vreme (dobijeno od TimeBase) u trenutku kada prezentacija poinje, MediaStartTime - pozicija u samom toku podataka odakle poinje prezentacija Rate odnos Clock vremena i sistemskog vremena.
73
JMF Menaderi
Kako bi se uinilo lakim implementiranje aplikacija, JMF koristi vei nivo apstrakcije uvo enjem pojma menadera. JMF koristi etiri vrste menadera: Manager koristi se za stvaranje osnovnih JMF objekata Players, Processors, DataSources, and DataSinks. Stvaranje ovih objekata se radi uvek na isti nain, bez obzira da li se stvaraju JMF predefinisani objekti ili objekti implemenirani od strane korisnika. PackageManager sadri infomacije o raznim JMF paketima. CaptureDeviceManager prua informacije o dostupnim ure ajima za akviziciju (CaptureDevices) PlugInManager zaduen i prua infomacije o raznim dodatnim (plug-in) komponentama za procesiranje kao to su Multiplexers, Demultiplexers, Codecs, Effects, and Renderers.
Svaki JMF program koristi metodu create(), objekta Menager, da formira Player, Processor, DataSources i DataSinks. Ukoliko se podaci dobijaju akvizicijom sa nekog od spoljnih ure aja (kamera, mikrofon) koristi se objekat CaptureDeviceManager, kako bi se saznalo koji su sve ure aji na raspolaganju. Ukoliko se eli kontrola nad samom obradom ulaznih podataka koristi se objekat PlugInManager, kako bi se dobila informacija koje su sve komponente za procesiranje (plugins) registrovane. Ukoliko korisnik eli da registruje svoje komponente koristi se PackageManager.
74
Model podataka
DataSource objekat enkapsulira lokaciju podataka kao i programsku podrku (protokol) koja se koristi za dobijanje podataka. DataSource objekat se formira ili na osnovu JMF MediaLocator ili na osnovu URL (universal resource locator). DataSource kontrolie skup SourceStream objekata. Standardni DataSource koristi niz bajtova kao jedinicu transfera. Sa druge strane postoje i Buffer Data Source koji koriste klasu Buffer kao jedinicu transfera.
75
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave DataSource objekati mogu biti podeljeni na dva tipa, prema tome ko inicira transfer podatka: Pull Data-Source Push Data-Source U JMF-u postoji mogunost kloniranja izvora podatka pozivom metode createCloneableDataSource() objekta Manager. Kao parametar ove metode se prosle uje izyor podataka koji se eli kolnirati. Klonovi se mogu kontrolisati pozivom metoda connect, disconnect, start, i stop kloniranog objekta.
Format podataka
Format podatka u JMF je predstavljen preko Format objekta. Ovaj objekat nosi informaciju o nainu zapisa podataka kao i o samom tipu podataka.
AudioFormat nosi infomacije specifine za audio podatke kao to su brzina odabiranja (sample rate), veliina odbirka (bits per sample), broj audio kanala itd. VideoFormat nosi infomacije relevantne za video podatke. Neki od standardnih video fomata su:
Ukoliko elimo da pratimo promene formata u okviru Controller objekta, implmentira se ControllerListener interfejs i oslukuje se FormatChangeEvents doga aj.
76
Kontrole
JMF Control objekti pruaju mehanizam kontrolisanja (postavljana kao i dobijanje infomacija) nad atributima nekog objekta. Mnogi JMF objekti kao to su Controller, DataSource, DataSink objekti omoguavaju kontrolu nad njima. Ukoliko neki JMF objekat eli da omogui kontrolu nad njim, potrebno je da taj objekat implementira Controls interfejs. Controls interfejs definie metode, pomou kojih se dobijaju Control objekti, objekta koji eli da se kontrolie. Mnoge kontrole omoguavaju kontrolu preko vizuelnih komponenti (AWT komponente).
Standardne kontrole
JMF definie standardne kontrole Control prikazane na Slika 7
77
U JMF je proces prezentacije modelovan preko Controller interfejsa. Controller interfejs definie stanja i mehanizam tranzicije izme u njih, za svaki objekat koji kontrolie, prezentuje i vri akviziciju audio/video podataka. Controller generie razliite MediaEvents objekte kako bi notifikovao promene stanja svojim oslukivaima. Oslukivai doga aja moraju da implemetiraju ControllerInterface. JMF API definie dve vrste Controller objekata: Players i Processors.
Players
Player procesira ulazni tok podataka i reprodukuje ih na odre eni ure aj u zavisnosti od tipa podataka.
Player ne omoguava nikakvu kontrolu nad obradom podataka, niti kontrolu za njiuhov prikaz reprodukcijom.
78
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Player moe biti u jednom od est stanja:
Player u Unrealized stanju je instanciran ali nema nikakvu informaciju o audio/video podacima koje treba da reprodukuje. Kada se prvi put instancira Player preko objekta Manager, one se nalazi u Unrealized stanju. Kada se pozove realize() metoda, Player iz Unrealized stanja prelazi u Realizing stanje. U ovom stanju Player vri alokaciju potrebnih resursa ali nema jo uvek eksluzovno pravo na njih. Nakon alociranja potrebnih resursa, Player prelazi u Realized stanje. U ovom stanju Player zna koji sve reursi su mu potrebni, kao i tip samih podataka koje treba da reprodukuje. Poto u ovom stanju zna kako da reprodukuje podatke, Player u ovom stanju nudi razne kontrole kao i vizuelne komponente. Pozivom prefetch metode, Player prelazi u Prefetching stanje u kome se priprema za prezentaciju podataka. Nakon pripreme, Player prelazi u Prefetched stanje u kom moe biti startovan. Pozivom metode start, Player prelazi u Started stanje.
79
Processors
Processor objekti se mogu isto koristiti za prezentovanje audio video podataka. Propcessor objekti su specijalizovani Player objekti koji pored standardnih kontrola omoguavaju i kontrolu procesiranja ulaznih podataka.
Kao dodatak, Processor objekti izlazne podatke mogu preko DataSource da proslede sledeem Processor, Player objektu ili da zapiu u podatke datoteku korienjem DataSink objekta.
Kao to je reeno Processor za razliku od Player omoguava korisniku da definie implmentira obradu samih podataka. Obrada samih podataka se deli u nekoliko faza:
80
Projektovanje sistema zasnovanih na raunarima Priprema za laboratorijske vebe iz Jave Demultiplexing je proces parsiranja ulaznog toka podatka. Ukoliko se ulazni tok sastoji iz vie razliitih tokova podataka oni se razdvajaju u posebne tokove podataka (Track). Na primer QuickTime datotoeka nakon demultipleksiranja se razdvaja na audio i video tok podataka. Demulktipleksiranje se obavlja automatski ukoliko. Pre-Processing je proces primenjivanja razliitih efekata nad pojedinanim tokom podataka. Coding je proces konverotvanja ulaznih podataka u kompresovani tok podataka. Decoding je proces dekompresije ulaznih podataka. Post-Processing je proces primene efekata nad kompresovanim podacima Multiplexing je proces spajanja kodovanih tokaova odataka u jedan Rendering je proces prezentacije podataka korisniku.
Procesiranje u svakoj fazi je odre eno preko procesnih komponeneta (plug-ins). Ukoliko Processor podrava TrackControls, korisnik selektovanjem odre enog plug-in moe da izabere odre eno procecisranje nad pojedinanim tokom. Postoje pet tipova JMF procesnih komponenti. Demultiplexer parsira ulazni tok podataka kao to su (WAV, MPEG ili QuickTime) Effect primenjuje razliite efekte nad na svakom izdvojenom toku podataka Codec komresija i dekompresija Multiplexer kombinuje vie ulaznih tokova u jedan Renderer procesira tok podataka i prezenuje ga korisniku
Processing kontrole
Da bi dobili kontrolu nad procesiranjem, poziva se Processor metoda getTrackControl koja vraa niz TrackControl objekata za svaki od tokova podataka unutar Processor-a. Pomou TrackControl objekta se mogu definisati efekti, kodeci koji ele da se primene nad tim tokom podataka. Pozivom TrackControl metode getControls dobija se niz kontrola za taj odre eni tok podataka ( kao to su BitRateControl, QualityControl itd).
81
public class TrivialPlayer extends Frame implements ControllerListener,WindowListener{ Player p = null; boolean stateTransitionOK = true; Object waitSync = new Object(); // sync object public TrivialPlayer() throws Exception{ // Get list of available video devices (any video format) Vector devicesList = CaptureDeviceManager.getDeviceList(new VideoFormat(null)); CaptureDeviceInfo info = null; // Display all video capture devices for(int i=0; i<devicesList.size();i++){ info = (CaptureDeviceInfo)devicesList.get(i); System.out.println(info.getName()); } // Get first video device if(devicesList.size() > 0){ info = (CaptureDeviceInfo)devicesList.firstElement(); }else{ throw new Exception(); } // Get media locator and create data source MediaLocator ml = info.getLocator(); p = Manager.createPlayer(Manager.createDataSource(ml)); // listen for transition events p.addControllerListener(this); // realize p.realize(); waitForState(p.Realized); // prefetch p.prefetch(); waitForState(p.Prefetched); Component vc = null; Component cc = null; // Layout the components setLayout(new BorderLayout()); // Get visual AWT component of player
82
/** * Controller Listener. */ public void controllerUpdate(ControllerEvent evt) { if (evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent || evt instanceof PrefetchCompleteEvent) { synchronized (waitSync) { stateTransitionOK = true; waitSync.notifyAll(); } } else if (evt instanceof ResourceUnavailableEvent) { synchronized (waitSync) { stateTransitionOK = false; waitSync.notifyAll(); } } else if (evt instanceof EndOfMediaEvent) { p.close(); } else if (evt instanceof SizeChangeEvent) { } } public void windowClosing(WindowEvent arg0) { p.close(); System.exit(0); } public RuntimeException windowClosingDelivered(WindowEvent arg0) { // TODO Auto-generated method stub return null; }
public RuntimeException windowClosingNotify(WindowEvent arg0) { // TODO Auto-generated method stub return null; }
83
84
import jmapps.util.StateHelper; public class DataStorage { Processor p = null; DataSink datasink = null; StateHelper sh = null; Dimension videoDimension = new Dimension(320, 240); VideoFormat videoFormat = new RGBFormat(videoDimension, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 3, 2, 1, 3, Format.NOT_SPECIFIED, Format.TRUE, Format.NOT_SPECIFIED); public DataStorage() throws Exception{ // Get list of available video devices (any video format) Vector devicesList = CaptureDeviceManager.getDeviceList(videoFormat); CaptureDeviceInfo info = null; // Display all video capture devices for(int i=0; i<devicesList.size();i++){ info = (CaptureDeviceInfo)devicesList.get(i); System.out.println(info.getName()); } // Get first video device if(devicesList.size() > 0){ info = (CaptureDeviceInfo)devicesList.firstElement(); }else{ throw new Exception(); } DataSource ds = Manager.createDataSource(info.getLocator());
p = Manager.createProcessor(ds); sh = new StateHelper(p); // Configure the processor if (!sh.configure(10000)) System.exit(-1); // Set the output content type and realize the processor p.setContentDescriptor(new
85
// Set the output content type and realize the processor if (!sh.realize(10000)) System.exit(-1); // Get the processor's output, create a DataSink. DataSource outputDS = p.getDataOutput(); MediaLocator dest = new MediaLocator("file://c://foo.avi"); datasink = Manager.createDataSink(outputDS, dest); datasink.open(); // now start data sink and processor datasink.start(); System.out.println("Started saving..."); // Capture for 20 seconds sh.playToEndOfMedia(20000); sh.close(); datasink.close(); } public static void main(String[] args) { try { DataStorage dataStorage = new DataStorage(); } catch (Exception e) { e.printStackTrace(); System.exit(-1); } } }
86
public class DataReader extends Frame implements ControllerListener{ Processor p = null; boolean stateTransitionOK = true; Object waitSync = new Object(); // sync object public DataReader() throws Exception{ FileDialog fd = new FileDialog(this, "DataReader", FileDialog.LOAD); fd.setVisible(true); File f = new File (fd.getDirectory(), fd.getFile()); System.out.println(fd.getDirectory() + fd.getFile()); //create data source of input file DataSource ds = Manager.createDataSource(f.toURL()); // Create processor p = Manager.createProcessor(ds); // Listen for transition events p.addControllerListener(this); // Configure p.configure(); waitForState(p.Configured); // Use as player p.setContentDescriptor(null); //Realize p.realize(); waitForState(p.Realized);
87
/** * Controller Listener. */ public void controllerUpdate(ControllerEvent evt) { if (evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent || evt instanceof PrefetchCompleteEvent) { synchronized (waitSync) { stateTransitionOK = true; waitSync.notifyAll(); } } else if (evt instanceof ResourceUnavailableEvent) { synchronized (waitSync) { stateTransitionOK = false; waitSync.notifyAll(); } } else if (evt instanceof EndOfMediaEvent) { p.close(); } else if (evt instanceof SizeChangeEvent) { } } public static void main(String[] args) { // try { Frame f = new DataReader(); f.setTitle("DataReader"); f.pack(); f.setVisible (true); } catch (Exception e) { e.printStackTrace(); System.exit(-1); } } }
88
import com.sun.image.codec.jpeg.ImageFormatException; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; public class MyEffect implements Effect { boolean takeSnapshot = true; protected protected protected protected Format inputFormat; Format outputFormat; Format[] inputFormats; Format[] outputFormats;
private Control[] controls; public MyEffect() { super(); // Array of input formats which this effect supports inputFormats = new Format[] { new RGBFormat(null, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 3, 2, 1, 3, Format.NOT_SPECIFIED, Format.TRUE, Format.NOT_SPECIFIED) }; // Array of output formats which this effect supports outputFormats = new Format[] { new RGBFormat(null, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 3, 2, 1, 3, Format.NOT_SPECIFIED, Format.TRUE, Format.NOT_SPECIFIED) }; } public Format[] getSupportedInputFormats() { return inputFormats; }
89
90
91
import javax.media.Control; import javax.swing.JButton; public class MyEffectControl implements Control, ActionListener { MyEffect effect = null; private Component component; private JButton button; public MyEffectControl(MyEffect effect) { super(); this.effect = effect; } public Component getControlComponent() { if (component == null) { button = new JButton("Take snapshot"); button.addActionListener(this); button.setToolTipText("Click to to take current snapshot"); Panel componentPanel = new Panel(); componentPanel.setLayout(new BorderLayout()); componentPanel.add("Center", button); componentPanel.invalidate(); component = componentPanel; } return component; } public void actionPerformed(ActionEvent e) { Object o = e.getSource(); if (o == button) { effect.takeSnapshot = true; } } }
92
import cbsjmf.MyVideoEffect; import example_3.DataReader; public class TestEffect extends Frame implements ControllerListener{ Processor p = null; boolean stateTransitionOK = true; Object waitSync = new Object(); // sync object
public TestEffect() throws Exception{ // Layout the components setLayout(new BorderLayout()); FileDialog fd = new FileDialog(this, "DataReader", FileDialog.LOAD); fd.setVisible(true); File f = new File (fd.getDirectory(), fd.getFile()); System.out.println(fd.getDirectory() + fd.getFile()); //create data source of input file DataSource ds = Manager.createDataSource(f.toURL()); // Create processor p = Manager.createProcessor(ds); // Listen for transition events p.addControllerListener(this); // Configure p.configure(); waitForState(p.Configured); // Use as player p.setContentDescriptor(null); // Get processor track controls TrackControl tc[] = p.getTrackControls(); if (tc == null) {
93
94
95
/* * Image data source has only one RGB format stream */ class ImageDataSource extends PullBufferDataSource{ ImageSourceStream streams[]; public ImageDataSource(int width, int height, File file) throws FileNotFoundException { streams = new ImageSourceStream[1]; streams[0] = new ImageSourceStream(width, height, file); } public PullBufferStream[] getStreams() { return streams; } public void connect() throws IOException { } public void disconnect() { } public String getContentType() { return ContentDescriptor.RAW; } public Object getControl(String arg0) { return null; } public Object[] getControls() { return new Object[0]; } public Time getDuration() { return DURATION_UNKNOWN; } public void start() throws IOException { } public void stop() throws IOException { } }
96
class ImageSourceStream implements PullBufferStream { int width, height; String fileName; long fileSize; Format format; public ImageSourceStream(int width, int height, File file) throws FileNotFoundException { super(); // Get image width and height this.width = width; this.height = height; // Get file name and its size in byte this.fileName = file.getPath(); this.fileSize = file.length(); // Format of data stream is RGB Dimension videoDimension = new Dimension(width, height); format = new RGBFormat(videoDimension, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 1, 2, 3, 3, width*3, Format.FALSE, RGBFormat.NOT_SPECIFIED);
} public Format getFormat() { return format; } /** * This is called from the Processor to read a frame worth * of video data. */ public void read(Buffer buf) throws IOException { // Open input data FileInputStream inputFile= new FileInputStream(fileName); DataInputStream in = new DataInputStream(new BufferedInputStream(inputFile)); // Check size of buffer byte data[] = null;
97
buf.setOffset(0); buf.setLength((int)fileSize); buf.setFormat(format); buf.setFlags(buf.getFlags()); // Close the random access file. in.close(); } public boolean willReadBlock() { return false; } public boolean endOfStream() { return false; } public ContentDescriptor getContentDescriptor() { return new ContentDescriptor(ContentDescriptor.RAW); } public long getContentLength() { return 0; } public Object getControl(String arg0) { return null; } public Object[] getControls() { return new Object[0]; } }
98
public class TestImageSource extends Frame implements ControllerListener{ Processor p = null; boolean stateTransitionOK = true; Object waitSync = new Object(); // sync object public TestImageSource() throws Exception{ // Choose file FileDialog fd = new FileDialog(this, "TestImageSource", FileDialog.LOAD); fd.setVisible(true); File f = new File (fd.getDirectory(), fd.getFile()); System.out.println(fd.getDirectory() + fd.getFile()); //create data source of input file ImageDataSource ids = new ImageDataSource(512, 512, f); // Create processor p = Manager.createProcessor(ids); // Listen for transition events p.addControllerListener(this); // Configure p.configure(); waitForState(p.Configured); // Use as player p.setContentDescriptor(null); //Realize p.realize(); waitForState(p.Realized); // Prefetch p.prefetch(); waitForState(p.Prefetched); // Layout the components setLayout(new BorderLayout()); Component vc = null; if ((vc = p.getVisualComponent()) != null) { add("Center", vc); } p.start();
99
100