Što je rano i kasno uvezivanje? Rano i kasno vezanje metode. Rano i kasno uvezivanje u Javi

10.11.2019 Programi

Zadnje ažuriranje: 04.02.2019

Prethodno smo pogledali dva načina za promjenu funkcionalnosti metoda naslijeđenih od osnovne klase - skrivanje i nadjačavanje. Koja je razlika između ove dvije metode?

Nadjačavanje

Uzmimo primjer s nadjačavanjem metode:

Klasa Osoba ( public string FirstName ( get; set; ) public string LastName ( get; set; ) public Person(string firstName, string lastName) ( FirstName = firstName; LastName = lastName; ) public virtual void Display() ( Console.WriteLine ($"(FirstName) (LastName)"); ) ) class Employee: Person ( public string Company ( get; set; ) public Employee(string FirstName, string LastName, string Company) : base(firstName, lastName) ( Company = tvrtka; ) public override void Display() ( Console.WriteLine($"(FirstName) (LastName) runs in (Company)"); ) )

Kreirajmo također objekt Employee i proslijedimo ga varijabli tipa Person:

Osoba tom = novi zaposlenik ("Tom", "Smith", "Microsoft"); tom.Display(); // Tom Smith radi u Microsoftu

Sada dobivamo drugačiji rezultat nego sa skrivanjem. A kada se pozove tom.Display(), izvršava se implementacija metode Display iz klase Employee.

Za rad s virtualnim metodama, prevodilac generira tablicu virtualnih metoda (Virtual Method Table ili VMT). U njega su upisane adrese virtualnih metoda. Svaki razred ima svoju tablicu.

Kada se kreira objekt klase, prevodilac prosljeđuje poseban kod konstruktoru objekta koji povezuje objekt i VMT tablicu.

A kada se pozove virtualna metoda, adresa njene VMT tablice preuzima se iz objekta. Adresa metode se zatim dohvaća iz VMT-a i kontrola se prenosi na njega. Odnosno, proces odabira implementacije metode provodi se tijekom izvođenja programa. Ovo je zapravo način na koji se izvršava virtualna metoda. Imajte na umu da budući da runtime okruženje prvo treba dobiti adresu željene metode iz VMT tablice, to malo usporava izvođenje programa.

Prikrivanje

Sada uzmimo iste klase Osoba i Zaposlenik, ali umjesto nadjačavanja koristimo skrivanje:

Klasa Osoba ( public string FirstName ( get; set; ) public string LastName ( get; set; ) public Person(string firstName, string lastName) ( FirstName = firstName; LastName = lastName; ) public void Display() ( Console.WriteLine( $"(Ime) (Prezime)"); ) ) class Employee: Person ( public string Company ( get; set; ) public Employee(string FirstName, string LastName, string Company) : base(firstName, lastName) ( Company = company ; ) public new void Display() ( Console.WriteLine($"(FirstName) (LastName) runs in (Company)"); ) )

I da vidimo što se događa u sljedećem slučaju:

Osoba tom = novi zaposlenik ("Tom", "Smith", "Microsoft"); tom.Display(); //Tom Smith

Tom varijabla predstavlja tip osobe, ali pohranjuje referencu na objekt Employee. Međutim, prilikom pozivanja metode Display izvršit će se verzija metode koja je definirana u klasi Osoba, a ne u klasi Zaposlenik. Zašto? Klasa Employee ne nadjačava metodu Display naslijeđenu od osnovne klase, već zapravo definira novu metodu. Stoga, kada se pozove tom.Display(), poziva se metoda Display iz klase Person.

Prije nego što se dotaknemo stvarne upotrebe virtualnih funkcija, potrebno je razmotriti takve koncepte kao što su rano i kasno vezanje. Usporedimo dva pristupa nabavi npr. kilograma naranči. U prvom slučaju unaprijed znamo da trebamo kupiti 1 kg. naranče. Stoga uzimamo mali paket, ne puno, ali dovoljno novca da pokrije ovaj kilogram. U drugom slučaju, kada izađemo iz kuće, ne znamo što ili koliko trebamo kupiti. Stoga, uzimamo auto (što ako ima puno stvari i nešto teško), opskrbljujemo se velikim i malim torbama i uzimamo što više više novca. Odemo na tržnicu i ispostavi se da trebamo kupiti samo 1 kg. naranče.

Gornji primjer u određenoj mjeri odražava značenje uporabe ranog i kasni uvez, odnosno. Očito je da za ovaj primjer prva opcija je optimalna. U drugom slučaju dali smo previše, ali nam nije trebalo. S druge strane, ako na putu do tržnice zaključimo da nam naranče ne trebaju i odlučimo kupiti 10 kg. jabuke, onda u prvom slučaju to više nećemo moći. U drugom slučaju, to je lako.

Pogledajmo sada ovaj primjer s programskog gledišta. Kada koristimo rano vezanje, čini se da govorimo kompajleru: "Znam točno što želim. Stoga čvrsto (statički) povežite sve pozive funkcija." Kada koristimo mehanizam kasnog povezivanja, čini se da govorimo kompajleru: "Još ne znam što želim. Kad dođe vrijeme, reći ću ti što i kako želim."

Stoga, tijekom ranog povezivanja, pozivatelj i pozvane metode vežu se prvom prilikom, obično pri kompilaciji.

Kada kasno vežete pozvanu metodu i pozivnu metodu, one se ne mogu vezati tijekom kompilacije. Stoga je implementiran poseban mehanizam koji određuje kako će se vezanje pozvane i pozivajuće metode dogoditi kada se poziv stvarno izvrši.

Jasno je da je brzina i učinkovitost ranog vezanja veća od one kasnog vezivanja. U isto vrijeme, kasno uvezivanje pruža određenu svestranost uvezivanja.

Konačno smo došli do samih virtualnih funkcija i metoda. Nažalost, za ilustraciju virtualnih metoda prilično je teško povući bilo kakvu analogiju s fizičkim svijetom. Stoga ćemo odmah razmotriti ovo pitanje s programskog gledišta.

Dakle, za što se koriste virtualne metode? Virtualne metode postoje kako bi se "nasljednik" ponašao drugačije od "pretka", a istovremeno zadržao kompatibilnost s njim.

Ovdje je definicija virtualnih metoda:

Virtualna metoda je metoda koja, kada se deklarira u potomcima, nadjačava svugdje odgovarajuću metodu, čak i u metodama opisanim za pretka ako se poziva za dijete.

Adresa virtualne metode poznata je samo u trenutku kada se program izvršava. Kada se pozove virtualna metoda, njena adresa se preuzima iz tablice virtualnih metoda svoje klase. Ovako se zove ono što treba.

Prednost korištenja virtualnih metoda je u tome što koristi kasno vezanje, što omogućuje obradu objekata čiji je tip nepoznat u vrijeme kompajliranja.

Kako bih ilustrirao korištenje virtualnih metoda, dat ću primjer u jeziku C++ koju sam posudio od jednoga C++ vodič. Čak i ako niste baš upućeni u ovaj jezik, nadam se da će vam moja objašnjenja barem nekako objasniti njegovo značenje.

#uključi // povezivanje standardne C++ biblioteke, // koja opisuje neke funkcije korištene u klasi programa vozilo // klasa "vozilo" ( int wheels; float weight; public: // početak javnog (otvorenog) dijela klase virtualna void poruka (void) (cout message(); // poziva metodu poruke brisanja unicycle objekta; // brisanje unicycle objekta // Svi sljedeći blokovi od 3 retka su apsolutno identični prvom // bloku sa samo razlika je u tome što se klasa stvorenog objekta // mijenja u auto, kamion, čamac unicycle = novi automobil; unicycle->message(); brisanje unicycle; unicycle = novi kamion; unicycle->message(); brisanje unicycle; unicycle = novi čamac; monocikl->poruka(); brisanje monocikla; )

Rezultati programa (ispis na ekranu):

Vozilo Osobni automobil Vozilo Brod

Razmotrimo navedeni primjer. Imamo tri razreda automobil, kamion I čamac, koji su izvedeni iz osnovne klase vozilo. U osnovnoj klasi vozilo opisana virtualna funkcija poruka. U dva od tri razreda ( automobil, čamac) također opisuje njegove funkcije poruka, i u razredu kamion nema opisa njegove funkcije poruka. Svi redovi za koje nisam dao komentar nisu od temeljne važnosti za ovaj primjer. Sada idemo preko glavnog bloka programa - funkcija glavni(). Opisivanje varijable monocikl, kao pokazivač na objekt tipa vozilo. Neću ulaziti u detalje o tome zašto je to pokazivač na objekt. Tako i treba biti. U tom slučaju prema radu s pokazivačem postupajte kao da radite sa samim objektom. Detalje rada s pokazivačima možete pronaći u opisima pojedinog OOP jezika. Zatim stvorite objekt klase vozilo, varijabla monocikl pokazuje na ovaj objekt. Nakon toga nazivamo metodu poruka objekt monocikl, au sljedećem retku brišemo ovaj objekt. U sljedeća tri bloka od 3 retka provodimo slične operacije, s jedinom razlikom što radimo s objektima klase automobil, kamion, čamac. Korištenje pokazivača omogućuje nam korištenje istog pokazivača za sve izvedene klase. Zanima nas poziv funkcije poruka za svaki od objekata. Da nismo naveli da funkcija poruka razreda vozilo je virtualna ( virtualan), tada bi prevodilac statički (tvrdo) vezao bilo koji poziv na metodu na objektu pokazivača monocikl s metodom poruka razreda vozilo, jer u opisu smo rekli da varijabla monocikl pokazuje na objekt klase vozilo. Oni. proizvela bi rano vezanje. Izlaz takvog programa bio bi ispis četiri retka "Vozilo". No korištenjem virtualne funkcije u razredu dobili smo nešto drugačije rezultate.

Pri radu s objektima klase automobil I čamac vlastite metode nazivaju se poruka, što se potvrđuje prikazom odgovarajućih poruka na ekranu. Na satu kamion nema metode poruka, zbog toga se poziva odgovarajuća metoda osnovne klase vozilo.

Vrlo često se klasa koja sadrži virtualnu metodu naziva polimorfna klasa. Najvažnija razlika je u tome što polimorfne klase mogu rukovati objektima čiji je tip nepoznat u vrijeme kompajliranja. Funkcije opisane kao virtualne u osnovnoj klasi mogu se modificirati u izvedenim klasama, a povezivanje se neće dogoditi u fazi kompilacije (ono što se naziva rano povezivanje), već u trenutku pristupa ovu metodu(kasni uvez).

Virtualne metode opisuju se pomoću ključne riječi virtualan u osnovnoj klasi. To znači da se u izvedenoj klasi ova metoda može nadjačati metodom koja je prikladnija za tu izvedenu klasu. Jednom kada se deklarira virtualnom u osnovnoj klasi, metoda će ostati virtualna za sve izvedene klase. Ako virtualna metoda nije poništena u izvedenoj klasi, tada će se metoda s tim imenom, kada se pozove, naći u hijerarhiji klase (tj. u osnovnoj klasi).

Zadnja stvar koju treba spomenuti kada govorimo virtualne funkcije, je koncept apstraktnih klasa. Ali to ćemo pogledati u sljedećem koraku.

2

Recimo da nije bilo funkcije Hello, a mi jednostavno pozivamo ob.display u osnovi, a zatim poziva funkciju prikaza klase B, a ne klase A.

Poziv za display() jednom postavlja kompilator na verziju definiranu u osnovnoj klasi. To se zove razrješenje poziva statičke funkcije ili statičko vezanje - poziv funkcije se predaje prije nego što se program izvrši. Ovo se ponekad naziva i ranim vezanjem jer je funkcija display() specificirana tijekom kompilacije programa.

Sada, kako može pozvati funkciju prikaza izvedene klase bez korištenja virtualne ključne riječi (kasno vezanje) prije funkcije prikaza u osnovnoj klasi?

Sada u ovom programu prosljeđivanje objekta kao poziva prema vrijednosti, poziva prema pokazivaču i poziva prema referenci na Hello funkciju radi dobro. Sada, ako koristimo polimorfizam i želimo prikazati funkciju članicu izvedene klase ako je pozvana, moramo dodati ključna riječ virtualni prije funkcije mapiranja baze podataka. Ako proslijedite vrijednost objekta kada pozivate pomoću pokazivača i pozivate po referenci, to je poziv funkcije u izvedenoj klasi, ali ako prosljeđujete objekt po vrijednosti, to nije slučaj zašto je to tako?>

Klasa A ( public: void display(); // virtualni void display() ( cout<< "Hey from A" <display() ) int main() ( B obj; Pozdrav(obj); // obj //&ob return 0; )

  • 2 odgovora
  • Sortiranje:

    Aktivnost

4

kako sada može pozvati funkciju prikaza izvedene klase bez korištenja virtualne ključne riječi (kasno vezanje) prije funkcije prikaza u osnovnoj klasi?

Nevirtualnu funkciju prevoditelj jednostavno rješava prema statičkom tipu objekta (ili reference ili pokazivača) koji poziva. Dakle, dati objekt izvedenog tipa, kao i referenca na njegov podobjekt:

Bb; A&a=b;

dobit ćete različite rezultate pozivanjem nevirtualne funkcije:

B.display(); // poziva se kao B a.display(); // naziva se A

Ako znate pravi tip, možete odrediti kako želite nazvati ovu verziju:

Static_cast (a).display(); // naziva se B

ali ono što bi bilo užasno pogrešno je da objekt a na koji se odnosi nema tip B .

Sada, ako koristimo polimorfizam i želimo mapirati funkciju članicu izvedene klase ako je pozvana, moramo dodati virtualnu ključnu riječ prije funkcije mapiranja u bazi.

Ispraviti. Ako funkciju učinite virtualnom, ona će se razriješiti tijekom izvođenja u skladu s dinamičkim tipom objekta, čak i ako koristite drugu referentnu vrstu ili pokazivač za pristup. Dakle, oba gornja primjera bi to nazvala B.

Ako proslijedimo vrijednost objekta pozivom po pokazivaču i pozivom po referenci, ona poziva funkciju u izvedenoj klasi, ali ako proslijedimo objekt po vrijednosti, to ne znači zašto je to slučaj?

Ako ga prođete po vrijednosti, onda vi rezanje na kriške njegovo: kopiranje samo A dijela objekta da bi se napravio novi objekt tipa A. Dakle, bez obzira na to je li ova funkcija virtualna, njezino pozivanje na ovom objektu odabrat će verziju A, budući da je to A i ništa osim A.

0

wikipedia kaže da se rezanje objekta događa jer nema prostora za pohranjivanje dodatnih izvedenih članova klase u superklasi, pa se reže. Zašto se rezanje objekata ne događa ako ga proslijeđujemo referencom ili pokazivačem? Zašto superklasa dobiva dodatni prostor za pohranu? -

---.NET sklopovi --- Kasno uvezivanje

Kasni uvez je tehnologija koja vam omogućuje instanciranje određenog tipa i pozivanje njegovih članova tijekom izvođenja bez tvrdog kodiranja njegovog postojanja tijekom kompajliranja. Prilikom izrade aplikacije koja zahtijeva kasno vezanje na tip iz nekog vanjskog sklopa, nema razloga za dodavanje reference na taj sklop, pa stoga nije izravno navedeno u manifestu pozivnog koda.

Na prvi pogled nije lako uočiti prednosti kasnog uvezivanja. Doista, ako je moguće izvršiti rano vezanje na objektu (na primjer, dodavanje reference sklopa i postavljanje tipa pomoću ključne riječi new), svakako biste to trebali učiniti. Jedan od najuvjerljivijih razloga je taj što rano vezanje omogućuje uočavanje pogrešaka u vrijeme kompajliranja, a ne u vrijeme izvođenja. Međutim, kasno vezanje također igra važnu ulogu u svakoj proširivoj aplikaciji koju izradite.

Klasa System.Activator

Klasni sustav. Aktivator (definiran u sklopu mscorlib.dll) igra ključnu ulogu u procesu kasnog povezivanja u .NET-u. U trenutnom primjeru, jedino što je za sada zanimljivo je Metoda Activator.CreateInstance()., koji će vam omogućiti instanciranje kasno vezanog tipa. Ova metoda ima nekoliko preopterećenja i stoga pruža prilično visoku fleksibilnost. U svojoj najjednostavnijoj verziji, CreateInstance() uzima važeći Type objekt koji opisuje entitet koji bi trebao biti dodijeljen memoriji u hodu.

Da vidimo što to znači, kreirajmo novi projekt tipa Console Application, uvezimo prostore imena System.I0 i System.Reflection u njega pomoću ključne riječi using, a zatim modificirajmo klasu Program kao što je prikazano u nastavku:

Korištenje sustava; koristeći System.Reflection; koristeći System.IO; namespace ConsoleApplication1 ( class Program ( static void Main() ( Assembly ass = null; try ( ass = Assembly.Load("fontinfo"); ) catch (FileNotFoundException ex) ( Console.WriteLine(ex.Message); ) if (ass != null) CreateBinding(ass); Console.ReadLine(); ) static void CreateBinding(Assembly a) ( try ( Type color1 = a.GetType("FontColor"); // Koristi objekt kasnog vezivanja obj = Activator.CreateInstance( boja1); Console.WriteLine("Object created!"); ) catch (Exception ex) ( Console.WriteLine(ex.Message); ) ) ) )

Prije nego što možete pokrenuti ovu aplikaciju, morate ručno kopirati sklop fontinfo.dll u poddirektorij bin\Debug unutar direktorija ove nove aplikacije koristeći Windows Explorer. Stvar je u tome što se ovdje poziva metoda Assembly.Load(), što znači da će CLR samo ispitati mapu klijenta (ako želite, možete upotrijebiti metodu Assembly.LoadFrom() i navesti punu stazu do sklopa, ali u ovom slučaju nema potrebe za ovim).

Ovaj paragraf, unatoč svojoj kratkoći, vrlo je važan - gotovo sav stručan programiranje u Javi se temelji na korištenju polimorfizma. Ujedno je ova tema studentima jedna od najtežih za razumijevanje. Stoga se preporuča nekoliko puta pažljivo pročitati ovaj odlomak.

Metode klase označene su statičkim modifikatorom s razlogom - za njih, prilikom prevođenja programskog koda, statičko povezivanje. To znači da u kontekstu koje klase je naziv metode naveden u izvornom kodu, poveznica se postavlja na metodu te klase u prevedenom kodu. Odnosno, provodi se vezanje naziva metode na mjestu poziva s izvršnim kodom ovu metodu. Ponekad statičko povezivanje nazvao rano uvezivanje, budući da se javlja u fazi kompilacije programa. Statičko povezivanje u Javi se koristi u još jednom slučaju - kada je klasa deklarirana s finalnim modifikatorom ("final", "final").

Objektne metode u Javi su dinamičke, odnosno podložne su dinamičko povezivanje. Javlja se u fazi izvođenja programa izravno tijekom poziva metode, a u fazi pisanja ove metode nije unaprijed poznato iz koje će klase biti izvršen poziv. To je određeno tipom objekta za koji ovaj kod radi - kojoj klasi objekt pripada, iz koje je klase metoda pozvana. Ovo se vezanje događa dugo nakon što je kod metode kompajliran. Stoga se ova vrsta uveza često naziva kasni uvez.

Programski kod temeljen na pozivu dinamičke metode, ima svojstvo polimorfizam– isti kod radi različito ovisno o vrsti objekta koji ga poziva, ali radi iste stvari na razini apstrakcije koja se odnosi na izvorni kod metode.

Kako bismo objasnili ove riječi, koje nisu baš jasne na prvo čitanje, razmotrimo primjer iz prethodnog paragrafa - rad metode moveTo. Neiskusni programeri misle da ovu metodu treba nadjačati u svakoj klasi potomku. To se zapravo može učiniti i sve će raditi ispravno. Ali takav će kod biti izuzetno suvišan - nakon svega, implementacija metode bit će u svim klasama potomcima Lik točno isto:

public void moveTo(int x, int y)( hide(); this.x=x; this.y=y; show(); );

Štoviše, ovaj slučaj ne iskorištava polimorfizam. Dakle, nećemo to učiniti.

Također je često zagonetno zašto apstraktna klasa Lik napišite implementaciju ove metode. Uostalom, pozivi na hide i show metode koje se u njemu koriste, na prvi bi pogled trebali biti pozivi apstraktne metode– odnosno, čini se da uopće ne mogu raditi!

Ali metode skrivanja i prikazivanja su dinamične, što, kao što već znamo, znači da se povezivanje naziva metode i njenog izvršnog koda vrši u fazi izvršavanja programa. Stoga, činjenica da su te metode navedene u kontekstu klase Lik, ne znači da će biti pozvani iz razreda Lik! Štoviše, možete jamčiti da metode skrivanja i prikazivanja nikada neće biti pozvane iz ove klase. Neka imamo varijable dot1 tipa Dot i circle1 tipa Circle i njima su dodijeljene reference na objekte odgovarajućih tipova. Pogledajmo kako se ponašaju pozivi dot1.moveTo(x1,y1) i circle1.moveTo(x2,y2).

Prilikom poziva dot1.moveTo(x1,y1) dolazi do poziva klase Lik metoda moveTo. Doista, ova metoda u klasi Dot nije nadjačana, što znači da je naslijeđena od Lik. U metodi moveTo, prva izjava je poziv metode dinamičkog skrivanja. Implementacija ove metode preuzeta je iz klase čiji je objekt dot1 koji poziva ovu metodu instanca. Odnosno iz klase Dot. Dakle, poanta je skrivena. Tada se mijenjaju koordinate objekta, nakon čega se on poziva dinamička metoda pokazati. Implementacija ove metode preuzeta je iz klase čiji je objekt dot1 koji poziva ovu metodu instanca. Odnosno iz klase Dot. Stoga se na novoj lokaciji prikazuje točka.

Za pozivanje circle1.moveTo(x2,y2) sve je apsolutno slično - dinamičke metode hide i show pozivaju se iz klase čiji je objekt circle1 instanca, odnosno iz klase Circle. Dakle, krug je skriven na starom mjestu, a prikazan na novom.

To jest, ako je objekt točka, točka se pomiče. A ako je objekt krug, krug se kreće. Štoviše, ako jednog dana netko napiše, na primjer, klasu Ellipse koja je potomak Circle i stvori objekt Elipsa elipsa=nova elipsa(…), tada će pozivanje ellipse.moveTo(…) premjestiti elipsu na novu lokaciju. A to će se dogoditi u skladu s načinom na koji su metode hide i show implementirane u klasi Ellipse. Imajte na umu da će kompajlirani polimorfni kod klase raditi prije mnogo vremena Lik. Polimorfizam je osiguran činjenicom da se reference na te metode ne stavljaju u kod metode moveTo u vrijeme kompajliranja - konfiguriraju se na metode s takvim imenima iz klase pozivajućeg objekta odmah u trenutku poziva metode moveTo.

Postoje dvije vrste objektno orijentiranih programskih jezika: dinamičke metode– zapravo dinamičan i virtualan. Prema principu rada, oni su potpuno slični i razlikuju se samo u značajkama implementacije. Poziv virtualne metode brže. Pozivanje dinamičkih je sporije, ali servisni stol dinamičke metode(DMT – Dynamic Methods Table) zauzima nešto manje memorije od tablice virtualne metode(VMT – Virtualna tablica metoda).

Možda se čini kao izazov dinamičke metode nije vremenski učinkovit zbog duljine vremena potrebnog za traženje imena. Zapravo, tijekom poziva se ne vrši traženje imena, već se koristi mnogo brži mehanizam koji koristi spomenutu tablicu virtualnih (dinamičkih) metoda. Ali nećemo se zadržavati na specifičnostima implementacije ovih tablica, budući da u Javi ne postoji razlika između ovih vrsta metoda.

6.8. Objekt osnovne klase

Klasa Object je osnovna klasa za sve Java klase. Stoga su sva njegova polja i metode naslijeđene i sadržane u svim klasama. Klasa Object sadrži sljedeće metode:

  • public Boolean equals(Object obj)– vraća true u slučaju kada su vrijednosti objekta iz kojeg je metoda pozvana i objekta koji je prošao kroz obj referencu na popisu parametara jednake. Ako objekti nisu jednaki, vraća se false. U klasi Objekt, jednakost se tretira kao referentna jednakost i ekvivalentna je operatoru usporedbe "==" . Ali u potomcima se ova metoda može nadjačati i može usporediti objekte prema njihovom sadržaju. Na primjer, to se događa za objekte numeričkih klasa ljuske. Ovo se lako može provjeriti kodom poput ovog:

    Dupli d1=1,0,d2=1,0; System.out.println("d1==d2 ="+(d1==d2)); System.out.println("d1.equals(d2) ="+(d1.equals(d2)));

    Prvi redak izlaza će dati d1==d2 =false, a drugi će dati d1. jednako(d2)=točno

  • javni int hashCode()– pitanja hash kod objekt. Hash kod je uvjetno jedinstveni numerički identifikator povezan s elementom. Iz sigurnosnih razloga, ne možete dati adresu objekta aplikacijskom programu. Stoga u Javi hash kod zamjenjuje adresu objekta u slučajevima kada je za neku svrhu potrebno pohraniti tablice adresa objekata.
  • zaštićeni klon objekta() baca CloneNotSupportedException – metoda kopira objekt i vraća vezu na stvoreni klon (duplikat) objekta. U potomcima klase Object potrebno ju je redefinirati, te također naznačiti da klasa implementira sučelje Clonable. Pokušaj pozivanja metode iz objekta koji se ne može klonirati uzrokuje podizanje iznimke CloneNotSupportedException("Klon nije podržan"). O sučeljima i iznimnim situacijama bit će riječi kasnije.

    Postoje dvije vrste kloniranja: plitko (plitko), kada se vrijednosti polja originalnog objekta kopiraju jedan na jedan u klon, i duboko (duboko), u kojem se stvaraju novi objekti za polja referentni tip, kloniranje objekata na koje se izvorna polja odnose. U plitkom kloniranju, i original i klon će referirati na iste objekte. Ako objekt ima samo polja primitivni tipovi, nema razlike između plitkog i dubokog kloniranja. Implementaciju kloniranja provodi programer koji razvija klasu; ne postoji mehanizam automatskog kloniranja. U fazi razvoja klase trebali biste odlučiti koju opciju kloniranja odabrati. U velikoj većini slučajeva to je potrebno duboko kloniranje.

  • javna konačna klasa getClass()– vraća referencu na metaobjekt klase tipa. Uz njegovu pomoć možete dobiti informacije o klasi kojoj objekt pripada i pozvati njegove metode klase i polja klase.
  • zaštićena praznina finalize() baca Throwable – poziva se prije nego što je objekt uništen. Mora se nadjačati u onim potomcima Objekta u kojima je potrebno izvršiti neke pomoćne radnje prije uništavanja objekta (zatvoriti datoteku, prikazati poruku, nacrtati nešto na ekranu, itd.). Ova metoda je detaljnije opisana u odgovarajućem odlomku.
  • javni niz u niz ()– vraća nizovnu reprezentaciju objekta (što je moguće adekvatnije). U klasi Object, ova metoda proizvodi niz potpuno kvalificiranog naziva objekta (s nazivom paketa), iza kojeg slijedi znak "@", a zatim heksadecimalni hash kod objekta. Većina standardnih klasa nadjačava ovu metodu. Za numeričke klase vraća se string reprezentacija broja, za string klase – sadržaj niza, za znakovne klase – sam znak (a ne string reprezentacija njegovog koda!). Na primjer, sljedeći isječak koda

    Objekt obj=novi Objekt(); System.out.println(" obj.toString() daje "+obj.toString()); Double d=novo Double(1.0); System.out.println(" d.toString() daje "+d.toString()); Znak c="A"; System.out.println("c.toString() daje "+c.toString());

    dat će zaključak

    obj.toString() daje java.lang.Object@fa9cf d.toString() daje 1.0 c.toString() daje A

Postoje i metode obavijestiti(), obavijestiSve(), te nekoliko preopterećenih varijanti metode čekati, dizajniran za rad s nitima. O njima se govori u odjeljku o nitima.

6.9. Dizajneri. Zadržane riječi super i ovo. Inicijalizacijski blokovi

Kao što je već spomenuto, objekti u Javi se kreiraju korištenjem rezervirane riječi new, nakon koje slijedi konstruktor - posebna podrutina koja stvara objekt i inicijalizira polja stvorenog objekta. Povratni tip nije naveden za njega i nije ni metoda objekta (pozvana kroz naziv klase kada objekt još ne postoji) niti metoda klase (objekt i njegova polja dostupni su u konstruktoru putem ove reference) . Zapravo, konstruktor, u kombinaciji s new operatorom, vraća referencu na objekt koji se stvara i može se smatrati posebnom vrstom metode koja kombinira značajke metode klase i metode objekta.

Ako objekt ne zahtijeva nikakvu dodatnu inicijalizaciju kada je kreiran, možete koristiti konstruktor, koji je prisutan prema zadanim postavkama za svaku klasu. Ovo je naziv klase, iza kojeg slijede prazne zagrade - bez popisa parametara. Nema potrebe navesti takav konstruktor prilikom razvoja klase; on je prisutan automatski.

Ako je potrebna inicijalizacija, obično se koriste konstruktori s popisom parametara. Pogledali smo primjere takvih konstruktora za klase Dot i Circle. Klase Dot i Circle naslijeđene su od apstraktne klase, u kojem nije bilo konstruktora. Ako postoji nasljeđivanje od neapstraktne klase, to jest one koja već ima konstruktor (čak i zadani konstruktor), pojavljuju se neke specifičnosti. Prva izjava u konstruktoru mora biti poziv konstruktoru iz nadklase. Ali to se ne radi kroz naziv ove klase, već korištenjem rezervirane riječi super(od "superclass"), nakon čega slijedi popis parametara potrebnih za baka i djed konstruktor. Ovaj konstruktor inicijalizira podatkovna polja koja su naslijeđena od superklase (uključujući od bilo kojih ranijih predaka). Na primjer, napišimo klasu FilledCircle - potomak od Circle, čija će instanca biti nacrtana kao krug u boji.

paket java_gui_example; import java.awt.*; javna klasa FilledCircle extends Circle( /** Stvara novu instancu FilledCircle */ public FilledCircle(Graphics g,Color bgColor, int r,Color color) ( super(g,bgColor,r); this.color=color; ) public void show())( Color oldC=graphics.getColor(); graphics.setColor(color); graphics.setXORMode(bgColor); graphics.fillOval(x,y,size, size); graphics.setColor(oldC); graphics . setPaintMode(); ) public void hide())( Color oldC=graphics.getColor(); graphics.setColor(color); graphics.setXORMode(bgColor); graphics.fillOval(x,y,size,size); grafika .setColor (stariC); graphics.setPaintMode(); ))

Općenito, logika za stvaranje složenih objekata: prvo se kreira i inicijalizira nadređeni dio objekta, počevši od dijela naslijeđenog iz klase Object, pa dalje po hijerarhiji, završavajući s dijelom koji pripada samoj klasi. To je razlog zašto je obično prva izjava konstruktora poziv baki i djedu konstruktoru super ( popis parametara), budući da pristup neinicijaliziranom dijelu objekta koji pripada nadređenoj klasi može dovesti do nepredvidivih posljedica.

U ovom razredu koristimo napredniji način crtanja i “skrivanja” oblika u odnosu na prethodne razrede. Temelji se na XOR (exclusive or) načinu crtanja. Ovaj način se postavlja pomoću metode setXORMode. U tom slučaju ponovljeni ispis figure na isto mjesto dovodi do vraćanja izvorne slike u izlazno područje. Prijelaz na normalni način slikanja provodi se metodom setPaintMode.

Vrlo često se koriste u konstruktorima