• No se han encontrado resultados

JavaIInternetProgramiranje

N/A
N/A
Protected

Academic year: 2021

Share "JavaIInternetProgramiranje"

Copied!
175
0
0

Texto completo

(1)

Univerzitet u Novom Sadu Fakultet tehničkih nauka

Katedra za računarske nauke i informatiku

Branko Milosavljević

Milan Vidaković

Java i Internet programiranje

Materijal za predmet

Sintetski praktikum iz računarstva

(2)

Sadržaj

0. Namena i program kursa...1

0.1 Potrebno predznanje 1

0.2 Program kursa 2

1. Uvod u programski jezik Java...3

1.1 Java virtuelna mašina 3

1.2 Programski jezik Java 4

1.3 Osnovni koncepti 4

1.3.1 Tipovi podataka 4

1.4 Klase i objekti 5

1.5 Prevođenje i pokretanje programa 6

1.6 Reference na objekte 7

1.7 Operatori 9

1.8 Kontrola toka programa 9

1.9 Inicijalizacija objekata 10

1.10 Uništavanje objekata 10

1.11 Metode i njihovi parametri 11

1.12 Ključna reč final 13

1.13 Ključna reč static 14

1.14 Nizovi 14

1.15 Višedimenzionalni nizovi 15

1.16 Paketi, CLASSPATH i JAR arhive 17

1.16.1 Paketi 17

1.16.2 CLASSPATH 19

1.16.3 JAR arhive 19

1.16.4 Podrazumevane komponente u CLASSPATH-u 20

1.17 Zadatak: klasa Matrix 21

1.18 Nasleđivanje 22 1.19 Modifikatori pristupa 22 1.20 Redefinisanje metoda 23 1.21 Apstraktne klase 23 1.22 Interfejsi 24 1.23 Unutrašnje klase 24 1.24 Polimorfizam 25

(3)

1.25 Izuzeci 25

1.26 Klasa Object 28

1.27 Klasa String 28

1.28 Primeri nekih klasa iz standardne biblioteke 29

1.28.1 Klasa java.util.Vector 30

1.28.2 Klasa java.util.Hashtable 30

1.28.3 Klasa java.util.StringTokenizer 31

1.29 Konvencije davanja imena 31

1.30 Generisanje programske dokumentacije i javadoc 31

1.31 Zadaci: modifikacije klase Matrix 33

2. Konkurentno programiranje u Javi...35

2.1 Kreiranje programskih niti 35

2.2 Daemon i non-daemon niti 36

2.3 Primer programa sa više niti 37

2.4 Sinhronizacija niti 39

2.5 Dodatne metode za sinhronizaciju 40

2.6 Primer programa sa sinhronizacijom niti 40

2.7 Zadatak: problem pet filozofa 44

3. GUI aplikacije i JavaBeans ...45

3.1 AWT i Swing 45

3.2 Event-driven model 46

3.3 Osnovna struktura GUI aplikacije 46

3.4 Razlika u konstrukciji GUI-ja za Windows i Java aplikacije 47

3.5 Dodavanje komponenti na prozor 47

3.6 Prostorni raspored komponenti 48

3.7 Rukovanje događajima 50

3.7.1 Događaji, osluškivači i komponente 50

3.7.2 Osluškivači kao unutrašnje klase 51

3.8 Primeri korišćenja standardnih komponenti 53

3.9 Apleti 57

3.9.1 Pojam apleta 57

3.9.2 Web čitači i Java Plug-In 58

3.9.3. Apleti i komponente korisničkog interfejsa 59

3.10. Aplet i aplikacija istovremeno 60

3.11. Korisnički definisane komponente 61

3.12. JavaBeans 63

4. Mrežno programiranje u Javi ...66

4.1. Osnovne karakteristike 66

4.1.1. Pojam socket-a 66

4.2. Identifikacija čvorova mreže 67

4.3. Klasa Socket 67

4.4. Tipičan tok komunikacije – klijent strana 68

4.5. Klasa ServerSocket 69

4.6. Tipičan tok komunikacije – server strana 69

4.7. Server koji opslužuje više klijenata 70

(4)

4.9. Zadatak: klijent i server za listanje sadržaja direktorijuma 74 5. Vežba: chat aplikacija ...75

5.1. Uvodna razmatranja 76

5.2. Funkcije klijenta 76

5.3. Funkcije servera 78

6. Rad sa bazama podataka – JDBC...81

6.1. Osnovne odrednice 81

6.2. JDBC drajveri 81

6.3. Uspostavljanje veze sa bazom podataka 82

6.4. Postavljanje upita 83

6.5. DML operacije 85

6.6. Uzastopno izvršavanje istih SQL naredbi 85

6.7. Pozivanje uskladištenih procedura 87

6.8. Upravljanje transakcijama 90

6.9. Dodatak: inicijalizacija drajvera 91

7. Uvod u višeslojne klijent/server sisteme...93

7.1. Klasični klijent/server sistemi 93

7.2. WWW i Java kao platforma za klijente 94

7.3. Troslojni klijent/server sistemi 95

8. Dinamičko generisanje HTML-a i servleti...99

8.1. HTTP protokol 99

8.2. Statički i dinamički Web sadržaji 101

8.3. Servleti 101

8.3.1. Metoda init 102

8.3.2. Metoda destroy 102

8.3.3. Metoda doGet 102

8.4. Primer: elementarni servlet 102

8.5. Analiza zaglavlja HTTP zahteva 103

8.6. Konkurentni pristup servletu 104

8.7. Praćenje sesije korisnika 105

8.8. Preuzimanje podataka sa HTML formi 108

8.8.1 GET i POST zahtevi 109

8.9 Pristup bazama podataka iz servleta 111

9. Java Server Pages ...114

9.1. JSP koncept 114

9.2. Vrste dinamičkih elemenata 115

9.2.1. Izrazi 115

9.2.2. Skriptleti 115

9.2.3. Deklaracije 117

9.2.4. Direktive 117

9.3. Predefinisane promenljive 118

9.4. Skladištenje podataka i JavaBeans 119

9.5. Opseg vidljivosti JavaBean komponenti 120

(5)

10. Tehnologije distribuiranih objekata ...123

10.1. Osnovni koncepti 123

10.2. RMI 124

10.2.1. Faze u pisanju RMI programa 124

10.2.2. RMI interfejs 125

10.2.3. RMI serverski objekat 126

10.2.4. RMI registry 126

10.2.5. RMI klijent 127

10.2.6. Primer RMI programa 127

10.2.7. RMI i multithreading 129

10.3. CORBA 129

10.3.1. Osnovne odrednice 129

10.3.2. IDL 130

10.3.3. CORBA Naming Service 131

10.3.4. Proces pisanja CORBA programa 131

10.3.5. CORBA izuzeci 134 10.3.6. Pozivi unatrag 136 10.3.7. RMI i CORBA 137 10.4. Enterprise JavaBeans 137 10.4.1. Session Beans 138 10.4.2. Entity Beans 139

10.4.3. Komunikacija klijenata sa EJB komponentama 140

10.4.4. Struktura EJB komponente 140

11. Vežba: Web shop aplikacija ...144

11.1. Model podataka 144

11.2. Struktura Web sajta 145

11.3. Softverske komponente 147

11.4. Dodatna razmatranja 147

11.4.1. Rukovanje konekcijama sa bazom podataka 147

Literatura ...152 Prilozi...154

(6)

Poglavlje 0

Namena i program kursa

Kurs “Java i Internet programiranje” ima za cilj upoznavanje polaznika sa programskim jezikom Java i arhitekturom višeslojnih Internet/intranet sistema i odgovarajućim Java tehnologijama za njihovu implementaciju. Nakon završenog kursa polaznici su osposobljeni da samostalno produbljuju znanja iz prikazanih oblasti i da učestvuju u razvoju softerskih sistema koji su predmet kursa.

Kurs je predviđen za izvođenje u laboratorijskim uslovima, na odgovarajućoj računarskoj opremi. Materijal za kurs čine slajdovi koji se prikazuju u toku izlaganja, Web sajt koji sadrži primere prikazane tokom izlaganja, zadatke za vežbu, literaturu koja se preporučuje za detaljnije proučavanje materije i potreban softver koji je u javnom vlasništvu. Ovaj praktikum je, takođe, sastavni deo tog materijala.

0.1 Potrebno predznanje

Za polaznike kursa je neophodno da poseduju znanja iz sledećih oblasti:

• objektno-orijentisano programiranje: poznavanje osnovnih pojmova i konce-pata (klasa, objekat, apstrakcija, nasleđivanje, polimorfizam);

• konkurentno programiranje: pojmovi procesa i niti; raspoređivanje procesa, sinhronizacija procesa, nedeljive operacije;

• relacione baze podataka i SQL: poznavanje relacionog modela podataka, njegova implementacija u okviru sistema za upravljanje relacionim baza-ma podataka, upotreba jezika SQL za operacije nad bazom podataka; • HTML: osnovni elementi strukture HTML dokumenata, rukovanje Web

čitačima;

Za polaznike je poželjno, ali ne i obavezno, poznavanje jezika C++. Polaznici koji poznaju ovaj jezik mogu daleko brže usvajati materiju koja se izlaže na početku kursa.

0.2 Program kursa

Kurs se sastoji iz više tema koje obuhvataju obradu nove materije i vežbanja. U ovom odeljku dat je sažet pregled sadržaja kursa po odgovarajućim temama.

(7)

U prvom poglavlju, Uvod u programski jezik Java, govori se o osnovnim karakte-ristikama Jave, kao programskog jezika, i kao platforme za izvršavanje pro-grama. Upoznaje se koncept Java virtuelne mašine (JVM) i prenosivosti prevedenog Java koda. Zatim se vrši pregled osobina Jave kao programskog jezika, i obrađuju se jezičke konstrukcije.

Drugo poglavlje nosi naziv Konkurentno programiranje u Javi i donosi pregled jezičkih koncepata koji omogućavaju pisanje konkurentnih programa.

Treće poglavlje, GUI aplikacije i JavaBeans, predstavlja sažeti prikaz pisanja aplikacija i apleta sa grafičkim korisničkim interfejsom. Definiše se struktura ovakvih aplikacija, način reagovanja na događaje koje izaziva korisnik i navode primeri korišćenja brojnih komponenti za izgradnju korisničkog interfejsa. Četvrto poglavlje, Mrežno programiranje u Javi, definiše pojmove koji se koriste prilikom pisanja programa koji komuniciraju preko mreže, a zatim opisuje elemente jezika koji se koriste za pisanje ovakvih programa. Podrazumeva se rad preko TCP/IP mreže.

Peto poglavlje sadrži vežbu, čiji je cilj konstrukcija mrežne klijent/server aplikacije za chat preko TCP/IP mreže. Konstrukcija ovakvog sistema obuhvata sve prethodno obrađene teme.

U šestom poglavlju Rad sa bazama podataka – JDBC dat je uvod u metode pri-stupa i korišćenja relacionih baza podataka iz Java programa. Podrazumeva se korišćenje sistema za upravljanje relacionim bazama podataka sa kojima se komunicira preko jezika SQL.

Sedmo poglavlje, Uvod u višeslojne klijent/server sisteme, definiše okvire u kojima se nalazi materija izložena u narednim poglavljima.

Osmo poglavlje – Dinamičko generisanje HTML-a i servleti – prikazuje servlete, osnovnu Java tehnologiju za dinamičko generisanje Web sadržaja i izgradnju Web sajtova.

U narednom poglavlju, Java Server Pages, predstavljena je tehnologija za pisanje dinamičkih Web stranica koja omogućava razdvajanje zadataka Web dizajnera i programera, pojednostavljujući tako razvoj Web-orijentisanih informacionih sistema.

Deseto poglavlje, Tehnologije distribuiranih objekata, donosi sažet prikaz tehno-logija namenjenih za pisanje distribuiranih objektno-orijentisanih aplikacija dostupnih iz programskog jezika Java.

Poslednje poglavlje sadrži vežbu čiji je cilj konstrukcija Web aplikacije za elektronsko poslovanje. Zadatak je napisati softver za Web sajt koji omogućava kupovinu putem Web-a.

(8)

Poglavlje 1

Uvod u programski jezik Java

1.1 Java virtuelna mašina

Specifikacija Jave obuhvata dve relativno nezavisne celine: specifikaciju pro-gramskog jezika Java i specifikaciju Java virtuelne mašine (JVM). Specifikacija programskog jezika Java se ne razlikuje mnogo od sličnih specifikacija za druge jezike slične namene. Međutim, JVM specifikacija predstavlja novinu u odnosu na druge raširene objektno-orijentisane programske jezike opšte namene.

Naime, JVM specifikacija predstavlja, zapravo, specifikaciju platforme za izvr-šavanje Java programa u čijoj osnovi se nalazi programski model izmišljenog procesora. Programi napisani u programskom jeziku Java se prevode za ovakvu platformu za izvršavanje. Samim tim, prevedeni programi se ne mogu pokretati direktno na nekoj konkretnoj računarskoj platformi; potreban je poseban softver koji će takav prevedeni program da prilagodi konkretnoj mašini i operativnom sistemu. Zapravo, potreban je odgovarajući interpreter.

Kompanija koja je vlasnik jezika Java, Sun Microsystems, je stavila u javno vlasništvo JVM interpreter, kompajler i skup drugih razvojnih alata grupisanih u paket pod nazivom Java Development Kit (JDK). U pitanju su alati koji se pokreću iz komandne linije i nude samo osnovni set funkcija za razvoj softvera. Sun je izdao JDK paket za nekoliko različitih platformi: Windows, Solaris/ SPARC, Solaris/Intel i Linux/Intel.

Kako je Java specifikacija (i sam jezik i JVM) javno dostupna, drugi proizvođači su proizveli svoje implementacije Jave za različite platforme. Na primer, IBM nudi svoje verzije implementacije za većinu svojih hardversko/softverskih plat-formi, ali i za Linux na Intel mašinama.

Iako se najčešće programski jezik Java i Java virtuelna mašina pominju u paru, kao dve komplementarne specifikacije, nema prepreka da se Java kod prevodi i za izvršavanje na nekoj drugoj platformi (na primer, TowerJ paket generiše Windows izvršni kod). Takođe, nema prepreka da se neki drugi jezici prevode za izvršavanje u okviru Java virtuelne mašine.

(9)

Kao posledica prethodno rečenog, može se reći da je Java kombinacija programskog jezika i platforme za izvršavanje programa koja ima nekoliko važnih osobina:

• Projektovana je tako da što manje zavisi od karakteristika konkretnog računarskog sistema na kome se izvršava.

• Jednom napisan i preveden program se može pokretati na bilo kojoj platformi za koju postoji odgovarajući JVM interpreter. Dakle, preno-sivost programa je garantovana na nivou izvršnog (prevedenog) koda. • Java je interpretirani jezik, što ima odgovarajući efekat na brzinu

izvr-šavanja programa.

Proizvod prevođenja izvornog Java koda je program predviđen za rad u okviru JVM, koji se često naziva bajt-kod (byte-code).

1.2 Programski jezik Java

Iako je Java virtuelna mašina sastavni deo specifikacije, o njoj se govori veoma retko; praktično je koriste samo autori kompajlera i JVM interpretera iza konkretne računarske platforme. Sa druge strane, većina Java programera govori o drugom delu Java specifikacije, samom programskom jeziku Java, koji je i tema preostalog teksta u ovom poglavlju.

Može se reći da je Java objektno-orijentisani programski jezik opšte namene, posebno pogodan za pisanje konkurentnih, mrežnih i distribuiranih programa. Sva referentna dokumentacija za Javu nalazi se na jednom mestu – sajtu firme JavaSoft (ogranak firme Sun Microsystems) http://java.sun.com.

Knjiga Thinking in Java, (autor Bruce Eckel) se smatra za jednu od najboljih knjiga o samom jeziku, a dostupna je osim u klasičnoj štampanoj formi i u elektronskom obliku koji je besplatan na http://www.bruceeckel.com.

1.3 Osnovni koncepti

Sintaksa Jave izuzetno podseća na sintaksu jezika C++, mada nije jednaka njoj. Sintaksna pravila neće biti posebno obrađena, jer smatramo da su dovoljno očigledna iz primera koji slede.

1.3.1 Tipovi podataka

Java operiše sa dve vrste tipova podataka: primitivnim tipovima i objektima. Primitivni tipovi su tipovi koji se sreću i u drugim jezicima, npr. celobrojni tip, karakter, itd. Tabela 1.1 sadrži spisak svih primitivnih tipova sa njihovim osnovnim karakteristikama.

Primitivni tip Veličina Minimum Maksimum

boolean 1-bit - -

char 16-bit Unicode 0 Unicode 216-1

byte 8-bit -128 +127

short 16-bit -215 +215-1

int 32-bit -231 +231-1

(10)

Primitivni tip Veličina Minimum Maksimum

float 32-bit IEEE 754 IEEE 754

double 64-bit IEEE 754 IEEE 754

void - - -

Tabela 1.1. Primitivni tipovi

Iz tabele se vidi da Java raspolaže primitivnim tipovima koji su na isti način definisani i u drugim programskim jezicima. Jedini izuzetak je tip char, koji zauzima dva bajta, umesto uobičajenog jednog bajta. Radi se o tome da se tipom char može predstaviti svaki karakter definisan Unicode standardom koji definiše kodni raspored koji obuhvata praktično sve današnje jezike (uključujući indoevropske, dalekoistočne, itd). To znači da su Java programi u startu osposobljeni da rade sa višejezičnim tekstom, ili u našim uslovima, ravnopravno sa srpskom latinicom i srpskom ćirilicom.

Treba primetiti da string, kao često korišćen tip podatka, nema odgovarajući primitivni tip u Javi, slično jezicima C i C++.

1.4 Klase i objekti

Druga vrsta podataka sa kojima operiše Java program su objekti. Objekti predstavljaju osnovni koncept objektno-orijentisane paradigme u modelovanju sistema. Svaki objekat realnog sistema koga posmatramo predstavljamo odgovarajućim objektom koji je sastavni deo modela sistema. Objekte koji zajedničke osobine (ne moraju imati iste vrednosti tih osobina) možemo da opišemo klasom. U tom smislu, objekat je jedna instanca (primerak) svoje klase. Klasa, dakle, predstavlja model objekta, koji obuhvata atribute i metode.

Sledi primer jedne Java klase:

class Automobil { boolean radi; void upali() { radi = true; } void ugasi() { radi = false; } }

(Plavom bojom su navedene ključne reči jezika). Klasa ima naziv Automobil, definiše jedan atribut koji se zove radi i logičkog je tipa (boolean), i definiše dve metode koje se mogu pozvati nad objektima te klase, metode upali i ugasi.

Kreiranje objekata koji predstavljaju instance (primerke) ove klase može se obaviti na sledeći način:

Automobil a = new Automobil(); Automobil b = new Automobil();

Time su kreirana dva objekta klase Automobil, koji su nazvani a i b. Atributu radi objekta a može se pristupiti pomoću:

(11)

a poziv metoda upali i ugasi mogao bi da izgleda kao u sledećem primeru: a.upali();

b.ugasi();

Ovo do sada rečeno izuzetno podseća na C++. Neke od osobina Jave koje je bitno razlikuju u odnosu na C++ su:

• Nije moguće definisati promenljive i funkcije izvan neke klase. Samim tim, nije moguće definisati globalne promenljive, niti globalne funkcije ili procedure.

• Ne postoje odvojene deklaracija i definicija klase. Java poznaje samo definiciju klase. Prema tome, ne postoje posebni “header” fajlovi koji sadrže deklaraciju klase.

Kako Java ne dopušta postojanje bilo čega što bi postojalo izvan neke klase, postavlja se pitanje odakle počinje izvršavanje Java programa. C i C++ koriste funkciju main kao osnovnu funkciju od koje počinje izvršavanje programa. Java takođe koristi funkciju main, samo što i ta funkcija mora biti metoda neke klase (u C++ terminologiji bi se reklo “funkcija članica”). Izgled jedne klase koja sadrži metodu main, i predstavlja primer jednog izvršivog Java programa dat je u sledećem primeru:

class Hello {

public static void main(String args[]) { System.out.println(“Hello world!”); }

}

(Trenutno nije bitno zašto metoda main mora biti definisana kao public static void, ali mora biti tako.) Kompletan tekst ove klase smešten je u datoteku Hello.java. Treba obratiti pažnju na naziv ove datoteke: njena ekstenzija je obavezno .java, a ime mora biti jednako imenu klase, uključujući i razliku između velikih i malih slova. Standardna preporuka je da se svaka klasa programa smešta u posebnu datoteku. Naziv datoteke mora odgovarati nazivu klase na prethodno opisani način. Iako će neki prevodioci dopustiti smeštanje teksta više klasa u isti fajl, ta praksa se ne preporučuje. Dakle, svakoj Java klasi odgovara jedan fajl sa identičnim nazivom i ekstenzijom .java.

1.5 Prevođenje i pokretanje programa

Svaka Java klasa se može prevesti nezavisno od ostalih elemenata programa. Komanda kojom se klasa Hello iz prethodnog primera prevodi je

javac Hello.java

(Primeri za prevođenje i pokretanje opisuju korišćenje alata iz standardnog JDK paketa). Dakle, kompajler se poziva komandom javac, a kao parametri navode se imena onih datoteka koje želimo da prevedemo (može ih biti više, i možemo da koristimo džoker-znake). Treba obratiti pažnju na to da je navođenje ekstenzije datoteke obavezno, iako je ekstenzija uvek .java.

(12)

Prevođenjem datoteke Hello.java dobija se datoteka Hello.class, koja sadrži JVM bajt-kod koji pripada klasi Hello. Naziv te datoteke obavezno mora imati ekstenziju .class, i naziv mora biti jednak nazivu klase. Svaka klasa, data u odgovarajućem .java fajlu, kao rezultat prevođenja daje odgovarajući .class fajl. Treba obratiti pažnju da je kod prevođenja ovog primera neophodno pozicionirati se (u okviru DOS Prompt-a ili nekog shell-a na UNIX-u) u onaj direktorijum gde se nalazi .java fajl.

Za pokretanje ovog programa dovoljan je dobijeni Hello.class fajl. Program ćemo pozvati iz komandne linije (ponovo se moramo nalaziti u istom direktorijumu gde i .class fajl) sledećom komandom:

java Hello

Ovog puta nije dozvoljeno navođenje ekstenzije .class prilikom pokretanja. U slučaju da se tako učini dobićemo poruku o grešci.

Sada sledi primer jednog programa koji se sastoji iz dve klase: Automobil.java

class Automobil { boolean radi;

void upali() { radi = true; } void ugasi() { radi = false; } }

Test.java

class Test {

public static void main(String args[]) { Automobil a;

a = new Automobil(); a.upali();

} }

Ove dve klase su smeštene u odgovarajućim datotekama Automobil.java i Test.java. Njihovim prevođenjem dobijaju se dva .class fajla, Automobil.class i Test.class. Program se pokreće tako što se navodi ime one klase koja sadrži metodu main, što bi u ovom primeru bilo

java Test

Nema nikakve prepreke da više klasa koje čine program poseduju metodu main. Odakle će se početi sa izvršavanjem programa? To se određuje prilikom pokretanja programa, tako što se navodi ime one klase čiju metodu main želimo da pokrenemo.

1.6 Reference na objekte

Kada smo u prethodnom primeru, u metodi main, napisali: Automobil a;

a = new Automobil();

deklarisali smo promenljivu a tipa Automobil, a zatim kreirali objekat klase Automobil i vezali ga za tu promenljivu a.

(13)

Promenljiva a predstavlja, zapravo, referencu na objekat klase Automobil. Promenljiva a je lokalna promenljiva u metodi main, tako da se smešta na stek, na sličan način kako se to odvija u drugim jezicima, dok se memorija za objekat klase Automobil zauzima na heap-u programa. Slika 1.1 prikazuje tu situaciju.

objekat klase Automobil

a

heap stek

Slika 1.1. Referenca koja ukazuje na objekat

U tom smislu kaže se da je a “referenca na objekat” klase Automobil. Promenljiva a nije pointer u smislu kako ga definiše C++, jer nije dopuštena nikakva aritmetika sa ovakvim promenljivama, niti dodeljivanje proizvoljnih vrednosti. Jedina vrednost koju referenca može da sadrži je “adresa” (namerno je pod navodnicima jer to nije prava adresa u memoriji) pravilno inicijalizovanog objekta na koga ukazuje.

Sledeći primer prikazuje kreiranje dva objekta klase Automobil i inicijalizaciju referenci tako da ukazuju na odgovarajuće objekte. Reference se nalaze na steku programa, dok su objekti smešteni na heap.

Automobil a = new Automobil(); Automobil b = new Automobil();

Situacija koja se nakon ovoga nalazi u memoriji je prikazana na slici 1.2.

objekat klase Automobil a heap stek b objekat klase Automobil

Slika 1.2. Dve refence koje ukazuju na dva objekta

Ako se sada izvrši naredba b = a;

(14)

objekat klase Automobil a heap stek b objekat klase Automobil

Slika 1.3. Situacija nakon kopiranja referenci

Postavlja se pitanje šta se u ovoj situaciji dešava sa objektom na koga je ukazivala referenca b: taj objekat više nije dostupan ni na jedan način, jer je jedina mogućnost da se nekoj referenci dodeli vrednost dodela vrednosti postojeće reference (tipa b = a) ili dodela vrednosti reference na novokreiran objekat (tipa a = new ...). Kako objekat više nije dostupan, valjalo bi ga ukloniti iz memorije kako bi se izbeglo “curenje memorije”. Java ne poseduje posebnu jezičku konstrukciju kojom se memorija dealocira (poput operatora delete u jeziku C++). Za dealokaciju memorije zadužen je poseban pozadinski proces programa koji se naziva garbage collector (“skupljač đubreta”). O garbage collector-u će biti više reči u odeljku 1.10.

1.7 Operatori

Operatori koji služe za gradnju Java izraza su operatori koji su, praktično, preuzeti iz jezika C++. Možemo ih grupisati u nekoliko grupa:

• aritmetički operatori (+, -, *, /)

• relacioni operatori (==, <, >, =, !=, >=, <=) • logički operatori (&&, ||, !)

• bit-operatori (&, |, !) • operator dodele (=)

Bitna razlika u odnosu na C++ je postojanje primitivnog tipa boolean; vrednost logičkih izraza mora biti ovog tipa. To znači da su vrednosti u if ili while konstrukcijama logičkog tipa, pa nije moguće pisati

while (1) ili, još opasnije

if (a = 1)

Prioritet operatora je definisan na standardan način. Detaljna specifikacija operatora data je u specifikaciji jezika.

1.8 Kontrola toka programa

Za kontrolu toka programa na raspolaganju su standardne konstrukcije, preuzete iz jezika C++. Treba primetiti da postoje izvesne razlike između Jave i jezika C++ i u ovom slučaju, pa za detaljnije informacije treba konsultovati specifikaciju jezika. Dostupne konstrukcije su sledeće:

(15)

• if ... else • switch • for • while • do ... while • break • continue

1.9 Inicijalizacija objekata

Prilikom konstruisanja novog objekta, nakon alokacije potrebne memorije za smeštaj objekta, biće pozvana specijalna metoda namenjena za inicijalizaciju objekta nazvana konstruktor. Konstruktor obavezno ima naziv jednak nazivu klase, i nema nikakav povratni tip, čak ni void. Sledi primer klase koja ima konstruktor. class A { A() { System.out.println("konstruktor"); } }

U ovom primeru, konstruktor će biti pozvan prilikom kreiranja objekta klase A. Na primer:

A varA = new A();

je deklaracija reference varA koja ukazuje na objekat klase A, pri čemu se odmah vrši i inicijalizacija ove reference na novokreirani objekat. U trenutku kada se izvrši ovaj red (zapravo kreiranje objekta pomoću new A()), na konzoli će se ispisati

konstruktor

što je rezultat izvršavanja konstruktora.

Ukoliko se unutar definicije klase ne navede nijedan konstruktor, kompajler će sam generisati tzv. podrazumevani konstruktor koji nema parametre i telo mu je prazno.

1.10 Uništavanje objekata

U odeljku 1.6 već je bilo reči o problemu uklanjanja nedostupnih objekata iz memorije. Za taj posao zadužen je poseban pozadinski proces koji se naziva garbage collector (GC). Ovaj proces radi nezavisno od pokrenutog programa, u smislu da sam odlučuje u kom trenutku će iz memorije osloboditi koji nedostupni objekat. Pored automatske dealokacije memorije, GC je zadužen i za automatsku defragmentaciju memorije.

Za razliku od jezika C++, Java klase ne mogu imati destruktore. Destruktori su specijalne metode koje se pozivaju neposredno pre uklanjanja objekta iz memorije. Svu potrebnu dealokaciju memorije u Javi obavlja GC proces. U trenutku dealokacije podrazumeva se da je Java objekat oslobodio ostale resurse koje je koristio (otvorene datoteke, mrežne konekcije, itd). Ukoliko je

(16)

neophodno obaviti neku operaciju neposredno pre nego što GC uništi objekat, ta operacija se može implementirati u okviru specijalne metode finalize. Metoda finalize se poziva neposredno pre uništavanja objekta od strane GC-a.

Ovde je važno naglasiti da metodu finalize ne treba koristiti za oslobađanje zauzetih resursa, jer se metoda finalize ne mora pozvati! Naime, GC sam određuje kada će ukloniti objekat iz memorije, i lako se može desiti da se to nikad ne dogodi: program je pokrenut, radio je neko vreme, računar raspolaže sa dovoljno radne memorije tako da GC nije počeo sa uklanjanjem objekata, i tako sve do završetka rada programa; GC nije uklonio objekat, samim tim nije pozvao metodu finalize, i eventualno oslobađanje zauzetih resursa se nije ni desilo.

Sledi primer klase koja implementira metodu finalize (pokretanjem ovog pro-grama sa java A verovatno će se demonstrirati mogućnost da se GC nikad ne aktivira):

class A { A() {

System.out.println("Konstruktor"); }

protected void finalize() throws Throwable { System.out.println("finalized");

}

public static void main(String[] args) { A a = new A();

System.out.println("main running..."); }

}

1.11 Metode i njihovi parametri

Sintaksa definisanja metoda (u smislu navođenja njihovog imena, liste parametara i tipa rezultata) je nalik sintaksi u jeziku C++. Sledi primer jedne metode koja prima tri parametra, sa tipovima String, int i boolean, a vraća vrednost tipa void (tj. ne vraća rezultat).

void metoda(String name, int value, boolean test) { ... } Parametri metode mogu biti primitivni tipovi i reference na objekte; tip rezultata metode može biti primitivni tip ili referenca na objekat.

Često se postavlja se pitanje da li promena vrednosti parametra u okviru metode ima efekta na promenljivu koja je korišćena kao parametar nakon povratka iz metode. Posmatrajmo sledeću metodu:

void test(Automobil a) { a.radi = true;

}

U pitanju je metoda koja u okviru svog tela vrši modifikaciju svog parametra a preko metode upali. (Koristi se klasa Automobil definisana u prethodnim primerima). U slučaju da se ova metoda pozove u sledećem segmentu koda:

(17)

x.radi = false; test(x);

// da li je atribut “radi” ovde true ili false?

vrednost atributa radi objekta x biće true. Slika 1.4 ilustruje šta se zapravo desilo: na steku je kreirana referenca x na objekat klase Automobil. Zatim je vrednost atributa radi ovog objekta postavljena na false (slika a). Nakon toga pozvana je metoda test, sa referencom x kao parametrom. Parametri metoda se, slično kao i u drugim programskim jezicima, smeštaju na stek prilikom poziva metode (ovde je nebitno u kom redosledu). Tako je i referenca x iskopirana na stek još jednom (slika b). U okviru tela metode test ova druga kopija reference x se koristi kao parametar metode i preko nje se pristupa istom onom objektu na koji ukazuje i originalna referenca x. Pristup objektu se u ovom slučaju svodi na promenu vrednosti atributa radi na true (slika c). Kod vraćanja iz metode nazad, sa steka se uklanjaju parametri korišćeni prilikom poziva metode. Tako se sa steka uklanja druga kopija reference x i ostaje samo originalna referenca. Kada preko nje pristupimo atributu radi, videćemo da je on promenio vrednost (slika d). x x x radi: false a) radi: false b) x x radi: true c) x radi: true d)

Slika 1.4. Promena stanja objekta koji je parametar metode

Nakon ovog primera može se zaključiti sledeće: promene nad parametrima metode načinjene u okviru tela metode koji su reference na objekte su vidljive nakon povratka iz metode. Ili, kako se to sreće u drugim programskim jezicima, efekat je isti kao kod prenosa parametara po adresi.

Sa primitivnim tipovima stvari stoje upravo suprotno: oni se, kao parametri metoda ponašaju kao kod prenosa parametara po vrednosti. Sledi primer:

void test(int a) { a = 1;

} ...

int a = 0; test(a);

// koliko je ovde vrednost a?

Slika 1.5 prikazuje šta se dešava u ovom slučaju: deklariše se promenljiva a tipa int i odmah se inicijalizuje na vrednost 0. Lokalne promenljive primitivnog

(18)

tipa se smeštaju na stek (trenutno stanje ilustruje slika a). Zatim se poziva metoda test sa parametrom a; parametar a se smešta na stek (zapravo, njegova vrednost se kopira još jednom – slika b). U okviru metode vrednost parametra se menja u 1, pri čemu se menja druga kopija na steku (slika c). Nakon povratka iz metode, parametar se uklanja sa steka i na steku ostaje originalna vrednost promenljive a koja nije menjana (slika d).

a = 0 a = 0 a = 0 a) a = 0 a = 1 c) a = 0 d) b)

Slika 1.5. Promena vrednosti parametra metode koji je primitivnog tipa

Preklapanje metoda (method overloading) je u Javi dopušteno. Preklopljene metode su metode koje imaju isto ime, ali se razlikuju po listi parametara. Kompajler ih smatra za sasvim različite metode, bez obzira što imaju isto ime. Metode ne mogu da se razlikuju samo po tipu rezultata kojeg vraćaju. Sledi primer klase sa tri preklopljene metode:

class A {

int metoda() { ... } int metoda(int i) { ... } int metoda(String s) { ... } }

Preklapanje metoda se odnosi kako na klasične metode, tako i na konstruktore.

1.12 Ključna reč final

Ključna reč final se može naći ispred definicije atributa ili metode unutar definicije klase. Ako se nađe ispred atributa, označava atribut kome nije moguće promeniti vrednost. Drugim rečima, final atribut predstavlja konstantu. Inicijalizacija prilikom deklaracije atributa je obavezna. Primer definicije jednog final atributa bio bi:

final int size = 100;

Ključna reč final ima drugo značenje kod metoda: označava metode koje se ne mogu redefinisati prilikom nasleđivanja date klase. O nasleđivanju će više biti reči u odeljku 1.18, a o redefinsanju metoda u odeljku 1.20. Primer jedne final metode glasi:

(19)

1.13 Ključna reč static

Ključna reč static se može naći ispred definicije atributa ili metode, nezavisno od pojave ključne reči final. Kada se nađe ispred definicije atributa, označava atribut koji pripada klasi, a ne objektima (kao instancama klase). Drugim rečima, može se reći da svi objekti date klase dele istu vrednost statičkog atributa. Na primer, klasa StaticTest poseduje jedan statički atribut:

class StaticTest { static int i = 0;

static void metoda() { i++; } }

Tada će sledeći programski segment izazvati promenu vrednosti atributa i u oba objekta:

StaticTest a = new StaticTest(); StaticTest b = new StaticTest(); a.i++;

// ovde je a.i == b.i == 1

Statički atribut je pridružen klasi, a ne njenim instancama. U tom smislu, može mu se pristupiti i kada nije kreiran nijedna instanca klase. Tada se atributu pristupa tako što se navodi ime klase, pa zatim ime atributa, kao u sledećem primeru:

StaticTest.i++;

Statičke metode su metode koje ne mogu biti pozvane nad objektima-instancama klase, već nad klasom samom. U tom smislu, nije moguće pisati

a.metoda(); nego samo

StaticTest.metoda();

Statičke metode imaju pristup samo statičkim atributima klase.

Često korišćeni primer upotrebe statičkog atributa je ispisivanje na konzolu: System.out.println("Hello, world!");

pri čemu se poziva metoda println objekta out koji je statički atribut klase System (out zapravo predstavlja standardni izlaz, slično kao stdout u jeziku C).

1.14 Nizovi

Nizovi se u Javi definišu vrlo slično kao u jeziku C++. Na primer, niz čiji su elementi tipa int se definiše na sledeći način:

int[] a; ili int a[];

Ovim je samo definisana referenca na niz; niz se nakon toga mora kreirati na način sličan kreiranju objekata. Sledi primer gde se alocira niz od pet int elemenata:

(20)

Prvi element niza ima indeks nula. Postoji i način da se niz definiše, alocira memorija za njega i odmah inicijalizuje, kao u sledećem primeru:

int[] a = { 1, 2, 3, 4, 5 };

Treba voditi računa o tome da se prilikom definicije niza referenca na niz čuva na steku, dok se elementi niza čuvaju na heap-u, slično kao i objekti. Slika 1.6 ilustruje ovu situaciju za niz definisan u prethodnom primeru.

a heap

1 2 3 4 5

Slika 1.6. Inicijalizovan niz

Kada je u pitanju niz čiji su elementi primitivnog tipa, alokacija memorije za elemente niza se odvija automatski. To nije slučaj kada je u pitanju niz čiji su elementi objekti neke klase. Na primer, niz od 5 elemenata klase Automobil se definiše kao na primer:

Automobil[] parking = new Automobil[5];

Ovim je zapravo definisan niz čiji elementi su reference na objekte klase Automobil. Slika 1.7 ilustruje ovu situaciju.

parking heap

Slika 1.7. Niz objekata pri čemu objekti nisu inicijalizovani

Ovakav niz referenci na objekte se može inicijalizovati, recimo, u odgovarajućoj for petlji, kao u primeru:

for (int i = 0; i < parking.length; i++) parking[i] = new Automobil();

(Svaki niz ima definisan atribut length koji predstavlja dužinu alociranog niza). Sada će stanje u memoriji izgledati kao na slici 1.8.

parking heap

a1 a2 a3 a4 a5

Slika 1.8. Niz inicijalizovanih objekata

1.15 Višedimenzionalni nizovi

Višedimenzionalni nizovi se predstavljaju kao nizovi nizova. Sintaksa je slična jeziku C++. Na primer, definicija

(21)

int[][] a = { {1, 2, 3}, {4, 5, 6} };

će kreirati niz od dva elementa koji su reference na nizove od tri elementa tipa int. Stanje u memoriji će nakon kreiranja ovakvog niza izgledati kao na slici 1.9.

a 1 2 3 heap

4 5 6

Slika 1.9. Dvodimenzionalni niz

Višedimenzionalni niz se može kreirati na sledeći način:

int[][] a = new int[2][3];

pri čemu će se izvršiti potrebna alokacija memorije, ali ne i inicijalizacija vrednosti elemenata niza. Dvodimenzionalni niz se može kreirati i postupno, kao u sledećem primeru:

int[][] a = new int[2][];

for (int i = 0; i < a.length; i++) a[i] = new int[3];

Prilikom kreiranja višedimenzionalnog niza čiji su elementi objekti, a ne primitivnog tipa, potrebno je još izvršiti i dodatno kreiranje svakog od objekata. Na primer:

Automobil[][] parking = new Automobil[2][];

for (int i = 0; i < parking.length; i++) { parking[i] = new Automobil[3];

for (int j = 0; j < parking[i].length; i++) parking[i][j] = new Automobil();

}

Stanje u memoriji nakon kreiranja ovakvog niza izgledaće kao na slici 1.10.

parking a1 a2 a3 heap

a4 a5 a6

Slika 1.10 Višedimenzionalni niz čiji elementi su objekti

Višedimenzionalni niz objekata može se inicijalizovati odmah prilikom definicije, slično kao kod višedimenzionalnog niza primitivnih tipova. Sledi primer:

Automobil[][] a = {

{ new Automobil(), new Automobil() }, { new Automobil(), new Automobil() } };

(22)

1.16 Paketi, CLASSPATH i JAR arhive

1.16.1 Paketi

Java programi se sastoje isključivo iz klasa. Broj klasa koje čine program može biti relativno velik, pa je uvođenje nekakve organizacije u takav skup klasa neophodno. Paketi su način da se klase grupišu po nekom kriterijumu. Paketi mogu da sadrže klase ili potpakete, analogno odnosu direktorijuma i datoteka u okviru fajl-sistema.

Svaka klasa mora da pripada nekom paketu. Ako se ne navede kom paketu pripada data klasa, podrazumeva se da pripada tzv. korenskom ili implicitnom paketu. Taj korenski paket nema posebno ime. On može da sadrži klase i potpakete, koji sa svoje strane takođe mogu da sadrže klase i potpakete. Slika 1.11 prikazuje strukturu paketa nekog programa.

Slika 1.11 Struktura paketa u programu

Paketi i klase su u okviru fajl-sistema zaista i organizovani kao direktorijumi i datoteke: paketi su predstavljeni direktorijumima, a klase se nalaze u odgovarajućim datotekama.

Klasa koja se nalazi u nekom paketu (osim korenskog), mora u okviru svoje datoteke imati odgovarajuću deklaraciju, kao u sledećem primeru:

package paket1;

class Automobil { ... }

Deklaracija package se mora nalaziti na samom početku teksta datoteke, tj. nijedna druga deklaracija se ne sme nalaziti ispred nje. Datoteka Automobil.java mora biti smeštena u direktorijum paket1 koji se nalazi u korenskom direkto-rijumu aplikacije. Naziv korenskog direktorijuma nije važan, niti je važno gde se on nalazi u okviru fajl-sistema. Prevođenje klase Automobil se mora obaviti komandom:

Važno je primetiti da se komanda za prevođenje poziva iz korenskog direktorijuma projekta i da se kao parametar navodi ime .java datoteke, zajedno sa relativnom putanjom do nje. Analogno tome, klasa Tocak koja se nalazi u paketu paket3 gornjeg primera, prevela bi se komandom:

Tekst klase Tocak obavezno mora početi odgovarajućom deklaracijom:

package paket2.paket3;

(23)

Vidimo da se za separaciju imena paketa u okviru Java programa koristi tačka, a ne kosa crta ili obrnuta kosa crta.

Već je rečeno da se prilikom pokretanja programa navodi ime one klase koja sadrži metodu main. Prilikom navođenja imena ove klase mora se navesti njeno puno ime, uključujući i paket u kome se klasa nalazi. Na primer, ukoliko klasa Tocak poseduje metodu main, i želimo da odatle počne izvršavanje programa, program moramo pokrenuti pomoću sledeće komande:

Dakle, ponovo je važno sa kog mesta se poziva Java interpreter: to mora biti korenski direktorijum aplikacije. Svaka prevedena klasa se u okviru aplikacije vidi u okviru paketa čija je putanja jednaka relativnoj putanji do odgovarajućeg direktorijuma. Separator naziva paketa je, kao što je već rečeno, tačka, a ne kosa crta ili obrnuta kosa crta.

Programski jezik Java stiže sa velikim brojem klasa grupisanim u pakete. Te klase su dostupne kao i klase koje sami pišemo (čak se može dobiti i njihov izvorni kod). Recimo klasa Vector koja se nalazi u paketu java.util je u programima dostupna kao java.util.Vector. Kako bi svako pominjanje ove klase u tekstu programa zahtevalo navođenje pune putanje do nje (odnosno navođenje odgovarajućeg paketa), to bi program učinilo manje čitljivim. Zato je moguće na početku teksta klase deklarisati da se koristi ta-i-ta klasa koja se nalazi u tom-i-tom paketu. Na primer:

package paket1;

import java.util.Vector;

class Automobil { ... }

Nadalje se u tekstu klase Automobil klasa Vector koristi samo navođenjem njenog imena, bez imena paketa u kome se nalazi. Treba obratiti pažnju na to da se import deklaracija mora nalaziti između (opcione) package deklaracije i definicije klase.

Ukoliko koristimo više klasa iz istog paketa, moramo svaku od njih navesti u odgovarajućoj import deklaraciji. Drugi način je da se importuju sve klase iz datog paketa pomoću džoker-znaka *:

package paket1;

import java.util.*;

class Automobil { ... }

Ovakav način importovanja ne obuhvata i sve potpakete importovanog paketa! Niti je dozvoljeno korišćenje džoker znakova kao u primeru:

import java.util.Vec*; // nije dozvoljeno!

Klase koje se nalaze u paketu java.lang nije potrebno importovati. Odgovarajuća import deklaracija se podrazumeva.

(24)

1.16.2 CLASSPATH

S obzirom na do sada izloženo, korišćenje klase Vector iz paketa java.util bi značilo da se odgovarajuće stablo direktorijuma java\util\... koje sadrži kompajlirane klase mora kopirati unutar strukture direktorijuma svake aplikacije koju pišemo. Time se bespotrebno zauzima prostor i komplikuje održavanje softvera. Zato postoji način da se paketi sa klasama koji se koriste iz više aplikacija čuvaju na jednom mestu, a sve aplikacije će pomoću odgovara-jućeg mehanizma te klase videti kao da je struktura direktorijuma iskopirana u okviru svake aplikacije. U pitanju je mehanizam sličan korišćenju PATH pro-menljive okruženja (environment variable).

Java interpreter za ovu svrhu koristi promenljivu okruženja koja se naziva CLASSPATH. Ona sadrži listu direktorijuma u kojima treba tražiti klase koje se koriste. Na primer, ukoliko je cela java\... hijerarhija paketa smeštena u direktorijum C:\java\lib, vrednost CLASSPATH promenljive bi mogla da glasi:

CLASSPATH=C:\java\lib

čime bi sve klase smeštene po svojim paketima unutar direktorijuma C:\java\lib bile vidljive za sve Java aplikacije. Ukoliko CLASSPATH treba da sadrži više direktorijuma, oni se navode jedan za drugim, sa tačkom-zarez kao separatorom. Na primer:

CLASSPATH=C:\java\lib;D:\mojalib

Na UNIX sistemima sintaksa navođenja vrednosti CLASSPATH promenljive se unekoliko razlikuje, time što se direktorijumi razdvajaju znakom dvotačka. Ukoliko u CLASSPATH dodamo direktorijum D:\temp\korenski paket iz pretho-dnih primera, na primer komandom:

tada i naš program možemo pokrenuti sa bilo kog mesta u okviru fajl sistema, jer će klase biti vidljive preko CLASSPATH-a. Na primer, komanda:

će pokrenuti naš program iako se ne nalazimo u direktorijumu D:\temp\korenski paket.

1.16.3 JAR arhive

Distribucija biblioteka klasa smeštenih u svoje pakete nije preterano elegantna u slučaju većeg broja klasa i paketa, jer se povećava broj datoteka i direktorijuma koje treba instalirati i navesti u CLASSPATH-u. Zbog toga je omogućeno arhiviranje biblioteka u tzv. JAR arhive. U pitanju su arhive koje sadrže klase u svojim paketima arhivirane u klasičnom Zip formatu. Podrazumevana ekstenzija im je .jar (mada može biti i .zip). Ovakve arhive se mogu generisati alatkom jar koja je sastavni deo JDK paketa, ali mogu i bilo kojim drugim programom koji može da generiše Zip arhive (ekstenziju možemo sami promeniti kasnije). Sve klase iz osnovne Java biblioteke su, prilikom instalacije

(25)

JDK paketa, smeštene u datoteku %JAVA_HOME%\jre\lib\rt.jar, gde je JAVA_HOME direktorijum gde je instaliran JDK paket. Ovu datoteku možemo otvoriti, recimo, programom WinZip, kao na slici 1.12.

Slika 1.12. Arhiva rt.jar otvorena programom WinZip

Na slici vidimo da se klasa Vector (zapravo, prevedena datoteka Vector.class) u okviru arhive nalazi u direktorijumu java\util, dakle onom koji odgovara paketu u kome se nalazi klasa.

Umesto da u okviru CLASSPATH-a navodimo direktorijum u kome se nalazi raspakovan sadržaj arhive rt.jar, možemo navesti samu datoteku rt.jar (sa svojom putanjom) i dobićemo isti efekat. Na primer:

CLASSPATH=C:\jdk1.3\jre\lib\rt.jar

Dakle, CLASSPATH može da sadrži nazive direktorijuma i Zip arhiva u kojima se nalaze deljene biblioteke. Korišćenje direktorijuma u arhiva je u ovom slučaju potpuno ravnopravno.

1.16.4 Podrazumevane komponente u CLASSPATH-u

U dosadašnjim primerima je naglašavano da se prilikom pokretanja programa moramo nalaziti u korenskom direktorijumu aplikacije. To je, zapravo, posledica činjenice da se tekući direktorijum u kome se nalazimo nalazi u CLASSPATH-u kada on nije definisan, kao da je

CLASSPATH=.

gde je tačka (.) oznaka za tekući direktorijum. Ako se CLASSPATH promenljiva definiše, tekući direktorijum se mora eksplicitno navesti u CLASSPATH-u. Još jedna komponenta CLASSPATH-a se podrazumeva, a to je upravo biblioteka rt.jar o kojoj je bilo reči u prethodnom odeljku. Nju ne moramo navoditi čak ni kada definišemo promenljivu CLASSPATH. Dakle, svaki CLASSPATH uvek sadrži ovu dve komponentu:

CLASSPATH=C:\jdk1.3\jre\lib\rt.jar

Ukoliko CLASSPATH uopšte nije definisan, onda on obuhvata i tekući direktorijum, tako da se može reći da CLASSPATH u tom slučaju glasi:

(26)

CLASSPATH=.;C:\jdk1.3\jre\lib\rt.jar

Ovde treba obratiti pažnju da je ovaj podrazumevani skup komponenti CLASSPATH-a uveden tek od Java verzije 1.2. U starijim verzijama CLASSPATH nema podrazumevanih komponenti.

Iako se u ovom tekstu u primerima poziva Java kompajlera koristi standardni javac, u praksi se umesto njega često koristi IBM-ov kompajler jikes, koji je znatno brži. jikes nije deo standardne JDK instalacije i mora se instalirati posebno. Do svoje verzije 1.02 (tekuća verzija u ovom trenutku) on se ponaša kao Java 1.1 kompajler, tako da nema podrazumevanih komponenti u CLASSPATH-u. Kako je prilično nezgodno menjati sadržaj CLASSPATH promenljive naizmenično za kompajliranje jikes-om i pokretanje java-om, problem se može prevazići korišćenjem promenljive okruženja JIKESPATH koju koristi isključivo jikes. Ona ima isto značenje kao CLASSPATH do Java verzije 1.1. Dakle, ako CLASSPATH ima sadržaj:

CLASSPATH=.;D:\nekamojabibl.zip JIKESPATH bi trebalo da ima sledeći sadržaj:

JIKESPATH=.;C:\jdk1.3\jre\lib\rt.jar;D:\nekamojabibl.zip

1.17 Zadatak: klasa Matrix

Zadatak 1. Napisati klasu Matrix datu na slici 1. Implementacija metoda je ostavljena kao zadatak. Namena svake metode je opisana odgovarajućim kometarom u tekstu klase.

Napomena: u realizaciji izostaviti provere ispravnosti parametara (odgovarajuće dimenzije matrica).

class Matrix {

/* Postavlja sadržaj matrice. */

void setData(double[][] x) {...}

/* Vraća sadržaj matrice. */

int[][] getData() {...}

/* Množi sadržaj matrice objekta koji je pozvan (this) sa sadržajem matrice b (objekta koji je prosleđen kao

parametar). Rezultat množenja smešta u novi objekat koga vraća kao rezultat metode. */

Matrix multiply(Matrix b) {...}

/* Množi sadržaj dve date matrice i rezultat množenja vraća kao rezultat metode. Obratiti pažnju da je ovo statička metoda! */

static Matrix multiply(Matrix a, Matrix b){...}

/* Množi sadržaj matrice objekta koji je pozvan (this) sa sadržajem matrice b (objekta koji je prosleđen kao

parametar). Rezultat množenja se smesta u matricu objekta koji je pozvan. Metoda ne vraća nikakav rezultat! */

void multiply2(Matrix b) {...}

/* Polazna tačka programa. Služi za testiranje funkcionalnosti ostalih metoda klase. Za potrebe testiranja formirati nekoliko

(27)

višedimenzionalnih nizova. */

public static void main(String[] args) {...}

/* Sadrzaj matrice */ double[][] data; /* Dimenzije matrice */ int n, m; }

1.18 Nasleđivanje

Nasleđivanje, kao jedan od osnovnih koncepata objektno-orijentisanog pro-gramiranja, postoji i u Javi. Kada jedna klasa nasleđuje drugu, potrebno je to naglasiti u okviru teksta klase klazulom extends kao u sledećem primeru, gde klasa BorbeniAvion nasleđuje klasu Avion:

class Avion {

Krilo levo, desno; void poleti() { ... } void sleti() { ... } }

class BorbeniAvion extends Avion { Top top;

Bomba[] bombe;

void poleti() { ... } void pucaj() { ... } }

Java ne dopušta višestruko nasleđivanje (onako kako je to definisano recimo u jeziku C++). Dakle klasa može da nasledi najviše jednu klasu.

1.19 Modifikatori pristupa

U Javi postoje sledeća tri modifikatora pristupa:

public: označava da su atribut ili metoda vidljivi za sve klase u programu

protected: atribut ili metoda su vidljivi samo za klase naslednice • private: atribut ili metoda su vidljivi samo unutar svoje klase

• nespecificiran (tzv. friendly): atribut ili metoda su vidljivi sa klase iz istog paketa

Modifikatori pristupa se navode ispred definicije metode ili atributa. Sledi primer:

class Avion {

protected Krilo levo, desno; public void poleti() { ... } public void sleti() { ... } }

Na sličan način modifikatori pristupa se mogu primeniti i na celu klasu, na primer:

(28)

1.20 Redefinisanje metoda

Redefinisanje metoda (method overriding) je postupak kada klasa naslednica redefiniše telo metode nasleđene od roditeljske klase. U Javi se to specificira prostim navođenjem nove definicije metode u klasi naslednici. Sledi primer:

class A {

int metoda1() {

System.out.println("metoda1 klase A"); }

int metoda2() {

System.out.println("metoda2 klase A"); }

}

class B extends class A { int metoda1() {

System.out.println("metoda1 klase B"); }

}

U slučaju da se izvrši sledeći segment koda: A varA = new A();

B varB = new B(); varA.metoda1(); varB.metoda1(); varA.metoda2(); varB.metoda2(); na konzoli će se ispisati:

metoda1 klase A metoda1 klase B metoda2 klase A metoda2 klase A

(metoda1 je redefinisana u klasi B, tako da je promenjen ispis na konzolu, dok metoda2 nije redefinisana, pa se za klasu B preuzima implementacija metode iz klase A).

1.21 Apstraktne klase

Apstraktne klase su klase koje ne mogu imati svoje instance (objekte). Razlog za to je što je implementacija neke od metoda izostavljena. U primeru

public abstract class A {

public void metoda1() { ... } public abstract void metoda2(); private int i;

}

metoda metoda2 je proglašena za apstraktnu korišćenjem ključne reči abstract. Njena implementacija nije navedena. Samim tim klasa je apstraktna pa se i za nju to mora navesti navođenjem ključne reči abstract ispred class. Dakle iskaz poput:

(29)

nije dopušten.

1.22 Interfejsi

Interfejsi su poseban koncept u Javi: nisu u pitanju klase, ali interfejsi mogu da sadrže deklaracije apstraktnih metoda, konstanti i statičkih atributa. Sledi primer:

interface Instrument { void sviraj();

void nastimaj(); }

Vidimo da interfejsi podsećaju na apstraktne klase. Veza između klasa i interfejsa je sledeća: kaže se da klasa implementira (a ne nasleđuje) interfejs. Klasa može da implementira više interfejsa istovremeno, što nije slučaj sa nasleđivanjem. Nema prepreke da klasa koja nasleđuje drugu klasu implementira i neke interfejse. Jedan interfejs može da nasledi drugi interfejs. Sledi primer:

class Klarinet implements Instrument { void sviraj() { ... }

void nastimaj() { ... } }

Time što je klasa implementirala interfejs zapravo se obavezala da će implementirati sve njegove metode. Kompajler neće dopustiti prevođenje klase koja implementira interfejs a nije redefinisala sve njegove metode.

1.23 Unutrašnje klase

Od Java verzije 1.1 klasa može, osim atributa i metoda, da poseduje i tzv. unutrašnje klase (inner classes). Sledi primer:

class Spoljasnja { void metoda() { ... } class Unutrasnja { int metoda2() { ... } } }

Klasa Unutrasnja je, u principu, vidljiva samo unutar klase Spoljasnja, mada se to može promeniti modifikatorima pristupa na uobičajen način. Instanca unutrašnje klase se može kreirati i izvan nje, ali samo preko instance spoljašnje klase, kao u sledećem primeru:

Spoljasnja s = new Spoljasnja();

Spoljasnja.Unutrasnja u = s.new Unutrasnja();

Sledeći izraz (pokušaj konstrukcije instance inutrašnje klase bez instance spoljašnje klase) nije dozvoljen:

Spoljasnja.Unutrasnja u = new Spoljasnja.Unutrasnja(); Koncept unutrašnjih klasa se najviše koristi prilikom izgradnje grafičkog korisničkog interfejsa, o čemu će više reči biti u poglavlju 3.

(30)

1.24 Polimorfizam

Polimorfizam je koncept koji omogućava objektima da ispolje različito ponašanje, zavisno od njihove klase, bez obzira što se oni koriste kao instance nekog zajedničkog roditelja. Posmatrajmo sledeće tri klase:

abstract class Instrument { abstract void sviraj(); }

class Violina extends Instrument { void sviraj() { ... }

}

class Klarinet extends Instrument { void sviraj() { ... }

}

Dakle, primer definiše tri klase: klasa Instrument je apstraktna klasa (njena metoda sviraj je apstraktna), a klase Violina i Klarinet nasleđuju klasu Instrument i, naravno, implementiraju (zapravo, redefinišu) apstraktnu metodu. Posma-trajmo sada klasu Muzicar:

class Muzicar {

void sviraj(Instrument i) { i.sviraj();

} }

Vidimo da klasa Muzicar ima metodu sviraj koja kao parametar ima instancu klase Instrument; sa druge strane, znamo da klasa Instrument ne može imati instance, jer je apstraktna. Ova metoda će ipak biti upotrebljiva, jer se njoj kao parametar može proslediti instanca neke klase koja nasleđuje klasu Instrument – u ovom slučaju instance klasa Violina i Klarinet. Iskaz

Muzicar m = new Muzicar(); m.sviraj(new Klarinet());

će izazvati pozivanje metode sviraj klase Klarinet (iako se to nigde eksplicitno ne navodi u metodi sviraj klase Muzicar). Iskaz

m.sviraj(new Violina());

će izazvati pozivanje metode sviraj klase Violina po istom principu. Dakle, poziv metode sviraj klase Muzicar će imati različite efekte zavisno od toga koji objekat prosledimo kao parametar. Određivanje koja metoda će se pozvati se obavlja u toku izvršavanja programa.

U primeru se vidi da ovo specijalno ponašanje metoda nije ničim naglašeno u tekstu programa. Ovakav efekat se u jeziku C++ postizao korišćenjem tzv. virtuelnih funkcija članica, a u Javi je ovo podrazumevano (i jedino moguće) ponašanje. Dakle, možemo reći, u terminologiji jezika C++, da su sve metode u Javi virtuelne, pa se ta osobina ne mora naglašavati posebno u programu.

1.25 Izuzeci

Izuzeci su mehanizam za kontrolu toka programa koji se koristi za obradu grešaka nastalih u toku izvršavanja programa. Segment programskog koda za

(31)

koji smatramo da može da izazove izuzetak možemo da smestimo u tzv. try/catch blok, kao u sledećem primeru:

try {

// kod koji može da izazove izuzetak

} catch (Exception ex) {

System.out.println("Desio se izuzetak: " + ex); }

Izuzetak može biti, na primer, deljenje nulom, pristup elementu niza koji je izvan granice niza, itd. Ukoliko se prilikom izvršavanja koda koji se nalazi u try bloku desi izuzetak, tok izvršavanja programa se automatski prebacuje na početak catch bloka. Nakon izvršavanja koda u catch bloku, program dalje nastavlja rad.

U okviru catch bloka informacije o samom izuzetku koji se dogodio su dostupne preko objekta klase Exception ili neke njene naslednice. U primeru je to objekat ex.

Različite vrste izuzetaka su predstavljene različitim exception klasama, na primer: svi izuzeci prilikom izvršavanja aritmetičkih operacija (deljenje nulom, overflow, itd.) su predstavljene klasom ArithmeticException, pristup elementu čiji je indeks izvan granice niza je predstavljen klasom ArrayIndexOutOfBounds-Exception, itd. Klasa Exception je zajednički predak svim exception klasama. Jedan try blok može imati više sebi pridruženih catch blokova, kao u sledećem primeru:

try {

// kod koji može da izazove // izuzetak

}

catch (ArithmeticException ex) {

System.out.println("Deljenje nulom"); }

catch (ArrayIndexOutOfBoundsException ex) {

System.out.println("Pristup van granica niza"); }

catch (Exception ex) {

System.out.println("Svi ostali izuzeci"); }

finally {

// kod koji se izvršava u svakom slučaju

}

Kada se dogodi izuzetak, niz catch blokova se sekvencijalno obilazi i ulazi se u onaj catch blok čija klasa odgovara izuzetku koji se dogodio. Reč odgovara u ovom slučaju znači: u pitanju je klasa kojoj exception objekat pripada, ili njen predak. Kada poslednji catch blok hvata izuzetak klase Exception, to znači da će svi izuzeci biti obrađeni, jer je klasa Exception zajednički roditelj.

Blok finally se ne mora navesti. On sadrži blok koda koji će se izvršiti u svakom slučaju, desio se izuzetak ili ne.

(32)

Kao što postoje odgovarajuće klase koje opisuju različite vrste izuzetaka, moguće je definisati i nove vrste izuzetaka definicijom odgovarajuće klase. Na primer, možemo da definišemo novu vrstu izuzetka predstavljenog klasom MojException koja je data u primeru:

public class MojException extends Exception { public MojException() { super(); } public MojException(String msg) { super(msg); } }

Klasa MojException ima dva konstruktora koji pozivaju odgovarajuće konstruktore roditeljske klase Exception. Pisanje ovakvih konstruktora nije obavezno, ali je obavezno naslediti klasu Exception (ili nekog njenog potomka). Ovakav korisnički izuzetak može biti izazvan samo programski, pomoću ključne reči throw, kao u sledećem primeru:

if (errorCheck())

throw new MojException("Houston, we have a problem."); Programski kod koji sadrži ovakvu throw naredbu mora biti smešten unutar try bloka koji hvata izuzetak MojException – na to će nas naterati kompajler. Dakle, ovo bi moglo da izgleda na sledeći način:

try {

if (errorCheck())

throw new MojException("Houston, we have a problem."); } catch (MojException ex) {

System.out.println("Exception: " + ex); }

Drugi način da obradimo nastanak ovakvog izuzetka je da metodu u kojoj se nalazi throw naredba označimo kao metodu u kojoj može da nastane izuzetak date vrste. Na primer:

public void metoda() throws MojException { ...

if (errorCheck())

throw new MojException("Houston, we have a problem."); ...

}

Sada poziv ovakve metode mora biti u odgovarajućem try bloku, ili metoda u kojoj sadrži ovaj poziv mora isto biti označena da može da izazove izuzetak. Dakle:

public void m1() {

try {

metoda();

} catch (MojException ex) {

System.out.println("Exception: " + ex); }

(33)

ili:

public void m1() throws MojException { metoda();

}

Na ovaj način odgovornost za obradu izuzetka može propagirati sve do metode main od koje počinje izvršavanje programa!

1.26 Klasa Object

Klasa Object predstavlja osnovnu klasu u hijerarhiji Java klasa, u smislu da sve klase nasleđuju klasu Object. Za klase za koje se navede da ne nasleđuju nijednu klasu (izostavljanjem extends klauzule) podrazumeva se da nasleđuju klasu

Object. Klasa Object nije apstraktna, tako da je moguće kreirati objekat ove klase. Ona definiše neke metode koje se relativno često koriste, poput ovih koje su opisane u nastavku ovog odeljka.

public boolean equals(Object o);

Koristi se prilikom poređenja objekata; poređenje tipa (a == b) je zapravo poređenje referenci. Poređenje (a.equals(b)) vraća rezultat zavisno od implementacije metode equals klase kojoj pripada objekat a. Klasa Object definiše podrazumevano poređenje objekata koje se svodi na poređenje referenci.

public int hashCode();

Izračunava hash vrednost za dati objekat. Koristi se najviše u hash-tabelama.

public String toString();

Vraća string reprezentaciju objekta. Ukoliko se ne redefiniše, poziva se implementacija iz klase Object koja vraća prilično nerazumljiv rezultat. Ova metoda ima donekle specijalan tretman od strane kompajlera, o čemu će više reči biti u sledećem odeljku.

1.27 Klasa String

Klasa String se nalazi u paketu java.lang i predstavlja string kao tip podatka (sećamo se da stringovi nemaju odgovarajući primitivni tip u Javi). U tom smislu, klasa String je kao i svaka druga Java klasa osim što ima donekle specijalan tretman od strane kompajlera.

1. Vrednosti primitivnih tipova se mogu predstaviti u Java programu odgova-rajućim konstantama. Na primer, 16 je vrednost tipa int, vrednost 16L označava vrednost tipa long, 'x' je vrednost tipa char, itd. Objektima se ne može pridružiti vrednost koja se može predstaviti literalom, osim u slučaju objekata klase String. U tom smislu, "tekst" u Java programu predstavlja objekat klase String čija je vrednost inicijalizovana na dati tekst. Zbog toga je sasvim ispravno pisati

String x = "tekst"; što ima isti efekat kao i

(34)

String x = new String("tekst");

2. Java ne omogućava redefinisanje operatora (kao što je to moguće u jeziku C++). Međutim, operator + može da se upotrebi za konkatenaciju stringova. Kada naiđe na izraz poput

"ime" + "prezime" ili x + "prezime" ili x + y

kompajler će generisati kod koji će izvršiti konkatenaciju stringova i vratiti rezultat u obliku novokreiranog objekta klase String. Konkatenacija može da obuhvati i primitivne tipove ili objekte drugih klasa. Na primer, iskaz

int a = 10;

String x = "vrednost: " + a;

rezultira kreiranjem novog String objekta čiji sadržaj je "vrednost: 10". U slučaju konkatenacije stringa sa objektom neke druge klase, kao na primer:

Automobil a = new Automobil(); String x = "Moj auto je: " + a;

biće pozvana metoda toString klase Automobil, pa će se zatim izvršiti konkatenacija stringova pomoću dobijenog stringa.

3. Metoda koja kao parametar ima tip String, može u svom pozivu da primi i objekat neke druge klase, pri čemu će kompajler automatski generisati kod koji poziva metodu toString() objekta, i zatim taj rezultat prosleđuje metodi koja se poziva. Na primer, posmatrajmo metodu

public void handleMessage(String message) { ... } i njen poziv

handleMessage(new Automobil());

Ovaj poziv metode handleMessage biće, u stvari, preveden u sledeći poziv: handleMessage(new Automobil().toString());

4. Za razliku od svih ostalih parametara, parametri metoda tipa String se ponašaju kao da se prenose po vrednosti a ne po referenci, iako su u pitanju objekti, a ne primitivni tipovi. Na primer, poziv sledeće metode

public void handleMessage(String message) { message += "xxx";

}

neće izazvati promenu objekta koji je prosleđen kao parametar, jer će se u telu metode, prilikom konkatenacije, generisati novi String objekat koji će biti dodeljen lokalnoj kopiji reference message. Po povratku iz metode uništava se promenjena lokalna kopija reference, i ostaje samo originalna referenca koja i dalje ukazuje na stari String objekat.

1.28 Primeri nekih klasa iz standardne biblioteke

Namena ovog odeljka je da ilustruje korišćenje nekih od klasa koje su sastavni deo standardne Java biblioteke, a koriste se često u praksi. Prednost postojanja ovako bogate standardne biblioteke je i u tome što se sa sigurnošću zna da su

(35)

ove klase dostupne u svakoj Java instalaciji, pa se klase iz biblioteke mogu slobodno koristiti, bez bojazni da ih neće biti na računaru gde softver instalira, ili čak, u slučaju Jave 1.2, da ih neće biti u CLASSPATH-u.

1.28.1 Klasa java.util.Vector

Klasa Vector služi kao “kontejnerska” klasa koja čuva kolekciju objekata kao sekvencu. Elementima sekvence se može pristupati po indeksu, a elementi se mogu dodavati i uklanjati na proizvoljnom mestu. Klasa je pisana tako da radi sa Object objektima, tako da je u stanju da prihvati sve objekte koje joj prosledimo.

Sledi primer u kome se u jednom Vector objektu čuva niz String-ova. Za doda-vanje elemenata u vektor koristi se metoda addElement, a za pristup objektima koristi se metoda elementAt. Iako ovaj primer demonstrira smeštanje objekata iste klase u vektor, to ne mora biti slučaj. Nema prepreke da se u isti vektor smeštaju objekti različitih klasa.

import java.util.Vector;

class VectorTest {

public static void main(String args[]) { Vector v = new Vector();

v.addElement("Ovo"); v.addElement("je"); v.addElement("probni"); v.addElement("tekst");

for (int i = 0; i < v.size(); i++)

System.out.print((String)v.elementAt(i) + " "); }

}

1.28.2 Klasa java.util.Hashtable

Klasa Hashtable implementira hash-tabelu koja mapira ključeve na vrednosti (i ključevi i vrednosti su predstavljeni odgovarajućim objektima). Parovi (ključ, vrednost) se u metodu smeštaju metodom put. Vrednost se u tabeli traži pomoću svog ključa metodom get. Sledeći primer predstavlja generisanje 10000 slučajnih celih brojeva u opsegu [0, 19] i brojanje koliko se puta koji broj pojavio pomoću hash-tabele. Tabela kao ključ koristi generisani broj, a vrednost je objekat klase Counter koji služi kao brojač pojava odgovarajućeg broja.

class Counter { int i = 1;

public String toString() {

return Integer.toString(i) + "\n"; }

}

import java.util.*;

class Statistics {

public static void main(String args[]) { Hashtable ht = new Hashtable();

for (int i = 0; i < 10000; i++) { Integer r =

Referencias

Documento similar