390 likes | 587 Views
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
E N D
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 StaticUgnjezdena{ // 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 StaticUgnjezdena nezavisno od toga da li su objekti spoljasnje klase uopšte kreirani. • Npr. • Spoljasnja.StaticUgnjezdena primer = new Spoljasnja.StaticUgnjezdena(); • 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 MagicniSesir koja definiše objekat koji sadrži promenljiv broj objekata klase Zec. • Definiciju klase Zec smestićemo unutar definicije klase MagicniSesir. • Zec je primer ugnježdene klase. • Osnovna struktura MagicniSesir.java biće: • public class MagicniSesir{ // definicija klase MagicniSesir ... // 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 ove 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 nextInt() 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 MagicniSesirbez 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 MagicniSesir koji mu je pridružen.
TestMagicniSesir • Svaki objekat klase MagicniSesir sadržaće slučajan broj objekata klase Zec. Konstruktor objekta MagicniSesir smešta ime šešira u njegov private član imeSesira i generiše niz zecevi sa najmanje 1, a najviše maxZeceva elemenata. • To se postiže izrazom: 1 + izbor.nextInt(maxZeceva) poziv nextInt(maxZeceva) vraća vrednost koja pripada [0,maxZeceva-1] a to +1 je iz intervala [1,maxZeceva] • Tako kreiran niz se zatim popunjava objektima klase Zec
Magični šešir • Klasa MagicniSesir takođe ima toString() 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 toString(). • Osnovna imena koja koristimo za generisanje imena zečeva definisana su u statičkom nizu imenaZeceva[] 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 brojImenaZeceva[]. Taj niz ima isti broj elemenata kao niz imenaZeceva[], i svaki element čuva vrednost koja će biti dopisana na odgovarajuće ime iz niza imenaZeceva[].
Magični šešir • Klasa Zec ima član ime za smeštanje imena inicijalizovanog u konstruktoru. • Slučajno osnovno ime bira se iz niza imenaZeceva[]. Zatim se doda tekući brojač za ime inkrementiran za 1, pa uzastopna upotreba osnovnog imena, npr. Dusko, proizvodi imena Dusko1, Dusko2, itd. • Metod toString() klase Zec vraća ime objekta • main() metod iz TestMagicniSesir kreira tri objekta klase MagicniSesir i ispisuje njihove string reprezentacije. Zadavanje objekta kao argumenta println() metoda automatski poziva metod toString() za objekat.
Ugnježdene klase • Ako pogledamo .class fajlove koje je generisao kompajler, klasa Zec ima svoj sopstveni fajl sa imenom MagicniSesir$Zec.class (ime ugnježdene klase Zec kvalifikovano je imenom klase koja je sadrži, tj. MagicniSesir)
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 imenaZeceva i brojImenaZeceva 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 brojImenaZeceva, imena koja se generišu neće biti jedinstvena. • Rešenje je zadržati imenaZeceva i brojImenaZeceva statičkim, ali smestiti ih u klasu MagicniSesir.
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 MagicniSesir. Može se koristiti ista verzija klase TestMagicniSesir. • 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 MagicniSesir su sada pridruženi tekućem objektu klase MagicniSesir 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 MagicniSesir, sa statičkom klasom Zec, nezavisnog zeca možemo kreirati u metodu main() sa: • System.out.println(”Nezavisan zec: ” + new MagicniSesir.Zec()); • Objekat klase Zec je potpuno slobodan – ne postoji objekat klase MagicniSesir 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 TestMagicniSesir da kreiramo drugi objekat klase MagicniSesir i zatim kreiramo objekat klase Zec za njega • MagicniSesir stariSesir = new MagicniSesir(”Stari sesir”); MagicniSesir.Zec zec = stariSesir.new Zec(); // Kreiranje zec objekta System.out.println(stariSesir); System.out.println(”\nNovi zec je: ” + zec);
Korišćenje ugnježdene klase izvan top-level klase • Dodati kod prvo kreira objekat stariSesir klase MagicniSesir. • On će imati svoje sopstvene zečeve. • Zatim koristi taj objekat da kreira objekat klase MagicniSesir.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 MagicniSesir. • 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 stariSesir. • To ne znači da objekat stariSesir 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 stariSesir. • Iz ovog primera se može videti da ime novog zeca nije deo objekta stariSesir, iako je pridruženo staromSesir-u. • To se može demonstrirati modifikovanjem metoda toString() u klasi Zec sa: • public String toString(){ return ime + ” roditelj: ” + imeSesira; }
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 (localinner) 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 akosu 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 runFinalization() metod: • System.runFinalization(); • JVM će dati sve od sebe da izvrši finalize() za svaki od mrtvih objekata pre povratka iz runFinalization() 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 getData(); // 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 ActionListener za jednokratnu upotrebu. • pickButton.addActionListener(new ActionListener() { // Code to define the class // that implements the // ActionListener interface } ); • Definicija klase pojavljuje se u izrazu new koji kreira argument za metod AddActionListener(). Ovaj metod zahteva referencu tipa ActionListener – drugim rečima referencu na klasu koja implementira ActionListener 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 ActionListener 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.