Predavanje br. index|1|2|3|4|5|6|7|8|9|10|11|12|13|14|HOME


Treće predavanje – objekti i klase

Objektno orijentirano programiranje – primjer: klasa Car – kreiranje objekata uz pomoć operatora new – separator pristupa članovima . – uporaba objekata unutar različitih klasa – inicijaliziranje atributa – metode – pozivanje metoda – implicirani this – member varijable nasuprot lokalnim varijablama – prenošenje argumenata u metodu – prenošenje argumenata u metodu, primjer – setter metode – uporaba setter metoda, primjer – vraćanje vrijednosti iz metode – vraćanje nekoliko vrijednosti iz metoda – uporaba getter metoda, primjer – konstruktori – uporaba konstruktora – ograničenja – zaštita pristupa(access protection) – četiri razine zaštite pristupa – što treba biti public, a što private? – tri koristi od zaštite pristupa – primjer zaštite pristupa – promjena implementacije


Objektno orijentirano programiranje

U klasičnom proceduralnom programiranju pokušava se problem iz realnog svijeta prikazati pomoću malog broja unaprijed definiranih tipova podataka: cijelih brojeva, realnih brojeva, stringova i eventualno polja. U objektno orijentiranom programiranju kreirate model promatranog sustava realnog svijeta. Klase su tipovi koje programer definira kako bi modelirao dijelove sustava.

Klasa je zamišljena kao prototip, nacrt ili ideja za svoje primjerke (instance). Možete imati cijele i realne brojeve i stringove, ali također imate i automobile, motocikle, ljude, zgrade, oblake, pse, anđele, studente, tečajeve, bankovne račune i bilo kakve tipove koji su bitni za rješavanje zadanog problema.

Klase specificiraju podatke i ponašanje, kako vlastito, tako i objekata koji se iz njih kreiraju. Klasa ima dva dijela: atribute i metode. Atributi opisuju što klasa jest. Metode opisuju što klasa čini.

Koristeći prototip koji klasa predstavlja, možete kreirati proizvoljan broj objekata, a svaki od njih je primjerak (instanca) klase. Različiti objekti iste klase imaju iste atribute i metode, ali vrijednosti atributa bit će općenito različite. Npr. Svi ljudi imaju neku boju očiju, ali se ona razlikuje od čovjeka do čovjeka.

S druge strane, objekt neke klase ima iste metode kao i ostali objekti te klase, osim što metode ovise o vrijednostima svojih argumenata i objektovih atributa.

To se reflektira u runtime obliku objekta. Svaki objekt ima odvojen blok memorije za svoje atribute, ali memorija u kojoj su zabilježene metode zajednička je za sve objekte dane klase.


Primjer: klasa Car

Pretpostavimo da želimo napisati program za simulaciju prometa koji bilježi prolazak vozila. Na svakom vozilu možemo promatrati svojstva kao što je brzina, maksimalna brzina i registarska oznaka koja ga jednoznačno identificira. U tradicionalnim programskim jezicima imali bismo opisali bismo to pomoću dvije floating point varijable i jednim stringom. U objektno orijentiranom jeziku koristimo koncept klase da bismo podatke spakirali u jedinstveni entitet, npr. ovako:

 
class Car {
 
  String licensePlate; // npr. "New York 543 A23"
  double speed;        // u kilometrima na sat
  double maxSpeed;     // u kilometrima na sat
 
}
 

Varijable licensePlate, speed i maxSpeed zovemo varijablama instanci, varijablama članovima ili atributima. Atributi nam kažu što neka klasa jest i koja su njena svojstva.

Objekt je specifična instanca, primjerak neke klase, sa konkretnim (eventualno promjenljivim) vrijednostima atributa. Dok je klasa općenita zamisao (blueprint) nekih objekata, instanca je konkretni objekt.


Kreiranje objekata uz pomoć operatora new

Da bismo kreirali konkretni object neke klase, odnosno instancirali klasu, koristimo ključnu riječ new iza koje slijedi poziv tzv. konstruktora klase. Pogledajmo kako bismo deklarirali i kreirali novu varijablu tipa Car koju ćemo nazvati c.

 
   Car c;
    c = new Car();
 

Prva riječ, Car, deklarira tip varijable c. Klase su tipovi pa kad varijablama dodjeljujemo tipove, dodjeljujemo im zapravo klase te ih deklariramo na isti način kao varijable čiji su tipovi int, char, double itd.

Znak jednakosti je operator pridruživanja, a riječ new je operator kreiranja (instanciranja).

Na kraju, primijetimo metodu Car(). Zagrade naznačuju da je to metoda, a ne tip podataka kao npr. riječ Car u prvom retku. To je konstruktor, metoda koja kreira novu instancu klase. O konstruktorima ćet učiti uskoro. Ako svoju klasu i ne snabdijete konstruktorima, kompajler će umetnuti svoj default konstruktor bez argumenata.

Deklaraciju tipa i kreiranje instance obično pišemo u jednoj naredbi, npr.

   Car c = new Car();

Separator pristupa članovima .

Jednom kad ste kreirali neki objekt, potrebna vam je mogućnost pristupa njegovim članovima (varijablama i metodama). Za to ćete upotrijebiti separator pristupa, točku (.). Klasa Car ima tri atributa:

Dakle, ako je c objekt tipa Car, c također ima tri odgovarajuće varijable:

Koristite ih kao što biste koristili bilo koju drugu varijablu. Na primjer:

 
   Car c = new Car();
    
    c.licensePlate = "New York A45 636";
    c.speed = 70.0;
    c.maxSpeed = 123.45;
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
      "kilometara na sat.");
 

Separator . selektira pojedinog člana (varijablu, kao u ovom primjeru, ali također i metodu) nekog objekta po njegovom imenu.


Uporaba objekata unutar različitih klasa

Sljedeći program kreira novi primjerak automobila (objekta klase Car), dodjeljuje vrijednosti njegovim varijablama i ispisuje rezultat.

 
class CarTest {
 
  public static void main(String args[]) {
    
    Car c = new Car();
    
    c.licensePlate = "New York A45 636";
    c.speed = 70.0;
    c.maxSpeed = 123.45;
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
      "kilometara na sat.");    
  }
  
}
 

Ovaj program ne zahtijeva samo klasu CarTest nego također i klasu Car. Da biste ga mogli izvršiti, stavite klasu Car u datoteku Car.java., a klasu CarTest u datoteku CarTest.java. Obje datoteke neka budu u istom direktoriju. Kompilirajte obje datoteke na uobičajeni način i na kraju izvršite CarTest.

 
% javac Car.java
% javac CarTest.java
% java CarTest
New York A45 636 se krece brzinom od 70.0 kilometara na sat.
%
 

Primijetite da klasa Car nema main() metodu, dakle ne možete je “izvršiti”. Ona postoji jedino tako da je pozivaju drugi programi koji imaju vlastite main() metode.

Mnoge aplikacije koje ćete pisati koristit će više klasa. Uobičajeno je da se svaka klasa stavi u svoju vlastitu datoteku. Uskoro ćete naučiti koristiti i pakete (packages) kako biste mogli organizirati svoje često korištene klase unutar različitih direktorija. Za sada držite sve datoteke sa izvornim programima (*.java) kao i one sa kompiliranim programima (*.class) unutar jednog direktorija. Iako smo kompilirali obje klase odvojeno, dovoljno bi bilo kompilirati samo klasu CarTest jer će kompajler već sam pronaći klasu Car.


Inicijaliziranje atributa

Atributi se mogu (i najčešće trebaju) inicijalizirati odmah čim su deklarirani, isto kao i lokalne varijable. Klasu Car možemo preraditi tako da inicijaliziramo njene attribute ovako:

class Car {
 
  String licensePlate = "";    // npr. "New York 543 A23"
  double speed        = 0.0;   // u kilometrima na sat
  double maxSpeed     = 120.0; // u kilometrima na sat
 
}
 

Sljedeći program kreira novi automobil (object tipa Car) i ispisuje odgovarajuće podatke.

 
class CarTest2 {
 
  public static void main(String args[]) {
    
    Car c = new Car();
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
      "kilometara na sat.");    
  }
  
}
 

Rezultat izvršavanja bi bio

 
% javac Car.java
% javac CarTest.java
% java CarTest
 se krece brzinom od 0.0 kilometara na sat.
%

Metode

Tipovi podataka ne znače mnogo ako ne možete s njima ništa napraviti. Zbog toga postoje metode. Dok atributi kazjuju što klasa jest, metode kazuju što ona čini. Atribute i metode nazivamo zajedničim imenom članovima klase. Klase koje ste do sada upoznali imaju uglavnom samo jednu metodu, main(). Međutim, općenito klase mogu imati mnogo različitih metoda. Na primjer, klasu Car možemo snabdjeti metodom koja će simulirati vožnju maksimalnom brzinom:

 
class Car {
 
  String licensePlate = "";    // npr. "New York 543 A23"
  double speed        = 0.0;   // u kilometrima na sat
  double maxSpeed     = 120.0; // u kilometrima na sat
  
  void floorIt() { // ubrzanje do maksimalne brzine
    this.speed = this.maxSpeed;  
  }
  
}
 

Atributi su ostali isti kao prije, ali sad je dodana metoda koju smo nazvali floorIt(). Počinje ključnom riječi void što je povratni tip (return type) te metode. Svaka metoda mora imati povratni tip koji može biti ili void ili neki tip podataka kao int, byte, float, String, ili klasa koju ste sami definirali. Povratni tip pokazuje vrstu vrijednosti koja će biti vraćena pozivnoj metodi nakon što se pozvana metoda izvrši. Ako je povratni tip na primjer int, onda tu metodu možete koristiti svagdje gdje biste inače koristili literal ili varijablu tipa int.Ako je povratni tip void, metoda ne vraća nikakvu vrijednost.

Ime ove metode je floorIt , a iza njega slijede prazne zagrade. Kad bi ova metoda imala argumente, oni bi se naveli unutar tih zagrada. Tijelo metode nalazi se unutar vitičastih zagrada.

this.speed = this.maxSpeed;

Primijetite da smo unutar klase Car koristili ispred imena member varijabli ključnu riječ this to kako bismo naznačili da su to varijable trenutno aktivnog objekta.


Pozivanje metoda

Izvan klase Car, metodu floorIt() pozvat ćete na isti način kao što ste referencirali attribute, dakle navodeći ime objekta kojeg želite “ubrzati” i separator ., kao što pokazuje sljedeći primjer

 
class CarTest3 {
 
  public static void main(String args[]) {
    
    Car c = new Car();
    
    c.licensePlate = "New York A45 636";
    c.maxSpeed = 123.45;
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
     " kilometara na sat.");
 
    c.floorIt();
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
     " kilometara na sat.");
 
  }
    
}
 

Izlaz:

 
% javac Car.java
% javac CarTest3.java
% java CarTest3
New York A45 636 se krece brzinom od 0.0 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
%
 

Metoda floorIt() je ugrađena u klasu Car. Svaka metoda u Java programu mora, za razliku od C++ programa, pripadati nekoj klasi.


Implicirani this

Unutar klase Car nije nužno dodavati imenima atributa prefiks this. jer se on podrazumijeva. To je zato jer metoda floorIt() mora biti pozvana od specifične instance klase Car, a ta instanca zna gdje su njeni podaci. Ili drugim riječima, svaki object iz klase Car ima svoju vlastitu floorIt() metodu.

 
class Car {
 
  String licensePlate = "";    // npr. "New York 543 A23"
  double speed        = 0.0;   // u kilometrima na sat
  double maxSpeed     = 120.0; // u kilometrima na sat
  
  void floorIt() { // ubrzanje do maksimalne brzine
    speed = maxSpeed;  
  }
  
}

U početku, zbog jasnoće, preporučljivo je uvijek eksplicitno koristiti prefiks this, ali kasnije se od toga može i odustati.


Member varijable nasuprot lokalnim varijablama

 
class Car {
 
  String licensePlate = "";    // member variabla
  double speed;       = 0.0;   // member variabla
  double maxSpeed;    = 120.0; // member variabla
  
  boolean isSpeeding() {
    double excess;             // lokalna variabla 
    excess = this.maxSpeed - this.speed; 
    if (excess < 0) return true;
    else return false;
  }
 
}
 

Svi dosadašnji programi bili su vrlo jednostavni. Svaki je imao točno jednu klasu koja je imala po jednu metodu, main(), i u njoj je bila sadržana sva programska logika i njene vlastite, lokalne varijable koje nisu mogle biti referencirane izvan metode.

S druge strane, varijable licensePlate, speed i maxSpeed klase Car class, pripadaju objektu tipa Car, a ne pojedinoj metodi. Definirane su izvan bilo koje metode, ali unutar klase i nazivaju se member varijable ili atributi (fields).

Member nije isto što i member varijabla ili atribut. Memberi uključuju i attribute i metode.


Prenošenje argumenata u metodu

Direktno pristupanje atributima smatra se lošom programerskom navikom. Dobro objektno orijentirano programiranje prakticira pristup poljima isključivo putem metoda. To vam omogućuje da promijenite implementaciju neke klase bez da išta mijenjate u njenom sučelju. To vam također omogućuje ugrađivanje raznih ograničenja na vrijednosti atributa.

Za to je potrebno imati mehanizam prenošenja informacija u klasu, a to se radi pomoću argumenata. Na primjer, da bismo dozvolili ostalim objektima da mijenjaju vrijednost atributa speed na nekom objektu tipa Car, klasu Car možemo snabdijeti metodom koju ćemo nazvati accelerate(). Ta metoda ne dozvoljava pojedinačnom automobilu ubrzavanje iznad maksimalne brzine ili ispod 0 km/h.

  void accelerate(double deltaV) {
 
     this.speed = this.speed + deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed <  0.0) {
       this.speed = 0.0; 
     }     
     
  }

Prva linija metode naziva se njenom signaturom.  Signatura void accelerate(double deltaV) označava da metoda accelerate() ne vraća nikakvu vrijednost, a uzima jedan argument tipa double koji će unutar te metode biti označen imenom deltaV. Java prenosi argumente metodama po vrijednosti, ne po referenci.


Prenošenje argumenata u metodu, primjer

class Car {
 
  String licensePlate = "";    // npr. "New York 543 A23"
  double speed        = 0.0;   // u kilometrima na sat
  double maxSpeed     = 120.0; // u kilometrima na sat
  
  void floorIt() { // ubrzanje do maksimalne brzine
    speed = maxSpeed;  
  }
  
  void accelerate(double deltaV) { // ubrzanje za zadani deltaV
 
     this.speed = this.speed + deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed <  0.0) {
       this.speed = 0.0; 
     }     
     
  }
  
}
 
class CarTest4 {
 
  public static void main(String args[]) {
    
    Car c = new Car();
    
    c.licensePlate = "New York A45 636";
    c.maxSpeed = 123.45;
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
     " kilometara na sat.");
 
    for (int i = 0; i < 15; i++) {
      c.accelerate(10.0);     
      System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
       " kilometara na sat.");
    }
 
  }
    
}
 

Izlaz:

 
% javac Car.java
% javac CarTest4.java
% java CarTest4
New York A45 636 se krece brzinom od 0.0 kilometara na sat.
New York A45 636 se krece brzinom od 10.0 kilometara na sat.
New York A45 636 se krece brzinom od 20.0 kilometara na sat.
New York A45 636 se krece brzinom od 30.0 kilometara na sat.
New York A45 636 se krece brzinom od 40.0 kilometara na sat.
New York A45 636 se krece brzinom od 50.0 kilometara na sat.
New York A45 636 se krece brzinom od 60.0 kilometara na sat.
New York A45 636 se krece brzinom od 70.0 kilometara na sat.
New York A45 636 se krece brzinom od 80.0 kilometara na sat.
New York A45 636 se krece brzinom od 90.0 kilometara na sat.
New York A45 636 se krece brzinom od 100.0 kilometara na sat.
New York A45 636 se krece brzinom od 110.0 kilometara na sat.
New York A45 636 se krece brzinom od 120.0 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
%

Setter metode

Setter metode, zvane također i mutator metode, imaju zadatak dodjeljivati atributima vrijednosti koje se zadaju putem argumenata. Ove metode u pravilu vraćaju void. Uobičajeno je koristiti this.name za referenciranje atributa i dodjeljivanje vrijednosti iz istoimenog argumenta. Na primjer:

 

class Car {
 
  String licensePlate = "";    // npr. "New York 543 A23"
  double speed        = 0.0;   // u kilometrima na sat
  double maxSpeed     = 120.0; // u kilometrima na sat
  
  // setter metoda za atribut licensePlate
  void setLicensePlate(String licensePlate) {
    this.licensePlate = licensePlate;
  }
 
  // setter metoda za atribut maxSpeed
  void setMaximumSpeed(double maxSpeed) {
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
  }
 
  void floorIt() { // ubrzanje do maksimalne brzine
    speed = maxSpeed;  
  }
  
  
  void accelerate(double deltaV) { // ubrzanje za zadani deltaV
 
     this.speed = this.speed + deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed <  0.0) {
       this.speed = 0.0; 
     }     
     
  }
  
}

Uporaba setter metoda, primjer

class CarTest5 {
 
  public static void main(String args[]) {
    
    Car c = new Car();
    
    c.setLicensePlate("New York A45 636");
    c.setMaximumSpeed(123.45);
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
     " kilometara na sat.");
 
    for (int i = 0; i < 15; i++) {
      c.accelerate(10.0);     
      System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
       " kilometara na sat.");
    }
 
  }
    
}
 

Izlaz:

 
% javac Car.java
% javac CarTest5.java
% java CarTest5
New York A45 636 se krece brzinom od 0.0 kilometara na sat.
New York A45 636 se krece brzinom od 10.0 kilometara na sat.
New York A45 636 se krece brzinom od 20.0 kilometara na sat.
New York A45 636 se krece brzinom od 30.0 kilometara na sat.
New York A45 636 se krece brzinom od 40.0 kilometara na sat.
New York A45 636 se krece brzinom od 50.0 kilometara na sat.
New York A45 636 se krece brzinom od 60.0 kilometara na sat.
New York A45 636 se krece brzinom od 70.0 kilometara na sat.
New York A45 636 se krece brzinom od 80.0 kilometara na sat.
New York A45 636 se krece brzinom od 90.0 kilometara na sat.
New York A45 636 se krece brzinom od 100.0 kilometara na sat.
New York A45 636 se krece brzinom od 110.0 kilometara na sat.
New York A45 636 se krece brzinom od 120.0 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
%

Getter metode

Često je korisno da metoda vraća neku vrijednost onoj klasi koja ju je pozvala. To se radi pomoću ključne riječi return na završetku tijela metode te naznakom povratnog tipa na njenom početku. Na primjer sljedeća metoda getLicensePlate() vraća trenutačnu vrijednost atributa licensePlate u klasi Car.

  String getLicensePlate() {
    return this.licensePlate;
  }
 
 

Ovakve metode koje samo vraćaju vrijednost nekog atributa nazivaju se getter ili accessor metode. Signatura String getLicensePlate() nam kaže da metoda getLicensePlate() vraća vrijednost tipa String i ne traži nikakve argumente. Unutar metode imamo liniju return this.licensePlate; koja vraća String sadržan u atributu licensePlate onome tko je metodu pozvao. Povratni tip u return naredbi mora odgovarati deklariranom povratnom tipu u signaturi.


Vraćanje nekoliko vrijednosti iz metoda

Metoda ne može vratiti više od jedne vrijednosti. Ne možete na primjer vratiti licensePlate, speed i maxSpeed iz iste metode. Mogli biste ih, naravno, složiti u objekt neke vrste i njega vratiti, no to nije pravi način. Pravilno bi bilo definirati odvojene metode, getSpeed(), getMaxSpeed(), i getLicensePlate(), od kojih svaka vraća po jednu odgovarajuću vrijednost.

class Car {
 
  String licensePlate = "";    // npr. "New York 543 A23"
  double speed        = 0.0;   // u kilometrima na sat
  double maxSpeed     = 120.0; // u kilometrima na sat
  
  // getter (accessor) metode
  
  String getLicensePlate() {
    return this.licensePlate;
  }
 
  double getMaxSpeed() {
    return this.maxSpeed;
  }
 
  double getSpeed() {
    return this.speed;
  }
 
  // setter metoda za atribut licensePlate
  void setLicensePlate(String licensePlate) {
    this.licensePlate = licensePlate;
  }
 
  // setter metoda za atribut maxSpeed
  void setMaximumSpeed(double maxSpeed) {
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
  }
 
  void floorIt() { // ubrzanje do maksimalne brzine
    speed = maxSpeed;  
  }
  
  void accelerate(double deltaV) { // ubrzanje za zadani deltaV
 
     this.speed = this.speed + deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed <  0.0) {
       this.speed = 0.0; 
     }     
     
  }
  
}

Uporaba getter metoda, primjer

class CarTest6 {
 
  public static void main(String args[]) {
    
    Car c = new Car();
    
    c.setLicensePlate("New York A45 636");
    c.setMaximumSpeed(123.45);
    
    System.out.println(c.getLicensePlate() + " se krece brzinom od " 
     + c.getSpeed() + " kilometara na sat.");
 
    for (int i = 0; i < 15; i++) {
      c.accelerate(10.0);     
      System.out.println(c.getLicensePlate() + " se krece brzinom od " 
       + c.getSpeed() + " kilometara na sat.");
    }
 
  }
    
}

Primijetite da više nema direktnog pristupa atributima!

Izlaz:

 
% javac Car.java
% javac CarTest5.java
% java CarTest5
New York A45 636 se krece brzinom od 0.0 kilometara na sat.
New York A45 636 se krece brzinom od 10.0 kilometara na sat.
New York A45 636 se krece brzinom od 20.0 kilometara na sat.
New York A45 636 se krece brzinom od 30.0 kilometara na sat.
New York A45 636 se krece brzinom od 40.0 kilometara na sat.
New York A45 636 se krece brzinom od 50.0 kilometara na sat.
New York A45 636 se krece brzinom od 60.0 kilometara na sat.
New York A45 636 se krece brzinom od 70.0 kilometara na sat.
New York A45 636 se krece brzinom od 80.0 kilometara na sat.
New York A45 636 se krece brzinom od 90.0 kilometara na sat.
New York A45 636 se krece brzinom od 100.0 kilometara na sat.
New York A45 636 se krece brzinom od 110.0 kilometara na sat.
New York A45 636 se krece brzinom od 120.0 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
New York A45 636 se krece brzinom od 123.45 kilometara na sat.
%

Konstruktori

Konstruktor kreira novu instancu klase. On inicijalizira potrebne varijable i obavlja sve poslove koji su potrebni da bi se klasa pripremila za uporabu. Pogledajmo primjer:

 
Car c = new Car(); 
 

Ovdje je Car() konstruktor klase Car. Konstruktor ima uvijek isto ime kao i pripadna klasa.

Ako klasu ne snabdijemo nikakvim konstruktorom, Java osigurava jedan generički, bez argumenata, tzv. noargs constructor, međutim, bolje je imati vlastite konstruktore. .Konstruktor se radi tako da se napiše metoda koja ima isto ime kao i klasa. Zato se konstruktor klase Car zove Car().

Konstruktori nemaju povratnog tipa. Oni zapravo vraćaju instancu svoje klase, ali to čine implicitno, ne eksplicitno.

Sljedeća metoda je konstruktor koji inicijalizira varijablu koja predstavlja registarsku pločicu (licensePlate) na prazan string, brzinu (speed) na nulu, a maksimalnu brzinu (maxSpeed) na 120.0 km/h.

 
  Car() {
    this.licensePlate = ""; 
    this.speed  = 0.0;
    this.maxSpeed = 120.0;
  }
 

Još bolje, možete napisati konstruktor koji prima tri argumenta i koristi ih za inicijaliziranje odgovarajućih varijabli:

 
  Car(String licensePlate, double speed, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = speed;
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
    if (speed > this.maxSpeed) this.speed = this.maxSpeed;
    if (speed < 0) this.speed = 0.0;
    else this.speed = speed;
    
  }
 

Ili možda želite da inicijalna brzina uvijek bude jedanaka nuli, a maksimalna brzina i registarska pločica specificirane argumentima:

 
  Car(String licensePlate, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = 0.0;
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
 
  }
 

Preradimo još jednom klasu Car, tako da u nju ugradimo ove konstruktore:

 
class Car {
 
  String licensePlate; // npr. "New York 543 A23"
  double speed;        // u kilometrima na sat
  double maxSpeed;     // u kilometrima na sat
  
  Car() {
    this.licensePlate = ""; 
    this.speed  = 0.0;
    this.maxSpeed = 120.0;
  }
 
  Car(String licensePlate, double speed, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = speed;
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
    if (speed > this.maxSpeed) this.speed = this.maxSpeed;
    if (speed < 0) this.speed = 0.0;
    else this.speed = speed;
    
  }
 
  Car(String licensePlate, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = 0.0;
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
 
  }
  
  // getter (accessor) metode
  
  String getLicensePlate() {
    return this.licensePlate;
  }
 
  double getMaxSpeed() {
    return this.maxSpeed;
  }
 
  double getSpeed() {
    return this.speed;
  }
 
  // setter metoda za atribut licensePlate
  void setLicensePlate(String licensePlate) {
    this.licensePlate = licensePlate;
  }
 
  // setter metoda za atribut maxSpeed
  void setMaximumSpeed(double maxSpeed) {
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
  }
 
  void floorIt() { // ubrzanje do maksimalne brzine
    speed = maxSpeed;  
  }
  
  void accelerate(double deltaV) { // ubrzanje za zadani deltaV
 
     this.speed = this.speed + deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed <  0.0) {
       this.speed = 0.0; 
     }     
     
  }
  
}

Uporaba konstruktora

Sljedeći program, CarTest7, koristi treći od navedenih konstruktora za inicijaliziranje objekata tipa Car umjesto da direktno postavlja vrijednosti.

 
class CarTest7 {
 
  public static void main(String args[]) {
    
    Car c = new Car("New York A45 636", 123.45);
    
    System.out.println(c.getLicensePlate() + " se krece brzinom od " + c.getSpeed() + 
     " kilometara na sat.");
 
    for (int i = 0; i < 15; i++) {
      c.accelerate(10.0);
      System.out.println(c.getLicensePlate() + " se krece brzinom od " + c.getSpeed() 
       + " kilometara na sat.");
    }
 
  }
    
}

Primijetite da više ne morate voditi računa o atributima licensePlate, speed i maxSpeed. Sve što trebate znati je kako konstruirati novi primjerak klase Car i kako ispisati njegove podatke.

Postavlja se pitanje jesu li metode setLicensePlate() i setMaximumSpeed() zaista još potrebne kad se atributi licensePlate i maxSpeed postavljaju u konstruktorima. Odgovor ovisi o tome želimo li dopustiti njihovo mijenjanje jednom nakon što je objekt već kreiran. Klase koje ne dopuštaju promjenu atributa svojih objekata nakon što su kreirani, nazivaju se nepromjenjivima (immutable). String je primjer takve klase. Ne možete promijeniti njegove podatke, možete samo kreirati novi objekt tipa String.


Ograničenja

Mogućnost implementiranja ograničenja jedan je od razloga za davanje prednosti korištenju konstruktora i setter metoda pred direktnim dohvaćanjem varijabli. Na primjer, u klasi Car važno je osigurati da brzina nikad ne bude veća od propisane maksimalne brzine i da ni jedna ne bude manja od nule.

Već smo to vidjeli u primjeru metode accelerate() koja, na način kako je napravljena, neće pridijeliti automobilu brzinu veću od propisane maksimalne brzine.

  void accelerate(double deltaV) {
 
     this.speed = this.speed + deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed < 0.0) {
       this.speed = 0.0; 
     }
     
  }
 

Takva se ograničenja mogu ugraditi i u konstruktore. Na primjer, sljedeći konstruktor (treći u nizu) klase Car osigurava da maksimalna brzina ne bude manja od nule.

 
  Car(String licensePlate, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = 0.0;
    if (maxSpeed >= 0.0) {
      this.maxSpeed = maxSpeed;
    }
    else {
      maxSpeed = 0.0;
    }
    
  }

Zaštita pristupa (access protection)

Globalne varijable su klasični izvor pogrešaka u većini programskih jezika. Neka nepoznata funkcija obično promijeni vrijednost varijable tamo gdje programer to ne očekuje.

Većina objektno orijentiranih jezika, Java također, omogućuje zaštitu varijabli od modifikacije izvana. To vam daje garanciju da će vaša klasa ostati konzistentna tako dugo dok su njene vlastite metode u redu. Na primjer, u klasi Car željeli bismo biti sigurni da ni jedan blok programskog koda iz neke druge klase neće moći promijeniti varijablu speed tako da bude veća od maxSpeed. Želimo pronaći način da sljedeći primjer koda učinimo nelegalnim:

 
 Car c = new Car("New York A234 567", 100.0);
  c.speed = 150.0;
 

Ovaj kod narušava uvjet koji smo postavili na našu klasu. Želimo omogućiti kompajleru da provede poštivanje takvih uvjeta.

Klasa prezentira prema vanjskom svijetu određenu sliku sebe, tzv. sučelje. Ta slika kaže da klasa ima određene metode i određene atribute. Sve ostalo, uključjući detaljni izgled i funkcioniranje metoda je skriveno. Tako dugo dok se ta slika ne mijenja, programer može slobodno mijenjati način na koji je ona implementirana. To između ostaloga dozvoljava programeru da mijenja i poboljšava algoritme koje klasa koristi bez bojazni da će to na neki nepredvidljiv način narušiti funkcioniranje klase. To se naziva enkapsulacijom

Drugi način razmišljanja o enkapsulaciji je sljedeći. Zamislimo da klasa potpisuje «ugovor» sa ostalim klasama u programu. Taj ugovor kaže da klasa ima određene jednoznačno imenovane metode sa zadanim tipovima argumenata i povratnim tipovima. Ugovor može također reći da klasa ima atribute zadanog imena i zadanog tipa. Međutim, on ne kaže kako su te metode implementirane niti zabranjuje postojanje drugih metoda i atributa koje klasa možda koristi. On samo garantira prisutnost određenih metoda i atributa pri čemu ne isključuje ostale metode ni atribute. Takav je ugovor implementiran kroz koncept zaštite pristupa.


Četiri razine zaštite pristupa

Bilo koja dva različita Java objekta mogu jedan prema drugom biti u jednoj od sljedećih relacija:

Ove relacije nisu međusobno isklučive. Jedan objekt može, na primjer, biti u podklasi klase drugog objekta unutar istog paketa.

Možete definirati koji će memberi vaše klase, dakle atributi i metode, biti dostupni drugim objektima iz svake od ove četiri grupe u odnosu na promatranu klasu.

·        Želite li da bilo koji objekt može pozvati određenu metodu ili promijeniti određeni atribut, označit ćete taj member kao public.

·        Želite li da samo objekti iz iste klase mogu pozvati određenu metodu ili promijeniti određeni atribut, označit ćete taj member kao private.

·        Želite li pristup dozvoliti samo objektima koji pripadaju podklasi ili se nalaze unutar istog paketa, označit ćete ih kao protected.

·        Konačno, želite li pristup do određenog membera omogućiti samo objektima iz istog paketa, izostavit ćete specifikaciju pristupa. Taj default pristup naziva se package, ali nema svoju ključnu riječ.

Po pretpostavci, sve klase koje pišete pripadaju istom paketu. Međutim, one nisu u istim paketima sa Java klasama kao što je System ili Applet.

Atributima i metodama označenim kao public može se pristupiti s bilo kojeg mjesta odakle je sam objekt vidljiv. Njihov broj treba držati na minimumu i oni trebaju biti usko povezani sa ključnom funkcionalnošću klase. Ne trebaju prikazivati detalje implementacije. Osim rijetkih izuzetaka, atributti ne bi trebali biti public.

Atributima i metodama označenim kao private može pristupiti jedino objekt sam ili drugi objekt iste klase (tzv. sibling).


Što treba biti public, a što private?

Ova se pravila mogu po volji mijenjati ako za to postoji razlog. Međutim, dobro ih je imati na umu jer pokrivaju veliku većinu praktičnih potreba.


Tri koristi od zaštite pristupa

Korištenje mogućnosti zaštite podataka osigurava tri bitne koristi:

  1. Omogućuje uvođenje ograničenja na stanje objekta
  2. Osigurava jednostavnije korisničko sučelje. Programeri korisnici ne moraju znati sve što se nalazi unutar klase, dovoljno im je da poznaju javne (public) segmente.
  3. Odvaja sučelje od implementacije, čime se omogućuje da se jedno mijenja nezavisno od drugoga (zamislimo npr. da smo odlučili da registarska pločica, licensePlate više ne bude String nego instanca nove klase LicensePlate).

Primjer zaštite pristupa

Evo kako bi klasa Car u praksi zaista izgledala. Primijetite da su svi atributi sada private, a može im se pristupiti jedino kroz metode koje su deklarirane kao public. To je uobičajeni obrazac za pisanje klasa u Javi.

 
class Car {
 
  private String licensePlate; // npr. "New York 543 A23"
  private double speed;        // u kilometrima na sat
  private double maxSpeed;     // u kilometrima na sat
  
  public Car() {
    this.licensePlate = ""; 
    this.speed  = 0.0;
    this.maxSpeed = 120.0;
  }
 
  public Car(String licensePlate, double speed, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = speed;
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
    if (speed > this.maxSpeed) this.speed = this.maxSpeed;
    if (speed < 0) this.speed = 0.0;
    else this.speed = speed;
    
  }
 
  public Car(String licensePlate, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = 0.0;
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
 
  }
  
  // getter (accessor) metode
  
  public String getLicensePlate() {
    return this.licensePlate;
  }
 
  public double getMaxSpeed() {
    return this.maxSpeed;
  }
 
  public double getSpeed() {
    return this.speed;
  }
 
  // setter metoda za atribut licensePlate
  public void setLicensePlate(String licensePlate) {
    this.licensePlate = licensePlate;
  }
 
  // setter metoda za atribut maxSpeed
  public void setMaximumSpeed(double maxSpeed) {
    if (maxSpeed > 0) this.maxSpeed = maxSpeed;
    else this.maxSpeed = 0.0;
  }
 
  public void floorIt() { // ubrzanje do maksimalne brzine
    speed = maxSpeed;  
  }
  
  public void accelerate(double deltaV) { // ubrzanje za zadani deltaV
 
     this.speed = this.speed + deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed <  0.0) {
       this.speed = 0.0; 
     }     
     
  }
  
}

Pokušajmo sada iz neke druge klase direktno pristupiti atributima klase Car da vidimo što će se dogoditi:

class CarTest8 {
 
  public static void main(String args[]) {
    
    Car c = new Car("New York A45 636", 100.0);
    
    c.licensePlate = "New York A45 636";
    c.speed = 0.0;
    c.maxSpeed = 123.45;
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
     " kilometara na sat.");
 
    c.floorIt();
    
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed + 
     " kilometara na sat.");
 
  }
    
}

Evo što bi se dogodilo ako pokušate kompilirati CarTest8 uz revidiranu klasu Car:

% javac Car.java
% javac CarTest8.java
CarTest8.java:7: Variable licensePlate in class Car not accessible from class CarTest8.
    c.licensePlate = "New York A45 636";
     ^
CarTest8.java:8: Variable speed in class Car not accessible from class CarTest8.
    c.speed = 0.0;
     ^
CarTest8.java:9: Variable maxSpeed in class Car not accessible from class CarTest8.
    c.maxSpeed = 123.45;
     ^
CarTest8.java:11: Variable licensePlate in class Car not accessible from class CarTest8.
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed +
                        ^
CarTest8.java:11: Variable speed in class Car not accessible from class CarTest8.
    System.out.println(c.licensePlate + " is moving at " + c.speed +
                                                            ^
CarTest8.java:16: Variable licensePlate in class Car not accessible from class CarTest8.
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed +
                        ^
CarTest8.java:16: Variable speed in class Car not accessible from class CarTest8.
    System.out.println(c.licensePlate + " se krece brzinom od " + c.speed +
                                                            ^
7 errors
%

 

U mnogim slučajevima bit će također metoda koje su private, protected ili imaju default pristup. Zajednički se one nazivaju non-public metode.

U mnogim slučajevima atributi (varijable) bit će protected ili će imati default pristup, dok su, međutim, public varijable rijetke. Takva koncepcija omogućuje programerima promjenu implementacije klase i istovremeno zadržavanje nepromijenjenog sučelja prema vanjskom svijetu.


Promjena implementacije

Pretpostavimo da želimo prilagoditi klasu Car za uporabu u simulaciji prometa velikog grada kao što je New York, gdje će svaki realni automobil na cesti biti reprezentiran po jednim objektom tipa Car. To je velika količina objekata i poželjno je smanjiti količinu zauzete memorije. Svaki objekt tipa Car zauzima oko 60 byteova, što najviše ovisi o duljini Stringa za registarsku pločicu. Na numeričkim varijablama možemo uštedjeti 8 byteova po objektu ako float umjesto double, pri čemu će sučelje ostati isto:

 
class Car {
 
  private String licensePlate; // npr. "New York 543 A23"
  private float speed;         // u kilometrima na sat
  private float maxSpeed;      // u kilometrima na sat
  
  public Car() {
    this.licensePlate = ""; 
    this.speed  = 0.0F;
    this.maxSpeed = 120.0F;
  }
 
  public Car(String licensePlate, double speed, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = (float) speed;
    if (maxSpeed > 0) this.maxSpeed = (float) maxSpeed;
    else this.maxSpeed = 0.0F;
    if (speed > this.maxSpeed) this.speed = (float) this.maxSpeed;
    if (speed < 0) this.speed = 0.0F;
    else this.speed = (float) speed;
    
  }
 
  public Car(String licensePlate, double maxSpeed) {
 
    this.licensePlate = licensePlate; 
    this.speed  = 0.0F;
    if (maxSpeed > 0) this.maxSpeed = (float) maxSpeed;
    else this.maxSpeed = 0.0F;
 
  }
  
  // getter (accessor) metode
  
  public String getLicensePlate() {
    return this.licensePlate;
  }
 
  public double getMaxSpeed() {
    return this.maxSpeed;
  }
 
  public double getSpeed() {
    return this.speed;
  }
 
  // setter metoda za atribut licensePlate
  public void setLicensePlate(String licensePlate) {
    this.licensePlate = licensePlate;
  }
 
  // setter metoda za atribut maxSpeed
  public void setMaximumSpeed(double maxSpeed) {
    if (maxSpeed > 0) this.maxSpeed = (float) maxSpeed;
    else this.maxSpeed = 0.0F;
  }
 
  public void floorIt() { // ubrzanje do maksimalne brzine
    this.speed = this.maxSpeed;  
  }
  
  public void accelerate(double deltaV) { // ubrzanje za zadani deltaV
 
     this.speed = this.speed + (float) deltaV;
     if (this.speed > this.maxSpeed) {
       this.speed = this.maxSpeed; 
     }
     if (this.speed <  0.0) {
       this.speed = 0.0F; 
     }     
     
  }
  
}

Budući da je sučelje ostalo isto, ni jedna druga klasa koja ovisi o ovoj klasi ne mora se mijenjati pa čak ni ponovno kompilirati. Ovdje bismo mogli ići dalje pa napraviti vlastitu klasu LicensePlate koja bi doz voljava ASCII znakove od jednog bytea umjesto Unicode znakova od dva bytea i tako dalje.