Objektno orijentisano programiranje Java ugnjedene klase Ugnjedene nested
Objektno orijentisano programiranje Java, ugnježdene klase
Ugnježdene (nested) klase ● ● Klase do sada bile su međusobno odvojene – svaka smeštena odvojeno u svom izvornom fajlu Ne moraju sve klase biti definisane na taj način Definicija klase može se nalaziti unutar definicije neke druge klase. Unutrašnja klasa naziva se ugnježdena (nested) klasa Ukoliko za to ima potrebe, ugnježdena klasa u sebi može imati ugnježdenu klasu
Ugnježdene (nested) klase ● ● Kada definišemo ugnježdenu klasu, ona je član spoljašnje klase na isti način kao i ostali članovi Ugnježdena klasa može imati pristupni atribut kao i ostali članovi i pristup izvan spoljašnje klase određen je pristupnim atributima na isti način Ugnježdena klasa bi trebalo da ima specifičnu povezanost sa spoljašnjom klasom. Proizvoljno ugnježdenje jedne klase u drugu nema smisla top-level klasa je klasa koja sadrži ugnježdenu klasu, ali sama po sebi nije ugnježdena
Nestatičke ugnježdene (nested) klase ● ● ● Ako se ne definiše kao statički član spoljašnje klase, ugnježdena klasa ima smisao/značenje samo u kontekstu objekta spoljašnje klase. Dok se ne kreira objekat spoljašnje klase, ne može se kreirati objekat ugnježdene klase Međutim, kada se deklariše objekat spoljašnje klase, nije neophodno da se kreiraju objekti ugnježdene klase, osim, naravno, ako ih ne kreira konstruktor spoljašnje klase Npr.
Primer nestatičke ugnježdene klase public class Spoljasnja{ // Ugnjezdena public class Ugnjezdena{ // Detalji ugnjezdene klase. . . } // Ostali clanovi Spoljasnje klase } Spoljasnja sp = new Spoljasnja();
Primer nestatičke ugnježdene klase ● ● Nakon ovoga nije kreiran nijedan objekat klase Ugnjezdena. Ako sada želimo da kreiramo objekat ugnježdene klase, moramo referisati ime ugnježdene klase sa imenom spoljašnje klase kao kvalifikatorom – nakon prethodne naredbe, možemo kreirati objekat klase Ugnjezdena sa: Spoljasnja. Ugnjezdena ugnj = sp. new Ugnjezdena(); // definisanje objekta ugnjezdene klase Ovde smo kreirali objekat ugnježdene klase koji je pridružen objektu sp koji smo ranije kreirali Objekat klase Ugnjezdena kreiramo u kontekstu objekta sp.
Nestatičke ugnježdene klase ● ● ● Unutar nestatičkih metoda klase Spoljasnja, možemo koristiti ime klase Ugnjezdena bez kvalifikovanja, pošto će ono od strane kompajlera biti automatski kvalifikovano promenljivom this. Tako se unutar nestatičkog metoda klase Spoljasnja može kreirati novi objekat klase Ugnjezdena sa: Ugnjezdena ugnj = new Ugnjezdena(); što je ekvivalentno sa: this. Ugnjezdena ugnj = this. new Ugnjezdena();
Nestatičke ugnježdene klase ● ● ● Sve ovo povlači da statički metod spoljašnje klase ne može kreirati objekte nestatičke ugnježdene klase. Pošto Ugnjezdena nije statički član klase Spoljasnja, ako statički metod klase Spoljasnja pokuša direktno da kreira objekat klase Ugnjezdena, bez prethodnog uvođenja konkretnog objekta klase Spoljasnja, on pokušava da kreira objekat izvan legitimnog opsega tog objekta. Dalje, pošto klasa Ugnjezdena nije statički član klase Spoljasnja, ne može ni sama sadržati statičke članove, jer bi to bilo logički kontradiktorno.
Tipična upotreba ugnježdenih klasa ● ● Tipično se ugnježdene klase koriste za definisanje objekata koji u najmanju ruku imaju snažnu povezanost sa objektima spoljašnje klase. Dalja upotreba ugnježdenih klasa je za grupisanje povezanih klasa spoljašnjom klasom
Statičke ugnježdene klase ● ● Deklarisanjem ugnježdene klase kao statičke, objekti ugnježdene klase nisu zavisni od objekata spoljašnje klase. public class Spoljasnja{ public static class Static. Ugnjezdena{ // detalji staticke ugnjezdene klase. . . } // Ugnjezdena klasa public class Ugnjezdena{ // detalji ugnjezdene klase. . . } // Ostali clanovi Spoljasnje klase }
Statičke ugnježdene klase ● ● Sada možemo da deklarišemo objekte klase Static. Ugnjezdena nezavisno od toga da li su objekti spoljasnje klase uopšte kreirani. Npr. Spoljasnja. Static. Ugnjezdena primer = new Spoljasnja. Static. Ugnjezdena(); ● ● Ovo je značajno drugačije od onoga za nestatičku ugnježdenu klasu. Sada moramo da koristimo ime ugnježdene klase kvalifikovano imenom spoljašnje klase kao tip prilikom kreiranja objekta. Ime statičke ugnježdene klase postoji u kontekstu spoljašnje klase i ime ugnježdene klase je kvalifikovano imenom spoljašnje klase.
Statičke ugnježdene klase ● Statička ugnježdena klasa može imati statičke članove, dok nestatička ugnježdena klasa ne može.
Jednostavan primer ● ● Kreiraćemo klasu Magicni. Sesir koja definiše objekat koji sadrži promenljiv broj objekata klase Zec. Definiciju klase Zec smestićemo unutar definicije klase Magicni. Sesir. Zec je primer ugnježdene klase. Osnovna struktura Magicni. Sesir. java biće: public class Magicni. Sesir{ // definicija klase Magicni. Sesir. . . // ugnjezdena klasa za definisanje zeca static class Zec{ // definicija klase Zec. . . } }
Magični šešir, java. util. Random ● ● ● Ugnježdena klasa je definisana kao static jer želimo da možemo da definišemo statičke članove klase. Umesto Math. random() metoda koristimo objekat klase Random definisane u paketu java. util Objekat klase Random ima mnoštvo metoda za generisanje pseudo-slučajnih vrednosti različitih tipova i različitih opsega Metod next. Int() vraća integer koji je nenegativan, ali manji od integer vrednosti prosleđene kao argument Pa, ako se kao argument prosledi dužina niza, generisaće slučajnu vrednost indeksa, koja će uvek biti legalna za veličinu niza
Definicija ugnježdene klase Zec ● ● ● Kada kreiramo objekat klase Zec, želimo da ima jedinstveno ime da bismo mogli da razlikujemo zečeve. Jedinstvena imena možemo generisati izborom jednog od ograničenog skupa fiksiranih imena, a zatim dopisivanjem celog broja koji je drugačiji svaki put kada se koristi osnovno ime. Primetimo da konstruktor klase Zec može pristupati članu izbor spoljašnje klase Magicni. Sesir bez kvalifikovanja. To je moguće samo za statičke članove spoljašnje klase – ne možemo referisati nestatičke članove spoljašnje klase ovde, zato što ne postoji objekat klase Magicni. Sesir koji mu je pridružen.
Test. Magicni. Sesir ● ● ● Svaki objekat klase Magicni. Sesir sadržaće slučajan broj objekata klase Zec. Konstruktor objekta Magicni. Sesir smešta ime šešira u njegov private član ime. Sesira i generiše niz zecevi sa najmanje 1, a najviše max. Zeceva elemenata. To se postiže izrazom: 1 + izbor. next. Int(max. Zeceva) poziv next. Int(max. Zeceva) vraća vrednost koja pripada [0, max. Zeceva-1] a to +1 je iz intervala [1, max. Zeceva] Tako kreiran niz se zatim popunjava objektima klase Zec
Magični šešir ● ● ● Klasa Magicni. Sesir takođe ima to. String() metod, koji vraća String objekat koji sadrži ime šešira i imena svih zečeva iz šešira. Ovo pretpostavlja da klasa Zec takođe ima definisan metod to. String(). Osnovna imena koja koristimo za generisanje imena zečeva definisana su u statičkom nizu imena. Zeceva[] u klasi Zec. Broj za svako osnovno ime, koji ćemo dopisati na osnovno ime da bi se dobilo jedinstveno ime za zeca, smešten je u statičkom nizu broj. Imena. Zeceva[]. Taj niz ima isti broj elemenata kao niz imena. Zeceva[], i svaki element čuva vrednost koja će biti dopisana na odgovarajuće ime iz niza imena. Zeceva[].
Magični šešir ● ● Klasa Zec ima član ime za smeštanje imena inicijalizovanog u konstruktoru. Slučajno osnovno ime bira se iz niza imena. Zeceva[]. Zatim se doda tekući brojač za ime inkrementiran za 1, pa uzastopna upotreba osnovnog imena, npr. Dusko, proizvodi imena Dusko 1, Dusko 2, itd. Metod to. String() klase Zec vraća ime objekta main() metod iz Test. Magicni. Sesir kreira tri objekta klase Magicni. Sesir i ispisuje njihove string reprezentacije. Zadavanje objekta kao argumenta println() metoda automatski poziva metod to. String() za objekat.
Ugnježdene klase ● Ako pogledamo. class fajlove koje je generisao kompajler, klasa Zec ima svoj sopstveni fajl sa imenom Magicni. Sesir$Zec. class (ime ugnježdene klase Zec kvalifikovano je imenom klase koja je sadrži, tj. Magicni. Sesir)
Korišćenje nestatičke ugnježdene klase ● ● ● Ukoliko u prethodnom primeru obrišemo ključnu reč static iz definicije ugnježdene klase, program se neće iskompajlirati. Problem su statički članovi imena. Zeceva i broj. Imena. Zeceva klase Zec Ranije smo videli da nestatička ugnježdena klasa ne može imati statičke članove, tako da moramo naći alternativni način za rad sa imenima ako želimo da Zec učinimo nestatičkom ugnježdenom klasom.
Korišćenje nestatičke ugnježdene klase ● ● Ako nam ovi nizovi budu nestatički, to ima nekoliko mana. Prvo, svaki objekat klase Zec imaće svoju sopstvenu kopiju ovih nizova – nepotrebno dupliranje podataka. Ozbiljniji problem je što proces imenovanja zečeva neće raditi. Pošto svaki objekat ima sopstvenu kopiju niza broj. Imena. Zeceva, imena koja se generišu neće biti jedinstvena. Rešenje je zadržati imena. Zeceva i broj. Imena. Zeceva statičkim, ali smestiti ih u klasu Magicni. Sesir.
Pristup članovima top-level klase ● ● ● Jedine izmene su brisanje ključne reči static u definiciji klase Zec i pomeranje atributa vezanih za imena zečeva u klasu Magicni. Sesir. Može se koristiti ista verzija klase Test. Magicni. Sesir. Iako je izlaz iz programa u mnogome isti, ono što se dešava je drugačije. Objekti klase Zec koji se kreiraju u konstruktoru klase Magicni. Sesir su sada pridruženi tekućem objektu klase Magicni. Sesir koji se konstruise. Poziv konstruktora Zec() je zapravo this. Zec()
Korišćenje ugnježdene klase izvan top-level klase ● ● ● Objekti ugnježdene klase mogu se kreirati izvan top-level klase koja je sadrži. Kako se to radi, zavisi od toga da li je ugnježdena klasa statički član spoljašnje klase ili nije. Sa prvom verzijom klase Magicni. Sesir, sa statičkom klasom Zec, nezavisnog zeca možemo kreirati u metodu main() sa: System. out. println(”Nezavisan zec: ” + new Magicni. Sesir. Zec()); ● ● Objekat klase Zec je potpuno slobodan – ne postoji objekat klase Magicni. Sesir koji ga sadrži i sputava. U slučaju nestatičke klase Zec, stvari su drugačije.
Korišćenje ugnježdene klase izvan top-level klase ● ● Slučaj nestatičke ugnježdene klase Zec ( druga verzija ) Modifikujemo main() metod iz Test. Magicni. Sesir da kreiramo drugi objekat klase Magicni. Sesir i zatim kreiramo objekat klase Zec za njega Magicni. Sesir stari. Sesir = new Magicni. Sesir(”Stari sesir”); Magicni. Sesir. Zec zec = stari. Sesir. new Zec(); // Kreiranje zec objekta System. out. println(stari. Sesir); System. out. println(”n. Novi zec je: ” + zec);
Korišćenje ugnježdene klase izvan top-level klase ● ● ● Dodati kod prvo kreira objekat stari. Sesir klase Magicni. Sesir. On će imati svoje sopstvene zečeve. Zatim koristi taj objekat da kreira objekat klase Magicni. Sesir. Zec ( ime ugnježdene klase se referiše na ovaj način: kvalifikovanjem imenom top-level klase ) Konstruktor ugnježdene klase se može zvati samo kvalifikovanjem imenom objekta klase Magicni. Sesir. Ovo zbog toga što nestatička ugnježdena klasa može referisati članove top-level klase – uključujući instancne promenljive. Tako, instanca top-level klase mora postojati da bi ovo bilo moguće.
Korišćenje ugnježdene klase izvan top-level klase ● ● ● ● Primetiti kako se top-level objekat koristi u pozivu konstruktora. Ime objekta je kvalifikator ispred ključne reči new koja prethodi pozivu konstruktora ugnježdene klase Time se kreira objekat zec u kontekstu objekta stari. Sesir. To ne znači da objekat stari. Sesir ima objekat zec kao član. To znači da ako se članovi top-level klase koriste u ugnježdenoj klasi, oni će biti članovi objekta stari. Sesir. Iz ovog primera se može videti da ime novog zeca nije deo objekta stari. Sesir, iako je pridruženo starom. Sesir-u. To se može demonstrirati modifikovanjem metoda to. String() u klasi Zec sa: public String to. String(){ return ime + ” roditelj: ” + ime. Sesira; }
Lokalne ugnježdene klase ● ● ● Klasa se može definisati unutar metoda, i to je tzv. lokalna ugnježdena klasa Još se naziva i lokalna unutrašnja ( local inner) klasa, pošto se nestatička ugnježdena klasa često još zove i unutrašnja (inner) klasa Objekti lokalne unutrašnje klase mogu se kreirati samo lokalno, tj. unutar metoda u kome je i definicija klase. To je korisno kada izračunavanje u metodu zahteva upotrebu specijalizovane klase koja se ne zahteva i ne koristi nigde drugde. Dobar primer su osluškivači događaja (listeners for events) Lokalna unutrašnja klasa može referisati promenljive deklarisane u metodu u čijoj definiciji se pojavljuje, ali samo ako su final.
Metod finalize() ● ● ● U definiciju klase može se uključiti metod finalize(). Ovaj metod se poziva automatski pre nego se objekat konačno uništi i oslobodi prostor koji je zauzimao u memoriji. U praksi, to može biti nešto nakon što objekat postane nedostupan u programu Kada JVM uništava objekat, ona zove metod finalize() za objekat. protected void finalize(){ // nas kod. . . }
Metod finalize() ● ● Ovaj metod je koristan ako objekti klase koriste resurse koji zahtevaju neku specijalnu akciju kada se uništavaju. Tipično, to su resursi koji nisu iz Java okruženja i ne garantuje se da će ih objekat sam osloboditi. To mogu biti grafički resursi, fontovi ili drugi resursi povezani sa crtanjem koje je obezbedio host operativni sistem ili eksterni fajlovi na hard disku. Ukoliko se oni ne oslobode pre uništenja objekta, zauzimaju sistemske resurse i ukoliko ih potrošimo u dovoljnoj količini, naš program, a možda i drugi programi na sistemu mogu da prestanu da rade.
Metod finalize() ● ● Za većinu klasa ovo nije neophodno, ali ako je npr. objekat otvorio fajl sa diska, a ne garantuje njegovo zatvaranje, želimo da budemo sigurni da će fajl biti zatvoren kada se objekat uništi. Možemo implementirati metod finalize() tako da se pobrine za to. Druga upotreba metoda finalize() je da upamti činjenicu da je objekat uništen. Možemo implementirati metod finalize() za klasu Sphere ( našu od ranije ) tako da dekrementira vrednost statičkog atributa count. Onda je count broj Sphere objekata, a ne koliko ih je kreirano. To međutim nije precizna mera. Sledi objašnjenje i zašto.
Metod finalize() ● ● ● Ne možemo računati na to da će objekat biti uništen kada više nije dostupan kodu našeg programa. Osim ako naš program ne poziva System. gc() metod, JVM će se osloboditi neželjenih objekata i osloboditi memoriju koju oni zauzimaju jedino ako joj ponestaje memorije, ili ako nema aktivnosti u našem programu – npr. kada čeka na ulaz. Kao posledica toga, može se desiti da se objekti ne unište dok se program ne završi. Takođe, nemamo garancije kada će biti pozvan metod finalize(). Jedino je sigurno da će biti pozvan pre nego što se oslobodi memorija koju je objekat zauzimao. Ništa što zavisi od vremena ne bi trebalo ostavljati finalize() metodu!
Metod finalize() ● ● ● Ukoliko ne dopuštamo mogućnost da naši objekti ”vise” okolo, ovo može izazvati probleme. Npr. ako kreiramo objekat u metodu koji otvara fajl i računamo da će ga metod finalize() zatvoriti, a zatim u petlji pozivamo taj metod, možemo završiti sa velikim brojem istovremeno otvorenih fajlova, pošto objekat koji se kreira svakim pozivom metoda neće obavezno biti uništen neposredno po povratku iz metoda. To otvara mogućnost da naš program pokuša da ima otvoren veći broj fajlova istovremeno nego što operativni sistem dopušta. U toj situaciji, treba da se osiguramo da je fajl zatvoren kada završimo sa njim, uključivanjem metoda objekta koji će ga eksplicitno zatvoriti, npr. close(). Klasa System takođe obezbeđuje drugu mogućnost. Možemo predložiti JVM da treba pokrenuti finalize() metode za sve odbačene objekte, ukoliko već nisu pokrenuti. Samo se pozove run. Finalization() metod: System. run. Finalization(); JVM će dati sve od sebe da izvrši finalize() za svaki od mrtvih objekata pre povratka iz run. Finalization() metoda, ali nema garancije.
Native metodi ● ● Moguće je u klasu uključiti metod implementiran u nekom drugom programskom jeziku, npr. C-u ili C++-u, eksternom za JVM. Za zadavanje takvog metoda unutar definicije klase, koristi se ključna reč native u deklaraciji metoda. Npr. public native long get. Data(); // deklarisanje ne-Java metoda Naravno, metod neće imati telo u Javi, pošto je deklarisan na nekom drugom mestu. Implementacija native metoda mora da koristi interfejs ka Java okruženju. Standardan API za implementiranje native metoda u C-u, npr. zove se JNI – Java Native Interface
Native metodi ● ● Korišćenjem native metoda gubi se portabilnost. Bezbednosni zahtevi za aplete ugrađene u web stranice zahtevaju da čitav kod mora biti napisan u Javi. Dakle, korišćenje native metoda u apletu jednostavno nije moguće. Pošto je primarni razlog za korišćenje Jave portabilnost koda i mogućnost proizvođenja apleta, potreba za dodavanje native metoda našim Java programima biće minimalna.
Anonimne klase ● ● ● Postoje situacije kada želimo da definišemo klasu za koju želimo da definišemo samo jedan objekat u programu i jedina upotreba tog objekta je da se prosledi direktno kao argument metoda U ovom slučaju, sve dok ta klasa nasleđuje postojeću klasu ili implementira neki interfejs, imamo opciju da je definišemo kao anonimnu klasu Definicija anonimne klase se pojavljuje u izrazu new, u naredbi u kojoj kreiramo i koristimo, pa tako nema ni potrebe da obezbedimo ime za klasu.
Anonimne klase, primer ● ● Pretpostavimo da želimo da definišemo objekat klase koja implementira interfejs Action. Listener za jednokratnu upotrebu. pick. Button. add. Action. Listener(new Action. Listener() { // Code to define the class // that implements the // Action. Listener interface } ); Definicija klase pojavljuje se u izrazu new koji kreira argument za metod Add. Action. Listener(). Ovaj metod zahteva referencu tipa Action. Listener – drugim rečima referencu na klasu koja implementira Action. Listener interfejs.
Anonimne klase, primer ● ● ● Zagrade koje prate ime interfejsa ukazuju na to da kreiramo referencu na objekat tog tipa, dok se detalji definicije klase pojavljuju unutar zagrada. Anonimna klasa može sadržati i atribute i metode, ali ne i konstruktore, pošto klasa nema ime. Ovde, svi metodi deklarisani u Action. Listener interfejsu moraju biti definisani. Ovaj pristup se koristi u praksi, kada se implementiraju prozorski orijentisane aplikacije.
Anonimne klase, primer ● ● ● Ako anonimna klasa nasleđuje postojeću klasu, sintaksa je u najvećoj meri ista. U ovom slučaju pozivamo konstruktor bazne klase, i ukoliko to nije podrazumevani konstruktor, možemo mu proslediti argumente zadajući ih unutar zagrada koje prate ime bazne klase. Definiciju anonimne klase takođe treba smestiti unutar para vitičastih zagrada, kao i u prethodnom primeru. Anonimna klasa je pogodna u situacijama kada je definicija klase kratka i jednostavna.
- Slides: 39