7. Objektumok használata

Egy tipikus Java program sok objektumot hoz létre, amik üzenetek küldésével hatnak egymásra. Ezeken keresztül tud egy program különböző feladatokat végrehajtani. Amikor egy objektum befejezi a működését, az erőforrásai felszabadulnak, hogy más objektumok használhassák azokat.

A következő CreateObjectDemo program három objektumot hoz létre: egy Point és két Rectangle objektumot:

public class CreateObjectDemo {
    public static void main(String[] args) {
        Point originOne = new Point(23, 94);
        Rectangle rectOne =
            new Rectangle(originOne, 100, 200);
        Rectangle rectTwo = new Rectangle(50, 100);
        System.out.println("Width of rectOne: " +
            rectOne.width);
        System.out.println("Height of rectOne: " +
            rectOne.height);
        System.out.println("Area of rectOne: "
            + rectOne.area());
        rectTwo.origin = originOne;
        System.out.println("X Position of rectTwo: "
            + rectTwo.origin.x);
        System.out.println("Y Position of rectTwo: "
        + rectTwo.origin.y);
        rectTwo.move(40, 72);
        System.out.println("X Position of rectTwo: "
            + rectTwo.origin.x);
        System.out.println("Y Position of rectTwo: "
            + rectTwo.origin.y);
    }
}

Ez a program létrehoz, megváltoztat és információt ír ki különböző objektumokról. Kimenete:

Width of rectOne: 100
Height of rectOne: 200
Area of rectOne: 20000
X Position of rectTwo: 23
Y Position of rectTwo: 94
X Position of rectTwo: 40
Y Position of rectTwo: 72

7.1. Objektumok létrehozása

Az objektum alapját egy osztály szolgáltatja, osztályból hozunk létre (példányosítunk) objektumot. A következő sorok objektumokat hoznak létre, és változókhoz rendelik őket:

Point originOne = new Point(23, 94);
Rectangle rectOne = new Rectangle(originOne, 100, 200);
Rectangle rectTwo = new Rectangle(50, 100);

Az első sor egy Point osztályból, a második és harmadik a Rectangle osztályból hoz létre objektumot.

Minden sor a következőket tartalmazza:

  • Deklaráció: Az = előtti részek a deklarációk, amik a változókhoz rendelik hozzá az objektum típusokat.
  • Példányosítás: A new szó egy Java operátor, ami létrehoz egy objektumot.
  • Inicializáció: A new operátort egy konsturktorhívás követi. Pl. a Point (23,94) meghívja a Point egyetlen konstruktorát. A konstruktor inicializálja az új objektumot.

7.1.1. Változó deklarálása objektum hivatkozásként

Egy változót a következőképpen deklarálunk:

type name;

Ez közli a fordítóval, hogy a name tagot használjuk egy adatra hivatkozáshoz, aminek a típusa type. A Java nyelv a változó típusokat két fő kategóriára osztja: egyszerű típusok és hivatkozás (referencia) típusok.

Az egyszerű típusok (byte, short, int, long, char, float, double, boolean) mindig egyszerű értékeket tartalmaznak az adott típusból.

A hivatkozás típusok azonban némileg összetettebbek. A következő módok bármelyike szerint deklarálhatók:

A deklarált típus megegyezik az objektum osztályával:

MyClass myObject = new MyClass();

A deklarált típus egy szülő osztálya az objektum osztályának:

MyParent myObject = new MyClass();

A deklarált típus egy interfész, amit az objektum osztálya implementál:

MyInterface myObject = new MyClass();

Így is deklarálható egy változó:

MyClass myObject;

Ha ezt használjuk, a myObject értéke automatikusan null lesz, amíg egy objektum ténylegesen létre nem lesz hozva és hozzárendelve. A változó deklaráció önmagában nem hoz létre objektumot. Ehhez a new operátort kell használni.

Amíg egy változó nem tartalmaz hivatkozást objektumra, null hivatkozást tartalmaz. Ha az originOne változót ilyen módon deklaráljuk, akkor a következőképen illusztrálható (változó neve a hivatkozással, ami nem mutat sehova):

originOne

7.1.2. Objektum példányosítása

A new operátor egy példányt hoz létre egy osztályból, és memóriaterületet foglal az új objektumnak.

A new operátor után szükség van egy osztályra, ami egyben egy konstruktor hívást is előír. A konstruktor neve adja meg, hogy melyik osztályból kell példányt létrehozni. A konstruktor inicializálja az új objektumot.

A new operátor egy hivatkozást ad vissza a létrehozott objektumra. Gyakran ezt a hivatkozást hozzárendeljük egy változóhoz. Ha a hivatkozás nincs hozzárendelve változóhoz, az objektumot nem lehet majd elérni, miután a new operátort tartalmazó utasítás végrehajtódott. Az ilyen objektumot névtelen objektumnak is szoktuk nevezni.

Megjegyzés: A névtelen objektumok nem olyan ritkák, mint ahogy azt gondolhatnánk. Pl. egy tömbbe vagy tárolóba helyezett objektum is névtelen, hiszen nincs saját, névvel ellátott hivatkozása.

7.1.3. Objektum inicializálása

A Point osztály kódja:

public class Point {
    public int x = 0;
    public int y = 0;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Ez az osztály egy konstruktort tartalmaz. A konstruktornak ugyanaz a neve, mint az osztálynak, és nincs visszatérési értéke. A Point osztály konstruktora két egész típusú paramétert kap: (int x, int y). A következő utasítás a 23 és 94 értékeket adja át paraméterként:

Point originOne = new Point(23, 94);

Ennek a hatását mutatja a következő ábra:

originOne objektum

A Rectangle osztály négy konstruktort tartalmaz:

public class Rectangle {
    public int width = 0;
    public int height = 0;
    public Point origin;
    public Rectangle() {
        origin = new Point(0, 0);
    }
    public Rectangle(Point p) {
        origin = p;
    }
    public Rectangle(int w, int h) {
        this(new Point(0, 0), w, h);
    }
    public Rectangle(Point p, int w, int h) {
        origin = p;
        width = w;
        height = h;
    }
    public void move(int x, int y) {
        origin.x = x;
        origin.y = y;
    }
    public int area() {
        return width * height;
    }
}

Akármelyik konstruktorral kezdeti értéket adhatunk a téglalapnak, különböző szempontok szerint: a koordinátái (origin); szélessége és magassága; mind a három; vagy egyik sem.

Ha egy osztálynak több konstruktora van, mindnek ugyanaz a neve, de különböző számú vagy különböző típusú paraméterekkel rendelkezik. A Java platform a konstruktort a paraméterek száma és típusa alapján különbözteti meg. A következő kódnál a Rectangle osztálynak azt a konstruktorát kell meghívnia, ami paraméterként egy Point objektumot és két egészet vár:

Rectangle rectOne = new Rectangle(originOne, 100, 200);

Ez a hívás inicializálja a téglalap origin változóját az originOne-nal (originOne értékét veszi fel az origin változó), ami egy Point objektumra hivatkozik, a width értéke 100-zal lesz egyenlő, a height értékét 200-zal. Most már két hivatkozás van ugyanarra a Point objektumra; egy objektumra több hivatkozás is lehet:

rectangle objektum

A következő sor két egész típusú paraméterrel rendelkező konstruktort hívja meg, amik a width és height értékei. Ha megnézzük a konstruktor kódját, látjuk, hogy létrehoz egy új Point objektumot, aminek az x és y értéke is nulla lesz:

Rectangle rectTwo = new Rectangle(50, 100);

Ez a konstruktor nem vár paramétereket, paraméter nélküli konstruktor:

Rectangle rect = new Rectangle();

Ha egy osztály nem deklarál egy konstruktort se, akkor a Java platform automatikusan szolgáltat egy paraméter nélküli (alapértelmezett, default) konstruktort, ami „nem csinál semmit”. Így minden osztálynak van legalább egy konstruktora.

7.2. Hivatkozás egy objektum tagjaira

Ha létrehozunk egy objektumot, valószínűleg használni is akarjuk valamire. Ennek két módja lehet:

  • módosítani vagy megnézni a tagváltozóit
  • meghívni a metódusait

Általános formája:

objectReference.variableName

Amikor egy változó az érvényességi körén belül van – itt az osztályon belül –, használható az egyszerű hivatkozás is.

A hivatkozás első része egy objektum referencia kell, hogy legyen. Lehet használni egy objektum nevét, vagy egy kifejezést, ami egy objektum-hivatkozással tér vissza. A new operátor egy hivatkozással tér vissza, ezzel hozzáférhetünk egy új objektum változóihoz:

int height = new Rectangle().height;

Miután ez végrehajtódik, többé nem lehet hivatkozni a létrejött Rectangle objektumra, mert nem tároltuk el a hivatkozást egy változóban.

A tagváltozók hozzáférhetősége

Konvenció szerint egy objektum tagváltozóit más objektum vagy osztály közvetlenül nem módosíthatja, mert lehet, hogy értelmetlen érték kerülne bele.

Ehelyett, hogy a változtatást megengedje, egy osztály biztosíthat metódusokat, amiken keresztül más objektumok megnézhetik, illetve módosíthatják a változóit. Ezek a metódusok biztosítják, hogy megfelelő konzisztens érték kerüljön a változóba. (Például a Cat osztályban a macska súlya ne legyen negatív.) A másik előnyük, hogy az osztály megváltoztathatja a változó nevét és típusát anélkül, hogy hatással lenne a klienseire.

Azonban néha szükség lehet arra, hogy közvetlen hozzáférést biztosítsunk a változókhoz. Ezt úgy tehetjük meg, hogy a public szót írjuk eléjük, a private-tel pedig tilthatjuk a külső hozzáférést.

7.3. Metódushívás

Itt is használható ugyanaz a forma, mint a változóknál. A metódus neve utáni zárójelekben adhatók meg a paraméterek. Ha nincsenek, üres zárójelet kell írnunk.

objectReference.methodName(argumentList);
objectReference.methodName();

Az objektum referencia itt is lehet változó vagy kifejezés:

new Rectangle(100, 50).area();

A kifejezés egy hivatkozást ad vissza a Rectangle objektumra. A pont után írva a metódus nevét, az végrehajtódik az új Rectangle objektumon.

Néhány eljárásnak van visszatérési értéke, ezért ezek kifejezésekben is használhatóak. A visszaadott értéket változóhoz rendelhetjük:

int areaOfRectangle = new Rectangle(100, 50).area();

Metódusok hozzáférhetősége

Ugyanúgy működik, mint a változókhoz való hozzáférés. A metódusokhoz való hozzáférést is a public kulcsszóval engedélyezhetjük más objektumoknak, a private-tel pedig tilthatjuk.

7.4. Nem használt objektumok eltávolítása

A Java platform lehetővé teszi annyi objektum létrehozását, amennyit csak akarunk (korlát csak az, hogy mennyit tud kezelni a rendszerünk), és nem kell aggódnunk a megszüntetésük miatt. A futtatókörnyezet törli az objektumokat, ha többet már nem használjuk. Ez a szemétgyűjtés.

Egy objektum akkor törölhető, ha már nincs rá több hivatkozás. Meg lehet szüntetni egy objektumot úgy is, hogy a hivatkozását null értékre állítjuk. Egy objektumra több hivatkozás is lehet, ezeket mind meg kell szüntetni, hogy az objektum a szemétgyűjtőbe kerülhessen.

A szemétgyűjtő

A szemétgyűjtő periódikusan felszabadítja a már nem használt objektumok által foglalt memóriát. Automatikusan végzi a dolgát, bár néha szükség lehet rá, hogy közvetlen meghívjuk. Ezt a System osztály gc metódusával tehetjük meg. Olyankor lehet rá szükség, ha egy kódrész sok szemetet hoz létre, vagy egy következő kódrésznek sok memóriára van szüksége. Általában elegendő hagyni, hogy magától fusson le.

Megjegyzés: Egyes fejlesztők a Java egyik gyenge pontjának tartják a memóriakezelését. Aki úgy gondolja, hogy tud jobb megoldást az Oracle programozói által létrehozottnál, akár a sajátját is használhatja.

7.5. Takarítás

Mielőtt egy objektum a szemétgyűjtőbe kerülne, a gyűjtő lehetőséget ad az objektumnak, hogy kitakarítson maga után úgy, hogy meghívja az objektum finalize metódusát.

A finalize metódus az Object osztály tagja, ami minden osztály szülőosztálya, a hierarchia tetején áll. Egy osztály felülírhatja a finalize metódust, ha szükséges, de ekkor a metódus legvégén meg kell hívnia a super.finalize() függvényt.

7.6. Ellenőrző kérdések

  • Mikor lehet egy objektumot paraméter nélkül létrehozni? Mikor nem?
  • Hogy lehet egy objektum adattagjainak kezdőértékét beállítani? Mi történik, ha ezt nem tesszük meg?
  • Mi történik azokkal az objektumokkal, amelyekre már nem hivatkozik a futó program?
  • Mikor fut le a szemétgyűjtő algoritmus? Hogyan működik?
  • Hogy néz ki Javában a destruktor?

Mi a hiba a következő programban?

public class SomethingIsWrong {
    public static void main(String[] args) {
        Rectangle myRect;
        myRect.width = 40;
        myRect.height = 50;
        System.out.println("myRect's area is " +
                           myRect.area());
    }
}

A következő kód létrehoz egy Point és egy Rectangle objektumot.

Hány referencia hivatkozik az objektumokra a következő kódrészlet lefutása után? Ha lefut a szemétgyűjtés, melyik objektum fog megszűnni?

...
Point point = new Point(2,4);
Rectangle rectangle = new Rectangle(point, 20, 20);
point = null;