Gyakorlati alapok II.

Alapszinten a static módosítóról

 

A static módosító működésének bemutatásakor a legtöbb programozási szakkönyv nagyon részletgazdag. Ezzel nincs is gond, azzal viszont azzal igen, hogy a static problémája bár már viszonylag hamar felmerül, teljes megértése azonban messze túlnyúlik egy adott tudásszinten, hiszen adott pontján meg kell ismernünk a példányosítás, az öröklődés, a polimorfizmus mechanizmusait is. Másodsorban pedig a static módosító használata szinte mindenre hatással van, aminek átlátása szintén nem kezdők szintje, sőt megjegyzem: a jelen tudásszinten nem is lényeges. Mivel mindez még nincs tisztázva, egy másik oldalról, a memóriahely-foglalás felől próbálom megközelíteni a static módosító magyarázatát.

 

A műveleti memória az a hely, ahová a programkód, tulajdonképpen minden program és annak minden adata betöltődik. Az utasításokat végrehajtó processzor innen emeli ki és hajtja végre a programok adatait és utasításait. Maga a műveleti memória, mint úgynevezett elsődleges memória processzortípustól és számítógépes architektúrától függően több részből áll:

  1. processzor-regiszterek - az éppen futó programok tárhelyei, a processzoron belül,

  2. gyorsítótárak (cache) - gyors elérésű átmeneti memóriaterületek, általában a processzoron belül,

  3. operatív tár (RAM) - a fő memória, külön hardveregység.

A műveleti memória teljes adminisztrációját a futó operációs rendszer végzi, de Java-alkalmazás indítása -még mindig az operációs rendszer felügyelete alatt-, már a JVM (Java Virtual Machine - a Java alkalmazásokat futtató, egyfajta mini operációs rendszer) feladata. Hiányában a Java-alkalmazás nem indul el és hibaüzenetet kapunk. Azonban programozás-technikailag lényegtelen, sőt az átlagos programozó számára nem is követhető (szakszóval transzparens), hogy a JVM miképpen gazdálkodik a számára kirendelt memóriaterülettel, ebből következően a továbbiakban ezt egységesen műveleti memória névvel illetem.

 

A műveleti memória optimális kezelése komplex probléma. Azt gondolhatjuk, hogy a leghatékonyabb memóriakezelés nyilvánvalóan az, ha a programkód villámgyorsan töltődik be a processzor-regiszterekbe és felhasználás után onnan ugyanilyen lendülettel távozik is. Ez a dinamikus memóriakezelés csúcsa. Azonban gondoljunk bele: mi történik olyan esetekben, amikor adott programkódokat folyamatosan kell felhasználnunk?

Adott például egy matematikai program, amelyik számításai során folyamatosan használja a PI értékét (final double PI = 3.1415926535;). Nem volna bölcs megoldás az első használat után dinamikusan kilapátolni a gyors elérésű processzor-regiszterből, sőt a dinamikussággal ellentétben az volna optimális, ha olyan memóriaterületre kerülne, amelyik statikusan, biztonságosan őrzi értékét, hogy az összes programkód villámgyorsan hozzáférhessen.

 

www.informatika-programozas.hu - Ezt most meg kell tanulni!

 

A statikusság tehát -ellentétben a dinamikussággal-, mindig robusztusabb, stabilabb, ellenállóbb építmény. Az építmény a mi esetünkben a műveleti memóriaterület egy adott területe, ahová a JVM a Java-program kódjait a futtatáshoz behelyezi. A static módosítóval illetett programkomponensek azonnal bekerülnek a műveleti memória egy, a JVM által kijelölt, statikus területére és onnan "nem is mozdulnak el". Pontosan ez a cél: hogy bizonyos programkomponensek számára jobban látható, stabilabb, ezáltal azonnali elérhetőséget biztosítson.

 

A static módosító elsődleges célja tehát, hogy azonnal és jól szegmentáltan létrejöjjön a programkomponens memóriahely-foglalása. Ebből következően most már megérthetjük, hogy miért van a main főprogram deklarációjában is static jelző:

 

public static void main(String[] args)

 

Attól függetlenül, hogy a main() még nem csinál semmit, statikussága máris biztosít számára egy jól szegmentált és biztonságos memóriaterületet. Ezért a main-be illesztett bármilyen (helyesen programozott) funkció azonnal le fog futni, például az alábbi egyszerű hibaüzenet:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
        System.out.println("Ez egy hibaüzenet!");
    }
}

 

Végeredmény:

Ez egy hibaüzenet!

 

Ha a hibaüzenetet külső metódusba tesszük, az a main() indításakor csakis abban az esetben fog lefutni, ha az szintén static jelzővel illetett, hiszen akkor azonnal megtörténik a memóriahely-foglalás:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {


    public static void hibaUzenet(){
        System.out.println("Ez egy hibaüzenet!");
    }

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

 

Végeredmény:

Ez egy hibaüzenet!

 

Ha azonban a külső metódustól elvesszük a static jelzőt, valós, JVM-szintű hibaüzenetet kapunk:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public void hibaUzenet(){
        System.out.println("Ez egy hibaüzenet!");
    }

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

 

Végeredmény:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Cannot make a static reference to the non-static method hibaUzenet() from the type Main at Main.main(Main.java:8)

 

E mögött egy egyszerű hozzáférési szabály húzódik meg:

 

www.informatika-programozas.hu - Ezt most meg kell tanulni!

 

Statikus változók és metódusok nem férhetnek hozzá NEM statikus változókhoz, metódusokhoz. Viszont NEM statikus változók és metódusok hozzáférhetnek  statikus változókhoz, metódusokhoz, hiszen éppen ez a statikusság fő előnye.