You are on page 1of 39

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 MagicniSesir 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 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 ( 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 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.

You might also like