Elméleti alapozás
Ezt sajnos most kell tisztáznunk: mutató és referencia
A C programozási nyelv mutatója
A Java referenciája (hivatkozás)
Tudom, érthetetlen a mondat a kezdők számára, miszerint: ne ijedjünk meg, a Javában nincsenek mutatók! Ám a csapás folytatódik: de mégis vannak hasonló működésű referenciák!
A fogalmak tisztázásához egy kicsit el kell merülnünk a "klasszikus" C-nyelvi mutató programozás-technikai szépségeiben.
A C programozási nyelv mutatója
A C programozási nyelvben a mutató (pointer) egy olyan különleges változó, amelynek nem konkrét, típusos adattartalma van, hanem csak egy memóriacímet tartalmaz egy másik változó felé. Nézzük meg ennek szabványos, C nyelvi deklarációját:
int szam = 1;
int *mutato;
mutato = &szam;
A fenti deklarációt verbálisan úgy fogalmazhatjuk meg, hogy a mutato az int típusú szam változóra mutat, ennek szabványos hozzárendelő operátora a & jel. Ekkor tehát a mutato nem a szam adattartalmát (1), hanem a szam memóriacímét tartalmazza (ezt a rendszer osztja ki, mi közvetlenül nem láthatjuk).
A mutató által mutatott változó adattartalmát a * jellel érhetjük el. Például a fenti deklarációt továbbgörgetve...
int szam = 1;
int *mutato;
mutato = &szam;
int szam2 = *mutato;
...int szam2 felveszi szam adattartalmát, a jelen esetben 1 értéket.
A mutató talán legfontosabb tulajdonsága, hogy segítségével memóriaterületek (még pontosabban: dedikált memóriaterületeken lévő változók) közvetlenül (de persze még rendszerfelügyelet alatt) megcímezhetők, ezáltal elérhetők. (A hozzárendelés csak változókra és tömbelemekre alkalmazható és nem használhatjuk kifejezésekre, állandókra vagy regiszterváltozókra.) Rendkívül praktikus, ám ugyanakkor veszélyes eszköz is, hiszen a közvetlen memóriacímzés azt jelenti, hogy képes megtörni a kód alapjában szekvenciális vagy más módon rendszerezett futását és segítségével bárhová át tudunk ugrani. Ezzel természetesen a tapasztalt C-programozók is tisztában vannak és legtöbbször csak szabályozott és jól ellenőrzött körülmények között használják fel.
A Java referenciája (hivatkozás)
Nyilvánvaló előnyei ellenére tehát a C-mutató nagy hátránya a zabolátlan kódugrálás lehetősége. Olyan ez, mint amikor valaki biztonsági kerítést épít fel, de aztán jön Frog Feri és egyetlen ugrással átugorja. Ezt a biztonsági rést már a Java alkotói is észrevették és főként ezen tulajdonságát hivatott kiküszöbölni a Javában bevezetésre került referencia (hivatkozás).
A referencia is mutató, de közvetlenül nem címezhető meg (a Javában nem működőképesek a & és * operátorok); működése voltaképpen annyira rejtett és biztonságos, hogy szinte még azt se vesszük észre rajta, hogy mutató jellegű. Valóban ezen 2 tulajdonsága jellemző rá leginkább: rejtett és biztonságos.
Mivel angol nyelvű anyagból tanultam a Java alapjait, sokáig nem értettem a referencia (reference) fogalmát. Aztán egyszer beugrott, hogy ez valójában úgy viselkedik, mint egy mutató, csak jóval szabályozottabban, a szabályozást pedig egyértelműen a Java alkotói iktatták be a rendszerbe. Később az egyik programozási honlapon olvastam a referencia fogalmának egy remek megközelítését: képzeljük el, hogy van egy házunk, amelynek geohelyzete, vele címe nyilvánvalóan nem változtatható. Nyomtatunk viszont kártyákat, rajtuk a ház címével és ezeket osztogatjuk, akinek akarjuk.
A referenciák olyan speciális kártyák, amelyeken elsődlegesen memóriacímek vannak a változóik és objektumaik felé, mindegyiknek külön-külön egyedi azonosítással. Ugyanakkor fontos kihangsúlyoznunk, hogy a referencia mindig a változó tartalmát adja vissza, nem pedig memóriacímét, ezért bár a tartalom elérhető, de sohasem módosítható. Az utóbbi csakis akkor volna lehetséges, ha működne az & operátor, amely engedné a memóriacím tartalmát felülírni.
Néhány esetben kideríthető a referencia mutató jellege is, például amikor rosszul használjuk fel. Alább láthatunk egy ilyen kódot, amelyben kitöltjük a kártyát egy memóriacímmel (azaz létrehozunk egy obj nevű referenciát), ám a cím üres helyre, üres telekre mutat (amelyet most null kifejezéssel modellezünk). A JVM nem tud mit kezdeni vele, amint a Magyar Posta Rozikája is visszadobná "címzett ismeretlen" üzenettel. A rendszer dob egy kivételt, amely viszont a NullPointerException hibaüzenettel felfedi a referencia mutató jellegét:
public class Main {
public static void main(String[] args){
Object obj = null;
System.out.println(obj.getClass());
}
}
Végeredmény:
Exception in thread "main" java.lang.NullPointerException
at Main.main(Main.java:11)
Fontos azonban hangsúlyozni, hogy a lefordított gépi kódban mutató-, és referencia-utasítás között nem lehet különbséget tenni!