Uvod u programiranje

Maja Čić


Blokovi, instance


Objektno orijentirano programiranje
je pokušaj približavanja modela programa načinu ljudskog razmišljanja.
Kod starog načina programiranja, programer je morao pronaći računalnu zadaću koju treba izvršiti da bi se riješilo neki problem. Programiranje se zatim sastojalo od pronalaženja niza naredbi koje bi izvršile taj zadatak.
U osnovi objektno orijentiranog programiranja, umjesto zadaća nalazimo objekte - jedinice koje imaju svoje ponašanje, drže podatke i mogu međusobno djelovati. Programiranje se sastoji od oblikovanja skupa objekata koji na neki način opisuju problem koji treba riješiti. Softverski objekti u programu predstavljaju stvarne ili zamišljene jedinice u području problema. Na ovaj način razvoj programa trebao bi biti prirodniji i stoga lakši za postavljanje i razumijevanje.

Do neke granice, objektno orijentirano programiranje je samo promjena točke gledanja, u okvirima standardnog programiranja objekt se može promatrati kao skup varijabli i nekih potprograma za upravljanje tim varijablama. Zapravo, moguće je koristiti objektno orijentirano programiranje u bilo kojem programskom jeziku. Naravno postoji velika razlika između programskih jezika u kojima je objektno orijentirano programiranje moguće i onih koji ga uistinu aktivno podržavaju. Objektno orijentirani programski jezici poput Jave imaju niz mogućnosti koje ih čine različitim od standardnih programskih jezika. Ispravno razmišljanje je osnova za korištenje tih mogućnosti.


Objekti su usko vezani uz klase. Potrebno je najprije razjasniti razlike između objekata i klasa. Klase, zapravo njihovi nestatički dijelovi opisuju objekte. Uobičajeno je reći da objekti pripadaju klasama. Sa programerskog gledišta natočnije bi bilo reći da se klase koriste za opisivanje objekata. Nestatički dijelovi klasa određuju koje će varijable i potprogrami biti sadržani u objektima. Ovo je ujedno i dio objašnjenja po čemu se objekti razlikuju od klasa: objekti se stvaraju i uništavaju tijekom izvođenja programa, a korištenjem jedne klase može biti stvoren neograničen broj objekata. Primjer klase koja se može koristiti za spremanje podataka o korisniku programa:

	class KorisnikoviPodaci {
		static String ime;
		static int godine;
	}

U programu koji koristi ovu klasu postoji samo jedan primjerak svih varijabli u klasi: KorisnikoviPodaci.ime i KorisnikoviPodaci.godine. Klasa KorisnikoviPodaci i podaci koje sadržava postoje samo dok se program izodi.

Sličan primjer, s nestatičkim varijablama:

	class PodaciIgraca {
		String ime;
		int godine;
	}

U ovom slučaju nema varijabli PodaciIgraca.ime i PodaciIgraca.godine, jer ime i godine nisu statički članovi klase PodaciIgraca. Dakle, u klasi nema ništa osim mogućnosti stvaranja objekata. To su, zapravo, ogromne mogućnosti jer je broj objekata koji se mogu stvoriti neograničen, a svaki od tih objekata ima svoje varijable ime i godine. Program može pomoću ove klase spremati podatke o svim igračima. Kad novi igrač uđe u igru, kreira se novi objekt PodaciIgraca koji predstavlja tog igrača i sadrži njegove podatke. Kad igrač napusti igru, objekt koji ga predstavlja može biti uništen. Ovakav sistem sa objektima se koristi za dinamičko opisivanje događanja u igri, što nije oguće uraditi pomoću statičkih varijabli.


Objekt koji pripada klasi naziva se instance te klase. Varijable koje taj objekt sadrži nazivaju se varijable instance. Potprogrami koje objekt sadrži nazivaju se metode instance. Na primjer, ako se gore definirana klasa PodaciIgraca koristi za kreiranje objekta, tada je taj objekt instanca klase PodaciIgraca, a ime i godine u tom su objektu varijable instance. Važno je zapamtiti da klasa objekta definira tip varijable instance; stvarni podaci sadržani su u pojedinom objektu, ne u klasi. Stoga, svaki objekt ima svoje podatke.

Aplet koji skrola poruku po web stranici može, npr., sadržavati metodu scroll(). Budući da je aplet objekt, ova metoda je metoda instance ovoga apleta. Izvorni kod metode je u klasi koja je korištenja za kreiranje apleta. Ipak ispravnije je reći da metoda instance pripada objektu a ne klasi. Nestatičke metode u klasi samo određuju koje će metode instance sadržavati objekti kreirani iz klase. scroll() metode u dva različita apleta rade istu stvar, tj. obje skrolaju poruke po ekranu. Stvarna razlika tih dviju metoda je u porukama koje skroliraju, one se mogu razlikovati. Dakle, metode određuju vrstu ponašanja objekata, ali to ponašanje se razlikuje između objekata po vrijednostima varijabli instance.

Očito je da su statički i nestatički dijelovi klase vrlo različite stvari i sa vrlo različitim namjenama. Mnogo klasa sadrži isključivo statičke ili nestatičke članove, iako je moguće miješanje statičkih i nestatičkih članova u jednoj klasi. Statičke varijable i potprogrami u klasama se zovu varijable klase, odnosno metode klase, jer pripadaju klasi a ne instancama te klase.


Razmotrimo pojednostavljeni primjer klase Student koja može poslužiti za pohranu podataka o studentima:

	class Student {
		String name;			// ime studenta
		double test1, test2, test3;	// ocjene na tri testa
		double getAvarage() {		// izračunava prosječnu ocjenu testova
			return (test1 + test2 + test3) / 3;
		}
	}  // kraj klase Student.
Ni jedan od članova ove klase nije deklariran kao static, dakle smisao klase je samo stvaranje objekata. Ova definicija klase kaže da bilo koji objekt koji je instanca klase Student sadrži varijable instance ime, test1, test2, i test3, te da uključuje metodu instance getAvarage(). Imena i ocjene na testovima će imati različite vrijednosti u različitim objektima. Kad se pozove metoda getAvarage() za nekog određenog studenta, ona će izračunati srednju vrijednost njegovih ocjena na testovima. Dakle, različiti studenti imaju različite srednje ocjene, čime je pokazano da metode instance pripadaju određenom objektu, a ne klasi. U Javi, klasa je tip, slično primitivnim tipovima poput int ili boolean, pa se stoga ime klase može koristiti za određivanje vrste varijable pri deklaraciji, tipu formalnog parametra ili povratnom tipu funkcije.

Deklaracija varijable std tipa Student ima oblik:
	Student std;

Ipak, deklariranje varijable ne stvara objekt! Vrlo je važno istaknuti da u Javi nikakva varijabla ne može sadržavati objekt, varijabla može sadržavati samo poziv objekta. O objektima treba razmišljati kao da slobodno lebde negdje u memoriji računala. Zapravo, postoji dio memorije nazvan heap u koji se smještaju objekti. Umjesto da sadržava objekt, varijabla sadrži samo podatke neophodne za pronalaženje objekta u memoriji. Ta informacija zove se poziv (reference) ili pokazivač (pointer) na objekt. Zapravo, poziv na objekt je adresa memorijske lokacije u kojoj je spremljen objekt. Pri korištenju varijabli tipa klase računalo koristi poziv u varijabli za pronalaženje stvarnog objekta. Objekti se stvaraju korištenjem operatora new, koji stvara objekt i vraća poziv na objekt. Na primjer, ako je std varijabla tipa Student, izraz:

	std = new Student();
bi stvorio novi objekt koji je instanca klase Student, i spremio bi poziv na taj objekt u varijablu std.

Vrijednost varijable je poziv na objekt, ne sam objekt. Stoga nije točna izjava da je objekt vrijednost varijable std. Jednako tome nije istinita ni izjava da je objekt spremljen u varijabli std. Pravilna izjava bi bila: "varijabla std pokazuje na objekt". Recimo da varijabla std pokazuje na objekt koji pripada klasi Student. Taj objekt ima varijable instance ime, test1, test2, i test3. Ove varijable instance pozivaju se kao: std.ime, std.test1, std.test2, i std.test3.

Razmotrimo program koji uključuje sljedeći kod:
	System.out.println("Dobar dan, "  +  std.ime 
		+ ".  Tvoje ocjene na testu su:");
	System.out.println(std.test1);
	System.out.println(std.test2);
	System.out.println(std.test3);

Ovaj kod bi na izlazu dao ime i ocjene iz objekta na koji se std odnosi. Slično tome std može poslužiti za poziv metode instance getAvarage() iz objekta izrazom: std.getAvarage(). Izraz koji ispisuje srednju ocjenu bi izgledao ovako:

	System.out.println( "Srednja ocjena je " + std.getAvarage() );

Općenito, varijablu std.name može se se koristiti bilo gdje gdje je dozvoljena varijabla tipa String (u izrazima, moguće joj je pridijeliti vrijednost, za pozivanje potprograma iz klase String). Moguće je da varijable poput std, kojima je tip određen klasom, ne ukazuju ni na jedan objekt. Kažemo da takve varijable sadržavaju null reference. Null reference u Javi može biti zapisan kao "null". Primjer pridjeljivanja null reference poziva varijabli std:

	std = null;

Ako je vrijednost varijable null, nije dozvoljeno pozivanje varijabli ili metoda instance kroz tu varijablu, jer nema objekta, pa ni varijabli koje bi se moglo pozvati. U slučaju da se u programu pokuša pozvati null reference poput ovog, javlja se pogreška null pointer exception. Primjer niza naredbi koje rade s objektima:

 
	Student std, std1, std2, std3;	// deklariranje četiriju varijabli tipa Student
	std = new Student();		// kreiranje novog objekta klase Student
                               		// i spremanje poziva na njega u varijablu std
	std1 = new Student();		// kreiranje drugog objekta
	std2 = std1;			// kopiranje vrijednosti poziva iz std1 u std2
	std3 = null;			// spremanje null reference u varijablu std3
	std.name = "John Smith";	// postavlja vrijednost varijable instance
	std1.name = "Mary Jones";	// postavlja vrijednost varijable instance
   					// ostale varijable instanci imaju početne vrijednosti nula


Nakon izvođenja ovih naredbi stanje u memoriji računal izgleda ovako: 



Slika prikazuje varijable kao male kutijice s napisanim imenima varijabli. Objekti su prikazani kao kutije sa zaobljenim rubovima. Vrijednost varijable koja sadržava poziv na objekt prikazana je kao strelica prema objektu. Varijabla std3 s vrijednosti null ne pokazuje nigdje, a strelice iz std1 i std2 pokazuju na isti objekt. Ovo nas upućuje na važnu činjenicu:

kad se vrijednost varijable jednog objekta pridijeli drugoj,
kopira se samo poziv,
objekt na koji se taj poziv odnosi se ne kopira.

Nakon izvršenog pridjeljivanja "std2 = std1;", nije kreiran novi objekt, umjesto toga std2 pokazuje na isti objekt kao i std1. Tako se, na primjer, varijable std1.name i std2.name odnose na istu varijablu, varijablu instance u objektu na koji se odnose i std1 i std2.

Jednakost i nejednakost objekata je moguće testirati korištenjem == i != operatora, ali je značenje tih operatora drugačije nego inače. Izrazom "if (std1 == std2)" ispituje se da li su vrijednosti u varijablama std1 i std2 jednake, ali te vrijednosti su pozivi na objekte, a ne sami objekti. Znači, ispituje se da li se std1 i std2 odnose na isti objekt, zapravo istu memorijsku lokaciju.

Da bi se ispitala jednakost vrijednosti varijabli instance pojedinih objekata potrebno je koristiti izraze poput:

	std1.test1 == std2.test1
	std1.test2 == std2.test2
	std1.test3 == std3.test1
	std1.name.equals(std2.name)

Stringovi su objekti, i zbog toga varijable tipa String mogu sadržavati samo poziv na string, a ne i sam string. Osim toga, varijabla može sadržavati i null reference, dakle ne pokazivati ni na jedan string. Zbog ovoga ispitivanje jednakosti stringova korištenjem == operatora nije dobra ideja.

Pretpostavimo da je pozdrav varijabla tipa String i da pokazuje na string "Zdravo". Kakav rezultat daje ispitivanje izraza pozdrav == "Zdravo"? Varijabla pozdrav i string "Zdravo" pokazuju na string koji sadržava znakove Z d r a v o, ali to ne znači da su to isti objekti, oni samo sadrže iste znakove. Izrazom pozdrav.equals("Zdravo") ispituje se da li pozdrav i "Zdravo" sadrže iste znakove, dok ispitivanje izraza pozdrav == "Zdravo" daje odgovor na pitanje da li su ti znakovi spremljeni na istoj memorijskoj lokaciji.


Pretpostavimo da je varijabla koja se odnosi na objekt deklarirana kao final. To znači da se vrijednost u varijabli ne može mijenjati nakon inicijalizacije. Vrijednost spremljena u varijabli je poziv na objekt, dakle varijabla će pokazivati na isti objekt dok god postoji. Ovo ne spriječava promjene vrijednosti u objektu jer varijabla je final, a ne objekt. Sljedeći izraz je primjer toga:

	final Student stu = new Student();
	stu.name = "Josip Jozić";	// mijenja vrijednost u objektu
					// vrijednost spremljena u stu se nije promijenila

Dalje, pretpostavimo da je obj varijabla koja pokazuje na objekt, pogledajmo što se događa kad se obj proslijedi potprogramu kao formalni parametar. Vrijednost obj se pridjeljuje formalnom parametru potprograma i potprogram se izvršava. Potprogram ne može promijeniti vrijednost spremljenu u varijabli obj, jer ima samo kopiju te vrijednosti koja je još uvijek poziv na objekt. Budući da potprogram ima poziv na objekt, može mijenjati podatke spremljene u objektu. Nakon završetka potprograma, obj još uvijek pokazuje na isti objekt, ali su vrijednosti spremljene u objektu promijenjene. Pretpostavimo da je x varijabla tipa int i stu varijabla tipa Student, usporedimo:

     void neMijenjaj(int z) {             	    void mijenjaj(Student s) {
         z = 42;                               	        s.name = "Frane";
     }                                     	    }
 
linije:                     	        	linije:
     x = 17;                               	    stu.name = "Janica";
     neMijenjaj(x);                        	    mijenjaj(stu);
     System.out.println(x);                	    System.out.println(stu.name);
 
daju vrijednost izlaza 17.              	daju vrijednost izlaza "Frane".
 
Vrijednost x nije promijenjena			Vrijednost stu nije promijenjena,
potprogramom, koji odgovara:			ali stu.name je, to odgovara
 
     z = x;                                	    s = stu;
     z = 42;                               	    s.name = "Fred";
 

[ prethodna stranica | Početak | sljedeća stranica ]