Java programozás 13. – Osztályok és objektumok

Osztályok és objektumok, avagy mi van a motorháztető alatt

Fura lesz, de nem a címmel megegyező sorrendben ismertetném ennek résznek az anyagát. Az ok egyszerű. Először értsük meg nagy vonalakban, hogy mi az objektum, az miből lesz, és mi az ami a működésében nagyon fontos szerepet játszik. Oké, mondhatod azt, hogy a Metódusok témakörben már elmagyaráztam, hogy mik ezek. Persze, tényleg ejtettem róluk pár szót, de csak a felszínt kapargattam. Most egy picit mélyebbre ásunk. A végéig persze most sem jutunk el, de már sokkal közelebb kerülünk ahhoz, hogy miért is jó nekünk a Java.

Objektum

A Java programozási nyelv alapvető eleme az objektum. Az ilyen nyelveket objektum orientált (OO) programozási nyelveknek nevezzük. Az objektum az adott feladat szempontjából fontos, a valódi világ valamilyen elemének a rá jellemző tulajdonságai és viselkedései által modellezett eleme. Az objektumokkal kapcsolatban valamilyen feladatokat szeretnénk megoldani. A nyelv tervezésekor fontos szempont volt az, hogy az objektumok többé-kevésbé állandóak, de a hozzájuk tartozó feladatok nem, ezért az objektumok kapnak nagyobb hangsúlyt. A mai programok nagyon sok, egymással kölcsönhatásban álló elemből állnak, így nem is igazán programokról, hanem programrendszerekről beszélhetünk.

Az objektumokkal kapcsolatban alapvetően négy fontos dologról beszélhetünk:

  1. Adat és kód egysége
  2. Zártság
  3. Öröklődés
  4. Polimorfizmus

Ezekből az utolsó kettőt nagyvonalúan későbbre teszem, ha folyamatosan haladsz a tanulással, akkor egyelőre inkább csak összezavarna.

Adat és kód egysége

Az elején már utaltam rá, hogy az objektum tulajdonságokból és viselkedésekből áll. Az előzőt nevezzük változóknak (adat), az utóbbit metódusoknak (kód). A kettő közötti kapcsolat az, hogy a változókat a metódusok kezelik, metódusok nélkül csak a bennük lévő egyetlen adat tárolására alkalmasak. Ezek szorosan együttműködve alkotják az objektum nagy részét. Az osztály ismertetésénél elmondom a többi alkotóelemet is.

Zártság

Az előzőekben tisztáztuk, hogy a változókat a metódusok kezelik. Ez alaphelyzetben nem így van, a változókhoz kívülről, direkt formában is hozzá lehet férni, ha nem vigyázunk. Vagyis metódusok nélkül is kezelhetőek. Sajnos. Ez viszont mindig roppant veszélyes. Egyrészt nem tudjuk biztosítani, hogy csak az kezelhesse, aki erre jogosult, másrészt lehet, hogy fogalmunk sincs az osztály felépítéséről, valamint hogy a változóit hogyan is kell kezelni.

Tegyük fel, írtál egy kutyát. Van egy éhséget, pontosabban annak mértékét tároló változója. Te úgy gondoltad, hogy ez a [0;10] intervallumból vehet majd fel értéket. A 0 jelenti azt, hogy a kutya nem éhes, tele van, a 10 pedig a majd éhen hal állapotot. És ha valaki ezt kívülről ellenőrzés nélkül -3-ra állítja? Fejre áll a programod. Azt, hogy egy változó milyen értékeket vehet fel, csak egyvalaki tudja jól, önmaga. (Meg remélhetőleg az, aki megírta) Ahhoz, hogy egy objektumot biztonsággal kezelhessünk, meg kell védenie a változóit, be kell zárni őket. Ha az objektum változóit priváttá tesszük, akkor csak és kizárólag ő férhet hozzájuk közvetlenül, mindenki más csak a metódusain keresztül ellenőrzött körülmények között használhatja. Ez szintaktikailag csak annyit jelent, hogy a változó deklarációját a típus megadása előtt a private kulcsszóval kell kiegészíteni:

private int ehseg;

Távirányítók

Az objektumok megszületésükkor az úgynevezett kupacra kerülnek. Mi az objektumhoz nem férhetünk hozzá közvetlenül, egyedül a címe az, amivel hivatkozhatunk rá. Egy változóban eltárolhatjuk, sőt el is kell tárolni az objektum címét. Így a változóval bármikor elérhetjük őt, beszélgethetünk vele, érdeklődhetünk a változói felől, stb. Erre egy példa:

Dog mydog;
mydog = new Dog("Buksi", "Tacsko", "Barna");

A mydog annak a változónak a neve, egyfajta “távirányító” lesz a létrehozott Dog objektumhoz. Ez is egy egyszerű változó deklaráció: típus változónév; Aztán teremtünk egy Kutyát! Előre megmondjuk, milyen tulajdonságai legyenek. Vedd észre, hogy az objektum címét tároló változó neve nem olyan típusú, amit eddig megszokhattunk. Azt is kiszúrhattad, hogy nagy betűvel írtam. Nem véletlenül. Ugye még mindig emlékszel, miket írunk nagy kezdőbetűvel?

Nagyon fontos dolog: Ne veszítsd el a távirányítódat, és ne is programozd át egy másik Dog-ra! Akkor az objektumodat is elvesztetted! A távirányítóval tudod kezelni a kutyádat. Megetetheted, megkérheted, hogy ugasson, stb.

A new egy speciális operátor, ami egy adott osztályból egy új példányt hoz létre. Ez a létrejött, betöltődött, beállított példány lesz a misztikus Objektum. A new utáni részt konstruktornak nevezzük, mindjárt előszedjük ezt is.

Szép dolog az objektum, szinte még el se kezdtem beszélni róla, de akkor miből is lesz? Osztályból.

Osztályok

Amikor egy Objektumot létrehozunk, azt valójában egy osztályból hozzuk létre. Az osztály egy megírt Java forráskód, aminek nagy vonalakban a következő részei vannak:

  1. Változók
  2. Metódusok
  3. Konstrukciós műveletek

Az első kettőről már hallottál, az utolsó volt az, amit megígértem, hogy előszedjük. A konstrukciós műveletek olyan speciális részei az osztálynak, amelyben azt írjuk le, hogy az objektumot hogyan kell felépíteni, létrehozni a terv alapján.

Az osztály az objektumok terve!

public class Dog
{
// Változók
  String nev;
  String fajta;
  String szin;

// Metódusok
  public String getNev()
  {
    return nev;
  }

  public String getFajta()
  {
    return fajta;
  }

  public String getSzin()
  {
    return szin;
  }

// Konstruktor
  public Dog(String nev, String fajta, String szin)
  {
    this.nev = nev;
    this.fajta = fajta;
    this.szin = szin;
  }
}

Amikor az osztályból egy új példányt szeretnénk létrehozni, a konstruktor az, amit megkérünk, hogy építse fel nekünk. Kell egy kutya! Lássuk akkor újra azt a pillanatot, amikor tényleg létrehozzuk egy kutyát. A terv már megvan, már csak le kell gyártani.

d = new Dog("Buksi", "Tacsko", "Barna");

Tehát a new utáni rész a konstruktor, ami a példában az osztály végén látható. Amikor egy kutyát létre akarunk hozni, meg kell mondanunk a nevét, fajtáját és színét. Pontosan ebben a sorrendben. Ha ezt nem tesszük meg, nem lesz kutyánk!

Miért kell megadni mindhármat? Nem lehet ezt megadni később? Lehetne, de ha megnézed, a Dog osztály konstruktora 3 paramétert vár, mert csak akkor tudja a kutyádat felépíteni, ha ezeket megkapja. Mindet. Pontosan ebben a sorrendben! Ha ezekből akár egy is hiányzik (vagy több van), akkor már a fordítótól hibaüzenetet fogsz kapni, mert ő nyilvántartja, hogy melyik tervnek hány és milyen típusú bemenő adat kell ahhoz, hogy objektumot hozhasson létre.

Egy osztálynak lehet több konstruktora is!

Ezt hívjuk a konstruktor túlterhelésének. Lehet olyan is, hogy a kutya összes adatát meg kell adni. Lehet olyan is, hogy csak a nevét. Lehet olyan is, hogy a nevét és a fajtáját. Ezzel kapcsolatban fontos tudnivaló:

Csak olyan konstruktorok lehetnek egy osztályban melyek a paraméterek számában és típusában is eltérnek!

Így hibás, mert a két konstruktor azonos típusú és számú paraméterrel rendelkezik:

public class Dog
{
  String nev;
  String fajta;
  String szin;

// Metódusok... nem idézem újra, elképzeled

// Konstruktor 1
  public Dog(String nev, String fajta)
  {
    this.nev = nev;
    this.fajta = fajta;
  }

// Konstruktor 2
  public Dog(String nev, String szin)
  {
    this.nev = nev;
    this.szin = szin;
  }
}

Egy ilyen példa esetén a második konstruktorra hibát fog jelezni a fordító, mert az osztály már rendelkezik olyan konstruktorral, amely két Stringet vár paraméterként.

Nagyon fontos, hogy a létrehozott objektumnak azonnal legyen egy rá mutató változója is (távirányító), különben úgy kerül a kupacra, hogy senkihez nem tartozik, így hamarosan csúnya véget ér! Vagyis soha, de soha ne csinálj ilyet (ha szereted az állatokat).

Hibás:

new Dog("Bodri", "Kuvasz", "Feher");

Szegény Bodri…

Csakis úgy példányosítsd, hogy az objektumot egy osztállyal megegyező típusú változóban, tömbben, listában, stb tárolod el. Azért, hogy később használni tudd, és ne kóboroljon a kupacon, amíg nem jön a sintér.

Helyes:

Dog d = new Dog("Bodri", "Kuvasz", "Feher");
// vagy
Dog[] kutyak;
kutyak[0] = new Dog("Bodri", "Kuvasz", "Feher");
// a listáról később...

Természetesen több kutyát is létrehozhatunk ebből az osztályból, csak legyen elég helyünk tárolni őket. Az osztályok egyik fontos szerepe az újrahasznosíthatóság. Nem kell 3 külön kódot írni arra, hogy legyen 3 kutyád, elég ugyanabból az osztályból létrehozni őket, de a tulajdonságaik lehetőleg legyenek különbözőek. Látható az is, hogy ha tényleg ilyen egyformák a kutyák, akkor minek készítsek nekik külön tervet? Hiszen ugyanolyan tulajdonságaik vannak, pontosabban a tulajdonságok típusa egyezik meg. A konkrét értékeik mások. Ezt az azonos tulajdonságok és viselkedések szerinti összevonást nevezzük osztályozásnak. Akkor ezt ismételjük meg újra, mert ez egy fontos dolog:

Osztályozásnak nevezzük azt a folyamatot, amikor az általunk modellezni kívánt valódi világbeli objektumokat közös tulajdonságok és viselkedések alapján csoportokba soroljuk.

Azt is meg kell jegyeznem, hogy még ha két tulajdonságaikban teljesen ugyanolyan kutyát hozok létre, az objektumaik akkor sem lesznek ugyanazok:

d1 = new Dog("Buksi", "Tacsko", "Barna");
d2 = new Dog("Buksi", "Tacsko", "Barna");

System.out.println(d1 == d2); // false

A fenti példában a két kutya minden tulajdonsága megegyezik, de a két objektum mégsem egyforma. Két teljesen különálló valami van ott a kupacon, de attól még nincsenek összekötve. Amikor használod a new-t, akkor mindig egy új objektumot teremtesz. Akkor is, ha már van egy látszólag pont ugyanolyan tulajdonságokkal rendelkező. Azt, hogy két objektum mikor egyenlő, azt csakis Te határozhatod meg, a Java nem találhatja ki helyetted. Ezt majd a saját objektum témakörben megtanuljuk.

Végezetül térjünk vissza picit az osztályozáshoz. Ha tehát háziállatokat szeretnénk modellezni, akkor a lábaik száma, viselkedésük, táplálékuk, stb szerint csoportokba lehet őket sorolni. Mondjuk ezt előttünk a biológusok megtették, hiszen a fajokba sorolás is osztályozás. De az azonos tulajdonságok és azonos viselkedések alapján elég egy tervet írni az egy osztályba tartozó állatoknak. Emlékszel, a tulajdonságokat változóknak, a viselkedéseket metódusoknak nevezzük. Ezek az osztályok tehát a megírt forráskódok, és ezeket használjuk akkor, amikor a programjainkat működtetjük.

Objektumból egyelőre példának legyen elég ez a kutya, később készítünk bonyolultabbakat is, amelyek már tényleg működnek is!

Következő lecke: Tömbök

10 Replies to “Java programozás 13. – Osztályok és objektumok”

  1. Pingback: Java programozás 15. – String |

  2. Üdvözlöm a Szerzőt!
    …és mindenkit, aki itt jár…

    Nem tudom, miért pont ide írom, mert igazából eddig az összes leckénél megtehettem volna:
    Tetszik
    -az összeszedettsége,
    -a tömörsége,
    -hogy utóbbi ellenére érthető, emészthető,
    -valamint, hogy jól van megközelítve…

    Ez az első magyar nyelven elérhető, HASZNOS, KEZDŐK SZÁMÁRA is érthető anyag, amit találtam.
    …pedig kerestem, mert kell a munkámhoz…

    Szóval: KÖSZÖNÖM!!! 😉

    • Én köszönöm az elismerést 🙂 Sajnos mostanság kevés időm van az oldalra, pár lecke még tervben van, de addig remélem ez a meglévő tananyag is sok embert elindíthat az úton, hogy utána már egyedül is tudjon haladni. Kéretik terjeszteni 🙂

  3. Pingback: Java programozás 12. – Ciklusok

  4. Pingback: Java programozás 21. – Fájlkezelés alapjai

  5. Egy teljes programkódot nem lehetne ide betenni? Nekem nem megy, nem hasonlítja össze a d1 és d2 kutyát, mert dinamikus meg statikus változó hiba gondja van.
    public class Kutyak {
    public static void main(String[] args) {
    System.out.println(d1);
    }
    public class Dog
    {
    String nev;
    String fajta;
    String szin;
    // Konstruktor 1
    public Dog(String nev, String fajta, String szin)
    {
    this.nev = nev;
    this.fajta = fajta;
    this.szin = szin;
    }
    }
    Dog d1 = new Dog(“Bodri”, “Kuvasz”, “Feher”);
    Dog d2 = new Dog(“Guszti”, “Kuvasz”, “Feher”);
    }

    • Egy példakód a tiédet átalakítva, bár őszintén szólva nem szeretem, ha ugyanabban a fájlban több osztály is szerepel. Később, amint időm engedi, írok egy komplexebb példát.

      class Dog
      {
      String nev;
      String fajta;
      String szin;

      // Konstruktor 1
      public Dog(String nev, String fajta, String szin)
      {
      this.nev = nev;
      this.fajta = fajta;
      this.szin = szin;
      }
      }

      public class Kutya
      {
      public static void main(String[] args)
      {
      Dog d1 = new Dog(“Bodri”, “Kuvasz”, “Feher”);
      Dog d2 = new Dog(“Guszti”, “Kuvasz”, “Feher”);
      System.out.println(d1);
      }
      }

  6. Nem csak tanulságos, lényegretörő és jól érthető, hanem roppant szórakoztató is, remek humorral sózva!
    Köszi!

    • Bárcsak annyi időm lenne rá, amennyit szeretnék 🙂 Pár lyukat még be kellene tömködni benne, meg jól átnézni a példákat benne, mert még a mai napig találok hajmeresztő dolgokat a megoldásaimban 🙂

Hozzászólás a(z) Mikó Csaba bejegyzéshez Válasz megszakítása

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük

*

Ez az oldal az Akismet szolgáltatást használja a spam csökkentésére. Ismerje meg a hozzászólás adatainak feldolgozását .