Elmélet - 1. java bevezető

|
1. java bevezető
|
bet | 2/51 | Sana | 07.04.2017 | Hajmi | 1,08 Mb. | | #3295 |
2.1. Elmélet
Az objektum alapú programozás közelebb hozta egymáshoz a programnyelv és a való élet fogalmait, így OOP alapon egyszerűbb és átláthatóbb programok születhetnek, amelyek karbantartása is egyszerűbb lehet, ha a programban használt fogalmak megközelítőleg fedik a valóságot. A jól megírt OOP szemléletű program a valóságot próbálja modellezni, ehhez több fogalmat is tisztázni kell.
Az egyik legfontosabb fogalom maga az objektum, egy-egy objektum a való élet egy-egy részét modellezi a programunkban: egy objektum lehet például egy üveg, egy kis sör vagy egy pohár. Minden objektumnak van állapota, vannak lehetséges műveletei és képes más objektumokkal együttműködni. Ez azt jelenti, hogy egy üveg sört ki tudunk nyitni, ezzel a "zárt" és "teli" állapotát a "nyitás" művelettel"nyitott" és "teli" állapotba hozni. Ha fogunk egy üres poharat, amelybe beletöltjük a nyitott üveg tartalmát, akkor a "kitöltés" művelet eredménye egy "teli" pohár illetve egy "üres" és "nyitott" sörösüveg. Természetesen a sörrel teli poharat az "ivás" művelettel üzemszerű módon tudjuk használni... :)
Ha kerítünk egy újabb üveg sört, akkor az már egy új objektum, de észre kell vennünk a két üveg sör közötti hasonlóságot. Ha szólunk a csinos pincércsajnak, hogy még egy üveg sört szeretnénk, akkor szinte biztosak lehetünk benne, hogy nem egy üveg bambit fog hozni. Ehhez mindkettőnknek tisztában kell lennünk az üveg sör fogalom jelentésével: a való életben a feltűnő hasonlóságok mentén - jobban mondva a közös jellemzők alapján - osztályokba tudjuk sorolni az objektumokat, egy-egy szavunk a dolgok egy-egy osztályát írja le. Az osztály határozza meg, hogy egy objektum milyen állapotokat vehet fel, milyen műveleteket lehet végezni vele, és milyen kölcsönhatások képzelhetők el más objektumokkal kapcsolatban. Az osztály neve egyedi kell legyen abban a környezetben, ahol hivatkozunk rá, ám fel kell készülnünk arra, hogy egy adott név másik környezetben mást jelent.
Mint mindent, a Java nyelv osztályait is példákon át ismerhetjük meg legjobban, ezért vegyük példának egy kocsma absztakcióját, hiszen oda mindenki naponta időnként eljár – tehát a továbbiakban egy kocsmát fogunk meglátogatni absztrahálni.
Nézzünk körül egy kocsmában, mit is látunk: alapvetően vannak dolgok. Ez egy igen magas absztrakciós szint, mivel teljesen elvonatkoztattunk attól, hogy a dolgok sörök, borok, zsíroskenyerek, üvegek, korsók, illetve poharak is lehetnek. Egy ilyen absztakció során az három dolgot kell megfigyelnünk a dologosztályba sorolt dolgokról:
-
milyen számunkra lényeges tulajdonságai vannak a dolgoknak
-
milyen számunkra lényeges műveleteket végezhetünk a dolgokon
-
milyen számunkra lényeges kölcsönhatások lehetnek a dolgok között
A számunkra lényeges azért van kiemelve, mivel ez az absztrakció lényege: csak azon tulajdonságokkalkell foglalkozunk, amelyek segítenek egymástól megkülönböztetni az osztályba sorolt különféle dolgokat, a többit figyelmen kívül kell hagynunk. Nézzünk újfent körül a kocsmában, és szedjük össze a dolgok közös-de-megkülönböztető jellemzőit. Az osztályt úgy célszerű elnevezznünk, hogy a neve a fogalmakat vagy a tárgyakat egy-két szóban jellemezze, és lehetőleg ne használjunk többes számot. Mivel a leírásnak minden egyes dologra illenie kell a kocsmában, ezért az osztály neve az lesz, hogy Dolog.
2.1.1. Az osztály
Soroljuk fel a kocsmában található összes kézzelfogható dolog lényeges és közös jellemzőjét. Mi az a tulajdonság, ami egyaránt jellemzi a sörösrekeszt, a sört, a zsíroskenyeret, a sörcsapot, a pultot és a kocsmárost? Hát... hm... ööö... izé. Az absztrakció nem is olyan egyszerű, nézzük pár ötletet:
-
Minden dolog kézzelfogható - nem jó, pont ezért elhagyható, mert minden dologra igaz.
-
Minden dolognak neve van - ez jó, a név megkülönbözteti a dolgokat.
-
Minden dolognak van mérete - nem jó, ez is elhagyható, mert minden dologra igaz.
-
Minden dolognak van kinézete - nem jó, egyrészt túl összetett, másrészt pedig nem szükséges nekünk a dolog kinézete.
Maradjunk annyiban, hogy a dolgoknak egyelőre csak neve van, ez kezdetnek egyszerű és jó. Az osztályokat legjobban és legtömörebben UML osztálydiagrammon lehet leírni, amint az a jobb oldali ábrán is egy ilyen látszik. Az UML ábra tetején láthatjuk az osztály nevét, amelyet egyszerűen úgy hívjuk, hogy Dolog. Az Attributes jelzi a tulajdonságokat, amelyek jellemzik az osztályt, jelen esetben egyetlen egy String típusú és név megnevezésű tulajdonságunk van csak - ahogy ezt már szövegesen le is írtuk. Az osztállyal tudunk műveleteket végezni, jelen esetben semmi különös műveletünk nincs, van egy konstruktor, illetve egy getter (lekérdező) és egy setter (beállító) metódus a névhez. Az attribútumok és a műveletek előtt látható private éspublic szó jelzi, hogy az adott jellemző vagy művelet elérhető-e az osztályon kívülről - értelemszerűen a public jelenti a publikus elérhetőséget, a private pedig a privát használatot - de ezekről később ejtünk több szót.
A Dolog osztály célja, hogy alapot adjon további osztályok definiálásához, ugyanis az objektum orientált programozás (és gondolkodás!) lényege az, hogy egyes osztályokból leszármaztatunk újabb osztályokat, amely esetben a leszármaztatott osztály specializáltabb lesz, mint az az osztály, amelyből leszármaztattuk (figyelem: nem szülő és gyermek osztály a nevük, célszerűbb az ős és a leszármazott szó használata). Az UML ábrán az osztály neve dőlt betűvel van szedve, amely azt jelenti, hogy ez az osztály absztrakt osztály lesz, így ezt nem lehet példányosítani, csak azokat az osztályokat, amelyeket ebből származtatunk le. A leszármaztatásról egy későbbi fejezetben lesz szó, egyelőre tegyük félre ezt a fogalmat.
2.1.1.1. A Java osztályok
Tekinsük meg az UML ábra osztályleírását Java nyelven is:
view plaincopy to clipboardprint?
-
package hu.javakocsma;
-
-
public abstract class Dolog
-
{
-
}
Haladjunk végig a fenti osztályon:
-
package csomagnév: a csomag neve, ahol az osztály található. Nem kötelező, de elvárt a megadása. A csomagnév meghatározza, hogy a fájlban lévő osztály melyik csomag része. Csomagokba vannak szervezve a Java környezet osztályai és az általunk írt osztályok is. Mivel a sokszáz-sokezer osztály között előfordulhatnak névazonosságok: a csomag neve különbözteti meg az azonos nevű osztályokat. A csomag neve mindig utal a benne lévő osztályok funkciójára, a java.io a be- és kimenet kezeléséért (fájlkezelés, írás, olvasás) felelős osztályok gyűjtőhelye, a java.net pedig a hálózati funkciók helye. Saját programok esetén általában a projekt neve a csomagok kiinduló pontja, másokkal (vagy a világgal) megosztott programok esetén pedig a gyártó cég vagy közösség internet nevét (és a program funkcióját) megfordítva kapjuk a csomag nevét (hu.javaforum.csomagnév). A csomagszerkezet teljesen azonos módon kezelendő, mint a háttértáron lévő könyvtárstruktúra; s ez olyannyira analóg, hogy csomagok gyakorlatilag meghatározzák, hogy a bennük foglalt osztályok melyik könyvtárban vannak. Egy csomag tartalmazhat alcsomagot, amelynek szintén lehet alcsomagja; ahogy a csomag mappa tartalmazhat almappát, amelynek szintén lehet almappája. Vagyis a csomagok fa struktúrát alkotnak.
-
class OsztályNév: az osztály neve. Minden osztályt névvel kell ellátnunk, amely név a Java nyelv előírásait teljesíti. Ennek a névnek azonosnak kell lennie (kisbetűvel és nagybetűvel írt név különböző!) a forrást tartalmazó fájl nevével, egy osztályt hordozó állomány nevét az osztály neve után írt .java kiterjesztéssel képezzük. Ha az osztály neve több szóból áll, akkor minden szót nagybetűvel kezdünk, és egybeírjuk - szóköz nélkül (például: OsztályNév). Bár a nyelv megengedi a Unicode karakterek használatát osztályok esetén is, a fájlrendszerek nem mindig teszik lehetővé az ékezetes betűk korrekt kezelését, ezért lehetőleg csak az angol karaktereket használjuk. A class kulcsszó elé kerülhetnek különféle módosítók (használatuktól később):
-
abstract: az osztály közvetlenül nem példányosítható, csak leszármazottai által.
-
final: az osztálynak nem lehetnek leszármazottai.
-
private: az osztály elérése privát, ez csak beágyazott osztályok esetén használható.
-
protected: az osztály elérése korlátozott, ez is csak beágyazott osztályok esetén használható. A protected megengedi, hogy a leszármazott osztályok is elérhessék ezeket a beágyazott osztályokat.
-
public: az osztály nyilvános, bárki számára elérhető.
-
static: az osztály csak egy példányban jön létre, ez szintén csak beágyazott osztályok esetén használható.
-
strictfp: az osztály törzsében a lebegőpontos műveletek IEEE 754 szerint futnak le.
-
Szabályok:
-
Az abstract és a final nem használható együtt.
-
A public, a private és a protected közül csak egyet használhatunk. Ha egyiket se adjuk meg, akkor az osztályt csak az azonos csomagban lévő osztályok látják, mások számára "láthatatlan".
Az osztály törzse kapcsos zárójelek közé kerül, ha az osztály üres, akkor is ki kell tennünk a nyitó és záró kapcsos zárójelet.
2.1.1.2. Tulajdonságok
Haladjunk tovább, én egészítsük ki ezt az UML ábrán látott tulajdonságokkal:
view plaincopy to clipboardprint?
-
package hu.javakocsma;
-
-
public abstract class Dolog
-
{
-
private String név;
-
}
A tulajdonságokat általában az UML leírás szerint soroljuk fel, egy tulajdonságot egy sorba - az UML tulajdonságot a Java programban már változónak jobban mondva példányváltozónak hívjuk, és az osztályba foglalását deklarációnak:
-
private String név: a változó deklarációja, lássuk visszafelé haladva:
-
név: a változó neve, egy osztályon belül a változó neve egyedi kell legyen, s a változók elnevezésének is vannak szabályai. Minden változónevet kisbetűvel írunk, ha a név több szóból áll, akkor az első szót leszámítva minden további szót nagybetűvel kezdünk, és egybeírjuk - szóköz nélkül (például: változóNév).
-
String: a változó típusa, amely azt határozza meg, hogy a változóba milyen típusú adatokat tudunk tenni. A változók típusáról a későbbiekben bővebben megemlékezünk.
-
private: az osztályhoz hasonlóan a változó működését és elérését a módosítók befolyásolhatják:
-
final: a változó értéke végleges, az a program futása során már nem változhat (más néven konstans változó). Afinal módosítóval ellátott változó nevét másképp írjuk! A változó nevét nagybetűvel írjuk, ha több szóból áll, akkor szóköz helyett aláhúzást teszünk (például: VÁLTOZÓ_NÉV).
-
private: a változót csak az osztályon belül lehet elérni és használni, a leszármazott osztályok sem használhatják.
-
protected: a változót az azonos csomagban lévő illetve leszármazott osztályok is elérik.
-
public: a változó nyilvános, bárki számára elérhető és módosítható.
-
static: a változó egyetlen példányban létezik csak - ekkor osztályváltozónak nevezzük.
-
transient: a változó nem határozza meg az osztály állapotát, értéket csak átmenetileg tárol.
-
volatile: a változó nem kerül gyorstárba, mivel tartalma több szál által is változhat.
-
Szabályok:
-
A final és a volatile nem használható együtt.
-
A public, a private és a protected közül csak egyet használhatunk. Ha egyiket se adjuk meg, akkor a változót csak az azonos csomagban lévő osztályok látják, a leszármazottak számára már "láthatatlan".
2.1.1.3. Műveletek
Lássuk az UML ábra utolsó harmadát, a műveleteket is:
view plaincopy to clipboardprint?
-
package hu.javakocsma;
-
-
public abstract class Dolog
-
{
-
-
private String név;
-
-
public Dolog()
-
{
-
}
-
-
public String getNév()
-
{
-
return this.név;
-
}
-
-
public void setNév(String név)
-
{
-
this.név = név;
-
}
-
}
Mint látható, az osztályban már az UML ábra műveletei is fel vannak sorolva, Java nyelvben konstruktor és metódus néven hivatkozunk a továbbiakban ezekre a műveletekre.
Konstruktorok
Emeljük ki a konstruktort, hogy megvizsgálhassuk közelebbről is:
view plaincopy to clipboardprint?
-
public Dolog()
-
{
-
}
A konstruktor legfőbb ismérve, hogy a neve azonos az osztály nevével és a név után szorosan a metódusokra is jellemző kerek zárójelet találjuk meg, a zárójelek között pedig paramétereket - a jelenlegi példában nincs ilyen paraméter - a paraméter nélküli konstruktortalapértelmezett konstruktornak nevezzük. A konstruktor neve előtt lehetnek módosítók:
-
private: a konstruktor csak az osztályon belül hívható meg, ez akkor hasznos, ha limitálni szeretnénk a létrehozott példányokat.
-
protected: a konstruktort az azonos csomagban lévő illetve leszármazott osztályok képesek elérni.
-
public: a konstruktor nyilvános, bárki számára elérhető.
-
Szabályok:
-
A public, a private és a protected közül csak egyet használhatunk. Ha egyiket se adjuk meg, akkor a konstruktort csak az azonos csomagban lévő osztályok látják, a leszármazottak számára már "láthatatlan".
Hasonlóan a osztályhoz - a konstruktornak is van törzse, amelyet kapcsos zárójelek zárnak közre. Ha a törzs üres, akkor is ki kell tennünk a nyitó és a záró kapcsos zárójelet.
A konstruktor feladata, hogy elkészítse az osztály egy új példányát. Egy osztálynak több példánya is lehet, a példányokat a példányváltozók különböztetik meg egymástól. Ha két példánynak azonos értékek vannak a példányváltozóikban - például két azonos nevű dolog, akkor a két példány ugyan egyenlő egymással, de mégis két külön példánynak számítanak.
Ha egy osztályban nincs konstruktor, akkor azt úgy kell vennünk, mintha a fentebb említett minimális konstruktor lenne benne - ez azt jelenti, hogy a Dolog osztályból a jelenlegi konstruktort el is hagyhatnánk, nem történne érdemi változás a program működésében. Természetesen egy osztályban lehet több konstruktor is - de erre visszatérünk a későbbiekben.
Metódusok
Egy metódus sok dologban hasonlít a konstruktorhoz, azonban a metódus neve előtt szerepeltetni kell egy típust, amely típusú értéket a metódus visszaad. Egy metódusra úgy is gondolhatunk, mint egy matematikai függvényre, például az y = sin(x) esetén a színusz függvény az x érték színuszát adja értékül az y változónak. Nézzük meg egy átlagos metódust közelebbről:
view plaincopy to clipboardprint?
-
public String getNév()
-
{
-
return this.név;
-
}
A metódus feje távolról hasonlít a példányváltozók deklarálásához, attól "csak" a kerek zárójelek és a metódus törzsét jelentő kapcsos zárójelek különböztetik meg:
-
public String getNév(): a metódus feje, haladjunk a végétől az eleje felé:
-
(): a kerek zárójelek közé átadott paramétereket tudunk felsorolni, jelen esetben nem adunk át paramétert.
-
getNév: a metódus neve, amelyre ugyan azok a szabályok érvényesek, mint a példányváltozókra - leszámítva, hogy egy osztályon belül lehet több azonos nevű metódus, ha eltérő paramétereik vannak.
-
String: a metódus átlal visszaadott érték típusa, ha nem akarunk értéket visszaadni, akkor void szerepelhet ezen a részen.
-
public: az változókhoz hasonlóan a metódus működését és elérését a módosítók befolyásolhatják:
-
abstract: a metódus törzs nélküli, a törzset a leszármazott osztályok tartalmazzák.
-
final: a metódus törzse végleges, a leszármazott osztályban nem lehet azt megváltoztatni felüldefiniálással.
-
native: a metódus törzs nélküli, a törzse nem Java nyelven van megvalósítva.
-
private: a metódust csak az osztályon belül lehet elérni és használni, a leszármazott osztályok sem használhatják.
-
protected: a metódust az azonos csomagban lévő illetve leszármazott osztályok is elérik.
-
public: a metódus nyilvános, bárki számára elérhető és módosítható.
-
static: a metódus csak statikus példányváltozókkal tud dolgozni, s használatához nem kell az osztályt példányosítani.
-
synchronized: a metódus törzsében egyszerre csak egy szál tartózkodhat, erre a környezet ügyel.
-
strictfp: a metódus törzsében a lebegőpontos műveletek IEEE 754 szerint futnak le.
-
Szabályok:
-
Az abstract metódus nem lehet private, static, final, native, strictfp vagy synchronized.
-
Egy abstract metódus csak olyan osztályban lehet deklarálva, amelyik maga is abstract.
-
Az abstract vagy a native metódusnak nem lehet törzse, a kapcsos zárójelek helyett pontosvesszővel kell lezárni.
-
A public, a private és a protected közül csak egyet használhatunk. Ha egyiket se adjuk meg, akkor a metódust csak az azonos csomagban lévő osztályok látják, a leszármazottak számára már "láthatatlan".
-
A native és a strictfp nem használható együtt.
A metódus törzsét a kapcsos zárójelek között találjuk meg - a példában ez üres, ide kerülnek a Java nyelvű utasítások, amelyek lépésről-lépésre elvégzik a kért műveletet:
view plaincopy to clipboardprint?
-
return this.név;
Jelen esetben a getNév (publikus) metódus visszaadja a privát elérésű név példányváltozó által hordozott értéket. Nézzük meg a következő metódust is:
view plaincopy to clipboardprint?
-
public void setNév(String név)
-
{
-
this.név = név;
-
}
Ennek a metódusnak már van egy paramétere, amely String típusú és neve egyszerűen név. A metódus törzse egy értékadás, ahol a paraméterben kapott értéket adjuk át a példányváltozónak.
Bean pattern
A két metódus közül az első a getter, a második a setter; a kettő publikus metódus és a privát példányváltozó együtt valósítják meg abean pattern nevű fogalmat, amely a Java egyik alapfogalma. Java nyelven bizonyos osztályokat úgy nevezünk, hogy bean (mint kávébab). Egy ilyen bean olyan osztály, amelynek van egy alapértelmezett konstruktora és példányai állapotát csak olyan példányváltozók határozzák meg, amelyekhez egy getter és egy setter metódus tartozik az alábbi módon:
view plaincopy to clipboardprint?
-
private String név;
-
public String getNév()
-
{
-
return this.név;
-
}
-
-
public void setNév(String név)
-
{
-
this.név = név;
-
}
Nézzük részletesen:
-
A példányváltozó privát, a két metódus publikus.
-
A példányváltozó típusa azonos a getter metódus által visszaadott típussal és a setter metódus egyetlen paraméterének típusával.
-
A getter metódus neve a példányváltozó nevéből képződik: egy get szót illesztünk elé és a név első betűjét nagybetűssé tesszük.
-
A setter metódus neve a getterhez hasonlóan képződik, csak a set szót illesztjük a példányváltozó neve elé.
-
A getter metódus visszaadja a példányváltozó értékét.
-
A setter metódus beállítja a példányváltozó értékét a paraméterben kapott értékére.
Vonjuk le a következtetést: a Dolog osztályunk egy Java bean.
|
| |