1. Változók inicializálása

Mint már tudod, az osztályodban több változót is deklarálhatsz, és nem csak deklarálhatod, hanem azonnal inicializálhatod is a kezdeti értékükkel.

És ugyanezek a változók inicializálhatók egy konstruktorban is. Ez azt jelenti, hogy elméletileg ezekhez a változókhoz kétszer is lehet értéket rendelni. Példa

Kód jegyzet
class Cat
{
   public String name;
   public int age = -1;

   public Cat(String name, int age)
   {
     this.name = name;
     this.age = age;
   }

   public Cat()
   {
     this.name = "Nameless";
   }
}



A ageváltozó kezdeti értéket kap




A kezdeti érték felülírásra kerül


Az életkor változó eltárolja a kezdeti értékét.
 Cat cat = new Cat("Whiskers", 2);
Ez megengedett: az első konstruktor meghívásra kerül
 Cat cat = new Cat();
Ez megengedett: a második konstruktor meghívásra kerül

Ez történik, amikor Cat cat = new Cat("Whiskers", 2);végrehajtják:

  • CatLétrejön egy objektum
  • Minden példányváltozó a kezdeti értékével inicializálva van
  • A konstruktor meghívása és kódja végrehajtásra kerül.

Más szóval, a változók először megkapják a kezdeti értéküket, és csak ezután kerül végrehajtásra a konstruktor kódja.


2. A változók inicializálásának sorrendje egy osztályban

A változókat nem csupán a konstruktor futása előtt inicializálják, hanem egy jól meghatározott sorrendben inicializálják őket: abban a sorrendben, amelyben deklarálják őket az osztályban.

Nézzünk néhány érdekes kódot:

Kód jegyzet
public class Solution
{
   public int a = b + c + 1;
   public int b = a + c + 2;
   public int c = a + b + 3;
}

Ez a kód nem fordítható le, mivel a aváltozó létrehozásakor még nincsenek b és c változók. De a kódot az alábbiak szerint is megírhatja - ez a kód lefordítható, és jól fog futni .

Kód jegyzet
public class Solution
{
   public int a;
   public int b = a + 2;
   public int c = a + b + 3;
}


0
0+2
0+2+3

De ne feledje, hogy a kódnak átláthatónak kell lennie a többi fejlesztő számára. Jobb, ha nem használunk ilyen technikákat, mert az rontja a kód olvashatóságát.

Itt emlékeznünk kell arra, hogy mielőtt a változókhoz értéket rendelnénk, alapértelmezett értékük van. A típusnál intez nulla.

Amikor a JVM inicializálja a aváltozót, egyszerűen hozzárendeli az int típus alapértelmezett értékét: 0.

Amikor eléri a értéket b, az a változó már ismert lesz, és van értéke, így a JVM 2 értéket rendel hozzá.

És amikor eléri a cváltozót, a aés bváltozók már inicializálva lesznek, így a JVM könnyen kiszámítja a kezdeti értéket c: 0+2+3.

Ha változót hoz létre egy metóduson belül, akkor nem használhatja azt, hacsak korábban nem adott hozzá értéket. De ez nem igaz egy osztály változóira! Ha egy osztály változójához nincs hozzárendelve kezdeti érték, akkor az alapértelmezett érték lesz hozzárendelve.


3. Állandók

Miközben az objektumok létrehozásának módját elemezzük, érdemes kitérni az állandók, azaz a változók inicializálására a módosítóval final.

Ha egy változóban van finalmódosító, akkor kezdőértéket kell hozzárendelni. Ezt már tudod, és nincs ebben semmi meglepő.

De amit nem tudsz, az az, hogy nem kell azonnal hozzárendelni a kezdeti értéket, ha a konstruktorban megadod. Ez tökéletesen működik egy végső változónál. Az egyetlen követelmény az, hogy ha több konstruktora van, akkor minden konstruktorban egy végső változóhoz kell hozzárendelni egy értéket.

Példa:

public class Cat
{
   public final int maxAge = 25;
   public final int maxWeight;

   public Cat (int weight)
   {
     this.maxWeight = weight; // Assign an initial value to the constant
   }
}


4. Kódolás konstruktorban

És még néhány fontos megjegyzés a konstruktorokról. Később, ahogy folytatja a Java tanulását, olyan dolgokkal fog találkozni, mint az öröklődés, a szerializálás, a kivételek stb. Ezek mind különböző mértékben befolyásolják a konstruktorok munkáját. Nincs értelme most mélyen belemerülni ezekbe a témákba, de kötelesek vagyunk legalább érinteni őket.

Például itt van egy fontos megjegyzés a konstruktorokról. Elméletileg bármilyen bonyolultságú kód írható egy konstruktorba. De ne tedd ezt. Példa:

class FilePrinter
{
   public String content;

   public FilePrinter(String filename) throws Exception
   {
      FileInputStream input = new FileInputStream(filename);
      byte[] buffer = input.readAllBytes();
      this.content = new String(buffer);
   }

   public void printFile()
   {
      System.out.println(content);
   }
}






Fájl olvasási adatfolyam megnyitása A fájl
beolvasása bájttömbbe
Mentse el a bájttömböt karakterláncként




A fájl tartalmának megjelenítése a képernyőn

A FilePrinter osztálykonstruktorban azonnal megnyitottunk egy bájtfolyamot egy fájlon, és elolvastuk a tartalmát. Ez összetett viselkedés, és hibákhoz vezethet.

Mi van, ha nem lenne ilyen fájl? Mi van, ha problémák adódtak a fájl olvasásával? Mi van, ha túl nagy volt?

Az összetett logika a hibák nagy valószínűségét jelenti, és ez azt jelenti, hogy a kódnak megfelelően kell kezelnie a kivételeket.

1. példa – Sorozatosítás

Egy szabványos Java programban rengeteg olyan helyzet adódik, amikor nem Ön hozza létre az osztályának objektumait. Tegyük fel például, hogy úgy dönt, hogy elküld egy objektumot a hálózaton keresztül: ebben az esetben a Java gép maga alakítja át az objektumot bájtok készletévé, elküldi, és újra létrehozza az objektumot a bájtkészletből.

De akkor tegyük fel, hogy a fájl nem létezik a másik számítógépen. Hiba lesz a konstruktorban, és senki sem fogja kezelni. És ez eléggé képes a program leállására.

2. példa – Egy osztály mezőinek inicializálása

Ha az osztálykonstruktor képes ellenőrzött kivételeket dobni, azaz a throws kulcsszóval van megjelölve, akkor a jelzett kivételeket meg kell fogni az objektumot létrehozó metódusban.

De mi van, ha nincs ilyen módszer? Példa:

Kód  jegyzet
class Solution
{
   public FilePrinter reader = new FilePrinter("c:\\readme.txt");
}
Ez a kód nem fordítható le.

Az FilePrinterosztálykonstruktor képes ellenőrzött kivételt dobni , ami azt jelenti, hogy nem hozhat létre FilePrinterobjektumot anélkül, hogy azt egy try-catch blokkba nem csomagolná. Try-catch blokkot pedig csak metódusban lehet írni



5. Alaposztályú konstruktor

Az előző leckéken az öröklődésről beszéltünk egy kicsit. Sajnos az öröklődésről és az OOP-ról szóló teljes tárgyalásunk az OOP-nak szentelt szintre van fenntartva, és a konstruktorok öröklődése már számunkra is aktuális.

Ha az osztály egy másik osztályt örököl, a szülő osztály egy objektuma be lesz ágyazva az osztály egyik objektumába. Sőt, a szülő osztálynak saját változói és saját konstruktorai vannak.

Ez azt jelenti, hogy nagyon fontos, hogy ismerje és megértse a változók inicializálását és a konstruktorok meghívását, amikor az osztálynak van szülőosztálya, és annak változóit és metódusait örököljük.

osztályok

Honnan tudjuk a változók inicializálásának és a konstruktorok meghívásának sorrendjét? Kezdjük azzal, hogy megírjuk két osztály kódját. Az egyik örökli a másikat:

Kód jegyzet
class ParentClass
{
   public String a;
   public String b;

   public ParentClass()
   {
   }
}

class ChildClass extends ParentClass
{
   public String c;
   public String d;

   public ChildClass()
   {
   }
}










Az ChildClass osztály örökli az ParentClassosztályt.

Meg kell határoznunk a változók inicializálásának és a konstruktorok meghívásának sorrendjét. A naplózás segít ebben.

Fakitermelés

A naplózás a program által futás közben végrehajtott műveletek rögzítésének folyamata úgy, hogy azokat a konzolra vagy egy fájlba írja.

Nagyon egyszerű megállapítani, hogy a konstruktort meghívták: a konstruktor törzsében írjon üzenetet a konzolnak. De hogyan lehet megállapítani, hogy egy változó inicializálva van-e?

Valójában ez sem túl nehéz: írjon egy speciális metódust, amely visszaadja a változó inicializálásához használt értéket, és naplózza az inicializálást. Így nézhet ki a kód:

Végső kód

public class Main
{
   public static void main(String[] args)
   {
      ChildClass obj = new ChildClass();
   }

   public static String print(String text)
   {
      System.out.println(text);
      return text;
   }
}

class ParentClass
{
   public String a = Main.print("ParentClass.a");
   public String b = Main.print("ParentClass.b");

   public ParentClass()
   {
      Main.print("ParentClass.constructor");
   }
}

class ChildClass extends ParentClass
{
   public String c = Main.print("ChildClass.c");
   public String d = Main.print("ChildClass.d");

   public ChildClass()
   {
      Main.print("ChildClass.constructor");
   }
}




ChildClassObjektum létrehozása


Ez a metódus beírja az átadott szöveget a konzolba, és vissza is adja azt.





Deklarálja a Display szöveg ParentClassosztályt

, és inicializálja vele a változókat is.




Írjon üzenetet, hogy meghívták a konstruktort. Figyelmen kívül hagyja a visszatérési értéket.


Deklarálja a Display szöveg ChildClassosztályt

, és inicializálja vele a változókat is.




Írjon üzenetet, hogy meghívták a konstruktort. Figyelmen kívül hagyja a visszatérési értéket.

Ha végrehajtja ezt a kódot, a képernyőn a következő szöveg jelenik meg:

A metódus konzol kimeneteMain.print()
ParentClass.a
ParentClass.b
ParentClass.constructor
ChildClass.c
ChildClass.d
ChildClass.constructor

Így mindig személyesen megbizonyosodhat arról, hogy egy osztály változói inicializálódnak a konstruktor meghívása előtt. Az alaposztály teljesen inicializálásra kerül az örökölt osztály inicializálása előtt.